diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-09-10 22:40:37 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-09-10 22:40:37 +0000 |
commit | f4a29f27f10f769aa96175eedfb93b6fd630b886 (patch) | |
tree | b3b087c993b12c693973459773f19474ff79f365 /sys | |
parent | 57a6ae151f8522fd757545b3431245c991b8c315 (diff) |
update the trunk(4) driver
- add multicast support by passing multicast addresses to the ports.
this is a requirement for carp(4) over trunk(4).
- support the smallest common interface capabilities. ie., this adds
support for VLAN MTUs if all attached ports have this capability.
- add a port_destroy callback to the trunk protocol. this fixes a
potential crash if the master port has been detached while running.
discussed with deraadt@, brad@ and some others.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if_trunk.c | 207 | ||||
-rw-r--r-- | sys/net/if_trunk.h | 33 |
2 files changed, 229 insertions, 11 deletions
diff --git a/sys/net/if_trunk.c b/sys/net/if_trunk.c index 84b896281b7..c7bc1473e25 100644 --- a/sys/net/if_trunk.c +++ b/sys/net/if_trunk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_trunk.c,v 1.4 2005/07/31 03:52:18 pascoe Exp $ */ +/* $OpenBSD: if_trunk.c,v 1.5 2005/09/10 22:40:36 reyk Exp $ */ /* * Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net> @@ -56,6 +56,7 @@ void trunkattach(int); int trunk_clone_create(struct if_clone *, int); int trunk_clone_destroy(struct ifnet *); void trunk_lladdr(struct trunk_softc *, u_int8_t *); +int trunk_capabilities(struct trunk_softc *); int trunk_port_create(struct trunk_softc *, struct ifnet *); int trunk_port_destroy(struct trunk_port *); void trunk_port_watchdog(struct ifnet *); @@ -64,6 +65,11 @@ struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *); int trunk_port_checkstacking(struct trunk_softc *); void trunk_port2req(struct trunk_port *, struct trunk_reqport *); int trunk_ioctl(struct ifnet *, u_long, caddr_t); +int trunk_ether_addmulti(struct trunk_softc *, struct ifreq *); +int trunk_ether_delmulti(struct trunk_softc *, struct ifreq *); +void trunk_ether_purgemulti(struct trunk_softc *); +int trunk_ether_cmdmulti(struct trunk_port *, u_long); +int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t); void trunk_start(struct ifnet *); void trunk_watchdog(struct ifnet *); int trunk_media_change(struct ifnet *); @@ -77,6 +83,7 @@ struct if_clone trunk_cloner = /* Simple round robin */ int trunk_rr_attach(struct trunk_softc *); int trunk_rr_detach(struct trunk_softc *); +void trunk_rr_port_destroy(struct trunk_port *); int trunk_rr_start(struct trunk_softc *, struct mbuf *); int trunk_rr_watchdog(struct trunk_softc *); int trunk_rr_input(struct trunk_softc *, struct trunk_port *, @@ -140,6 +147,7 @@ trunk_clone_create(struct if_clone *ifc, int unit) ifp->if_ioctl = trunk_ioctl; ifp->if_output = ether_output; ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_capabilities = trunk_capabilities(tr); IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); IFQ_SET_READY(&ifp->if_snd); @@ -167,6 +175,9 @@ trunk_clone_destroy(struct ifnet *ifp) struct trunk_port *tp; int error, s; + /* Remove any multicast groups that we may have joined. */ + trunk_ether_purgemulti(tr); + s = splnet(); /* Shutdown and remove trunk ports, return on error */ @@ -208,6 +219,23 @@ trunk_lladdr(struct trunk_softc *tr, u_int8_t *lladdr) } int +trunk_capabilities(struct trunk_softc *tr) +{ + struct trunk_port *tp; + int cap = ~0; + + SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) + cap &= tp->tp_capabilities; + + if (tr->tr_ifflags & IFF_DEBUG) { + printf("%s: capabilities 0x%08x\n", + tr->tr_ifname, cap == ~0 ? 0 : cap); + } + + return (cap == ~0 ? 0 : cap); +} + +int trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp) { struct trunk_softc *tr_ptr; @@ -273,7 +301,16 @@ trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp) SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries); tr->tr_count++; - return (0); + /* Update trunk capabilities */ + tr->tr_capabilities = trunk_capabilities(tr); + + /* Add multicast addresses to this port */ + trunk_ether_cmdmulti(tp, SIOCADDMULTI); + + if (tr->tr_port_create != NULL) + error = (*tr->tr_port_create)(tp); + + return (error); } int @@ -282,7 +319,7 @@ trunk_port_checkstacking(struct trunk_softc *tr) struct trunk_softc *tr_ptr; struct trunk_port *tp; int m = 0; - + SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { if (tp->tp_flags & TRUNK_PORT_STACK) { tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc; @@ -300,6 +337,12 @@ trunk_port_destroy(struct trunk_port *tp) struct trunk_port *tp_ptr; struct ifnet *ifp = tp->tp_if; + if (tr->tr_port_destroy != NULL) + (*tr->tr_port_destroy)(tp); + + /* Remove multicast addresses from this port */ + trunk_ether_cmdmulti(tp, SIOCDELMULTI); + /* Port has to be down */ if (ifp->if_flags & IFF_UP) if_down(ifp); @@ -332,6 +375,9 @@ trunk_port_destroy(struct trunk_port *tp) free(tp, M_DEVBUF); + /* Update trunk capabilities */ + tr->tr_capabilities = trunk_capabilities(tr); + return (0); } @@ -581,10 +627,10 @@ trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ifp->if_flags &= ~IFF_RUNNING; break; case SIOCADDMULTI: - error = ether_addmulti(ifr, &tr->tr_ac); + error = trunk_ether_addmulti(tr, ifr); break; case SIOCDELMULTI: - error = ether_delmulti(ifr, &tr->tr_ac); + error = trunk_ether_delmulti(tr, ifr); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: @@ -601,6 +647,143 @@ trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (error); } +int +trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr) +{ + struct trunk_mc *mc; + u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; + int error; + + /* Ignore ENETRESET error code */ + if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET) + return (error); + + if ((mc = (struct trunk_mc *)malloc(sizeof(struct trunk_mc), + M_DEVBUF, M_NOWAIT)) == NULL) { + error = ENOMEM; + goto failed; + } + + ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi); + ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm); + bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len); + SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries); + + if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI, + (caddr_t)ifr)) != 0) { + trunk_ether_delmulti(tr, ifr); + return (error); + } + + return (error); + + failed: + ether_delmulti(ifr, &tr->tr_ac); + + return (error); +} + +int +trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr) +{ + struct ether_multi *enm; + struct trunk_mc *mc; + u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN]; + int error; + + if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0) + return (error); + ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm); + + if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET) + return (error); + + if ((error = trunk_ioctl_allports(tr, SIOCDELMULTI, + (caddr_t)ifr)) != 0) { + /* XXX At least one port failed to remove the address */ + if (tr->tr_ifflags & IFF_DEBUG) { + printf("%s: failed to remove multicast address " + "on all ports\n", tr->tr_ifname); + } + } + + SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) { + if (mc->mc_enm == enm) { + SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries); + free(mc, M_DEVBUF); + break; + } + } + + return (0); +} + +void +trunk_ether_purgemulti(struct trunk_softc *tr) +{ + struct trunk_mc *mc; + struct trunk_ifreq ifs; + struct ifreq *ifr = &ifs.ifreq.ifreq; + + while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) { + bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len); + + /* Try to remove multicast address on all ports */ + trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr); + + SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries); + free(mc, M_DEVBUF); + } +} + +int +trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd) +{ + struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; + struct trunk_mc *mc; + struct trunk_ifreq ifs; + struct ifreq *ifr = &ifs.ifreq.ifreq; + int ret, error = 0; + + bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ); + SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) { + bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len); + + if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) { + if (tr->tr_ifflags & IFF_DEBUG) { + printf("%s: ioctl %lu failed on %s: %d\n", + tr->tr_ifname, cmd, tp->tp_ifname, ret); + } + /* Store last known error and continue */ + error = ret; + } + } + + return (error); +} + +int +trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data) +{ + struct ifreq *ifr = (struct ifreq *)data; + struct trunk_port *tp; + int ret, error = 0; + + SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) { + bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ); + if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) { + if (tr->tr_ifflags & IFF_DEBUG) { + printf("%s: ioctl %lu failed on %s: %d\n", + tr->tr_ifname, cmd, tp->tp_ifname, ret); + } + /* Store last known error and continue */ + error = ret; + } + } + + return (error); +} + void trunk_start(struct ifnet *ifp) { @@ -608,9 +791,6 @@ trunk_start(struct ifnet *ifp) struct mbuf *m; int error = 0; - if (ifp->if_flags & IFF_DEBUG) - printf("%s: start\n", ifp->if_xname); - for (;; error = 0) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) @@ -754,6 +934,8 @@ trunk_rr_attach(struct trunk_softc *tr) tr->tr_detach = trunk_rr_detach; tr->tr_start = trunk_rr_start; tr->tr_input = trunk_rr_input; + tr->tr_port_create = NULL; + tr->tr_port_destroy = trunk_rr_port_destroy; tp = SLIST_FIRST(&tr->tr_ports); tr->tr_psc = (caddr_t)tp; @@ -768,6 +950,15 @@ trunk_rr_detach(struct trunk_softc *tr) return (0); } +void +trunk_rr_port_destroy(struct trunk_port *tp) +{ + struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk; + + if (tp == (struct trunk_port *)tr->tr_psc) + tr->tr_psc = NULL; +} + int trunk_rr_start(struct trunk_softc *tr, struct mbuf *m) { diff --git a/sys/net/if_trunk.h b/sys/net/if_trunk.h index d41497e5d1e..b67e3533d83 100644 --- a/sys/net/if_trunk.h +++ b/sys/net/if_trunk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_trunk.h,v 1.2 2005/05/24 07:51:53 reyk Exp $ */ +/* $OpenBSD: if_trunk.h,v 1.3 2005/09/10 22:40:36 reyk Exp $ */ /* * Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net> @@ -102,6 +102,28 @@ struct trunk_port { #define tp_ifname tp_if->if_xname /* interface name */ #define tp_link_state tp_if->if_link_state /* link state */ +#define tp_capabilities tp_if->if_capabilities /* capabilities */ + +struct trunk_mc { + union { + struct ether_multi *mcu_enm; + } mc_u; + struct sockaddr_storage mc_addr; + + SLIST_ENTRY(trunk_mc) mc_entries; +}; + +#define mc_enm mc_u.mcu_enm + +struct trunk_ifreq { + union { + struct ifreq ifreq; + struct { + char ifr_name[IFNAMSIZ]; + struct sockaddr_storage ifr_ss; + } ifreq_storage; + } ifreq; +}; struct trunk_softc { struct arpcom tr_ac; /* virtual interface */ @@ -115,16 +137,21 @@ struct trunk_softc { SLIST_HEAD(__tplhd, trunk_port) tr_ports; /* list of interfaces */ SLIST_ENTRY(trunk_softc) tr_entries; + SLIST_HEAD(__mclhd, trunk_mc) tr_mc_head; /* multicast addresses */ + /* Trunk protocol callbacks */ int (*tr_detach)(struct trunk_softc *); int (*tr_start)(struct trunk_softc *, struct mbuf *); int (*tr_watchdog)(struct trunk_softc *); int (*tr_input)(struct trunk_softc *, struct trunk_port *, struct ether_header *, struct mbuf *); + int (*tr_port_create)(struct trunk_port *); + void (*tr_port_destroy)(struct trunk_port *); }; -#define tr_ifflags tr_ac.ac_if.if_flags /* interface flags */ -#define tr_ifname tr_ac.ac_if.if_xname /* interface name */ +#define tr_ifflags tr_ac.ac_if.if_flags /* flags */ +#define tr_ifname tr_ac.ac_if.if_xname /* name */ +#define tr_capabilities tr_ac.ac_if.if_capabilities /* capabilities */ void trunk_port_ifdetach(struct ifnet *); int trunk_input(struct ifnet *, struct ether_header *, struct mbuf *); |