diff --git a/sys/modules/if_lagg/Makefile b/sys/modules/if_lagg/Makefile index 4d84c92d1702..7c04604d7279 100644 --- a/sys/modules/if_lagg/Makefile +++ b/sys/modules/if_lagg/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -.include - .PATH: ${.CURDIR}/../../net KMOD= if_lagg SRCS= if_lagg.c ieee8023ad_lacp.c opt_inet.h opt_inet6.h @@ -10,7 +8,7 @@ SRCS= if_lagg.c ieee8023ad_lacp.c opt_inet.h opt_inet6.h opt_inet.h: echo "#define INET 1" > ${.TARGET} -.if ${MK_INET6_SUPPORT} != "no" +.if !defined(NO_INET6) opt_inet6.h: echo "#define INET6 1" > ${.TARGET} .endif diff --git a/sys/net/ieee8023ad_lacp.c b/sys/net/ieee8023ad_lacp.c index 11b2f5e89eab..2bc81700095e 100644 --- a/sys/net/ieee8023ad_lacp.c +++ b/sys/net/ieee8023ad_lacp.c @@ -38,9 +38,6 @@ __FBSDID("$FreeBSD$"); #include /* for net/if.h */ #include #include -#include -#include -#include #include #include @@ -114,9 +111,6 @@ static void lacp_aggregator_delref(struct lacp_softc *, /* receive machine */ -static void lacp_dequeue(void *, int); -static int lacp_pdu_input(struct lagg_port *, struct mbuf *); -static int lacp_marker_input(struct lagg_port *, struct mbuf *); static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); static void lacp_sm_rx_timer(struct lacp_port *); static void lacp_sm_rx_set_expired(struct lacp_port *); @@ -208,66 +202,17 @@ static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, }; -void -lacp_input(struct lagg_port *lgp, struct mbuf *m) -{ - struct lagg_softc *lgs = lgp->lp_lagg; - struct lacp_softc *lsc = LACP_SOFTC(lgs); - uint8_t subtype; - - if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { - m_freem(m); - return; - } - - m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); - switch (subtype) { - case SLOWPROTOCOLS_SUBTYPE_LACP: - IF_HANDOFF(&lsc->lsc_queue, m, NULL); - taskqueue_enqueue(taskqueue_swi, &lsc->lsc_qtask); - break; - - case SLOWPROTOCOLS_SUBTYPE_MARKER: - lacp_marker_input(lgp, m); - break; - - default: - /* Unknown LACP packet type */ - m_freem(m); - break; - } -} - -static void -lacp_dequeue(void *arg, int pending) -{ - struct lacp_softc *lsc = (struct lacp_softc *)arg; - struct lagg_softc *sc = lsc->lsc_lagg; - struct lagg_port *lgp; - struct mbuf *m; - - LAGG_WLOCK(sc); - for (;;) { - IF_DEQUEUE(&lsc->lsc_queue, m); - if (m == NULL) - break; - lgp = m->m_pkthdr.rcvif->if_lagg; - lacp_pdu_input(lgp, m); - } - LAGG_WUNLOCK(sc); -} - /* - * lacp_pdu_input: process lacpdu + * lacp_input: process lacpdu */ -static int -lacp_pdu_input(struct lagg_port *lgp, struct mbuf *m) +int +lacp_input(struct lagg_port *lgp, struct mbuf *m) { struct lacp_port *lp = LACP_PORT(lgp); struct lacpdu *du; int error = 0; - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { goto bad; @@ -277,10 +222,6 @@ lacp_pdu_input(struct lagg_port *lgp, struct mbuf *m) goto bad; } - if ((m->m_flags & M_MCAST) == 0) { - goto bad; - } - if (m->m_len < sizeof(*du)) { m = m_pullup(m, sizeof(*du)); if (m == NULL) { @@ -358,7 +299,7 @@ lacp_xmit_lacpdu(struct lacp_port *lp) struct lacpdu *du; int error; - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { @@ -415,7 +356,7 @@ lacp_linkstate(struct lagg_port *lgp) uint8_t old_state; uint16_t old_key; - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); bzero((char *)&ifmr, sizeof(ifmr)); error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); @@ -452,10 +393,8 @@ static void lacp_tick(void *arg) { struct lacp_softc *lsc = arg; - struct lagg_softc *sc = lsc->lsc_lagg; struct lacp_port *lp; - LAGG_WLOCK(sc); LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) continue; @@ -467,7 +406,6 @@ lacp_tick(void *arg) lacp_sm_tx(lp); lacp_sm_ptx_tx_schedule(lp); } - LAGG_WUNLOCK(sc); callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); } @@ -485,7 +423,7 @@ lacp_port_create(struct lagg_port *lgp) boolean_t active = TRUE; /* XXX should be configurable */ boolean_t fast = FALSE; /* XXX should be configurable */ - LAGG_WLOCK_ASSERT(lgs); + LAGG_LOCK_ASSERT(lgs); bzero((char *)&sdl, sizeof(sdl)); sdl.sdl_len = sizeof(sdl); @@ -511,7 +449,6 @@ lacp_port_create(struct lagg_port *lgp) lp->lp_ifp = ifp; lp->lp_lagg = lgp; lp->lp_lsc = lsc; - lp->lp_ifma = rifma; LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); @@ -530,9 +467,11 @@ void lacp_port_destroy(struct lagg_port *lgp) { struct lacp_port *lp = LACP_PORT(lgp); - int i; + struct ifnet *ifp = lgp->lp_ifp; + struct sockaddr_dl sdl; + int i, error; - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); for (i = 0; i < LACP_NTIMER; i++) { LACP_TIMER_DISARM(lp, i); @@ -543,9 +482,18 @@ lacp_port_destroy(struct lagg_port *lgp) lacp_unselect(lp); lgp->lp_flags &= ~LAGG_PORT_DISABLED; - /* The address may have already been removed by if_purgemaddrs() */ - if (!lgp->lp_detaching) - if_delmulti_ifma(lp->lp_ifma); + bzero((char *)&sdl, sizeof(sdl)); + sdl.sdl_len = sizeof(sdl); + sdl.sdl_family = AF_LINK; + sdl.sdl_index = ifp->if_index; + sdl.sdl_type = IFT_ETHER; + sdl.sdl_alen = ETHER_ADDR_LEN; + + bcopy(ðermulticastaddr_slowprotocols, + LLADDR(&sdl), ETHER_ADDR_LEN); + error = if_delmulti(ifp, (struct sockaddr *)&sdl); + if (error) + printf("%s: DELMULTI failed on %s\n", __func__, lgp->lp_ifname); LIST_REMOVE(lp, lp_next); free(lp, M_DEVBUF); @@ -597,7 +545,7 @@ lacp_disable_distributing(struct lacp_port *lp) char buf[LACP_LAGIDSTR_MAX+1]; #endif /* defined(LACP_DEBUG) */ - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { return; @@ -635,7 +583,7 @@ lacp_enable_distributing(struct lacp_port *lp) char buf[LACP_LAGIDSTR_MAX+1]; #endif /* defined(LACP_DEBUG) */ - LAGG_WLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { return; @@ -674,7 +622,7 @@ lacp_attach(struct lagg_softc *lgs) { struct lacp_softc *lsc; - LAGG_WLOCK_ASSERT(lgs); + LAGG_LOCK_ASSERT(lgs); lsc = malloc(sizeof(struct lacp_softc), M_DEVBUF, M_NOWAIT|M_ZERO); @@ -689,12 +637,8 @@ lacp_attach(struct lagg_softc *lgs) TAILQ_INIT(&lsc->lsc_aggregators); LIST_INIT(&lsc->lsc_ports); - TASK_INIT(&lsc->lsc_qtask, 0, lacp_dequeue, lsc); - mtx_init(&lsc->lsc_queue.ifq_mtx, "lacp queue", NULL, MTX_DEF); - lsc->lsc_queue.ifq_maxlen = ifqmaxlen; - - callout_init(&lsc->lsc_transit_callout, CALLOUT_MPSAFE); - callout_init(&lsc->lsc_callout, CALLOUT_MPSAFE); + callout_init_mtx(&lsc->lsc_transit_callout, &lgs->sc_mtx, 0); + callout_init_mtx(&lsc->lsc_callout, &lgs->sc_mtx, 0); /* if the lagg is already up then do the same */ if (lgs->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) @@ -716,9 +660,6 @@ lacp_detach(struct lagg_softc *lgs) lgs->sc_psc = NULL; callout_drain(&lsc->lsc_transit_callout); callout_drain(&lsc->lsc_callout); - taskqueue_drain(taskqueue_swi, &lsc->lsc_qtask); - IF_DRAIN(&lsc->lsc_queue); - mtx_destroy(&lsc->lsc_queue.ifq_mtx); free(lsc, M_DEVBUF); return (0); @@ -750,7 +691,7 @@ lacp_select_tx_port(struct lagg_softc *lgs, struct mbuf *m) uint32_t hash; int nports; - LAGG_WLOCK_ASSERT(lgs); + LAGG_LOCK_ASSERT(lgs); if (__predict_false(lsc->lsc_suppress_distributing)) { LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); @@ -1603,7 +1544,7 @@ lacp_marker_input(struct lagg_port *lgp, struct mbuf *m) struct markerdu *mdu; int error = 0; - LAGG_RLOCK_ASSERT(lgp->lp_lagg); + LAGG_LOCK_ASSERT(lgp->lp_lagg); if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { goto bad; @@ -1613,10 +1554,6 @@ lacp_marker_input(struct lagg_port *lgp, struct mbuf *m) goto bad; } - if ((m->m_flags & M_MCAST) == 0) { - goto bad; - } - if (m->m_len < sizeof(*mdu)) { m = m_pullup(m, sizeof(*mdu)); if (m == NULL) { diff --git a/sys/net/ieee8023ad_lacp.h b/sys/net/ieee8023ad_lacp.h index 5761e3e9abd2..5b053dd0fd70 100644 --- a/sys/net/ieee8023ad_lacp.h +++ b/sys/net/ieee8023ad_lacp.h @@ -190,7 +190,6 @@ struct lacp_port { int lp_flags; u_int lp_media; /* XXX redundant */ int lp_timer[LACP_NTIMER]; - struct ifmultiaddr *lp_ifma; struct lacp_aggregator *lp_aggregator; }; @@ -214,8 +213,6 @@ struct lacp_softc { struct callout lsc_callout; LIST_HEAD(, lacp_port) lsc_ports; u_int32_t lsc_hashkey; - struct task lsc_qtask; - struct ifqueue lsc_queue; /* pdu input queue */ }; #define LACP_TYPE_ACTORINFO 1 @@ -264,7 +261,8 @@ struct markerdu { #define LACP_PORT(_lp) ((struct lacp_port *)(_lp)->lp_psc) #define LACP_SOFTC(_sc) ((struct lacp_softc *)(_sc)->sc_psc) -void lacp_input(struct lagg_port *, struct mbuf *); +int lacp_input(struct lagg_port *, struct mbuf *); +int lacp_marker_input(struct lagg_port *, struct mbuf *); struct lagg_port *lacp_select_tx_port(struct lagg_softc *, struct mbuf *); int lacp_attach(struct lagg_softc *); int lacp_detach(struct lagg_softc *); diff --git a/sys/net/if.c b/sys/net/if.c index fbbf752ecf40..ef1272ac68d5 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -95,6 +95,7 @@ SYSCTL_INT(_net_link, OID_AUTO, log_link_state_change, CTLFLAG_RW, void (*bstp_linkstate_p)(struct ifnet *ifp, int state); void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); +void (*lagg_linkstate_p)(struct ifnet *ifp, int state); struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) = NULL; @@ -1133,6 +1134,10 @@ do_link_state_change(void *arg, int pending) KASSERT(bstp_linkstate_p != NULL,("if_bridge bstp not loaded!")); (*bstp_linkstate_p)(ifp, link_state); } + if (ifp->if_lagg) { + KASSERT(lagg_linkstate_p != NULL,("if_lagg not loaded!")); + (*lagg_linkstate_p)(ifp, link_state); + } devctl_notify("IFNET", ifp->if_xname, (link_state == LINK_STATE_UP) ? "LINK_UP" : "LINK_DOWN", NULL); @@ -2184,6 +2189,7 @@ if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) */ /* FALLTHROUGH */ case IFT_ARCNET: + case IFT_IEEE8023ADLAG: bcopy(lladdr, LLADDR(sdl), len); break; default: diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 5bc0d5512c7c..f6e753e5bf0f 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -120,6 +120,9 @@ int (*bridge_output_p)(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); void (*bridge_dn_p)(struct mbuf *, struct ifnet *); +/* if_lagg(4) support */ +struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); + static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -589,6 +592,17 @@ ether_input(struct ifnet *ifp, struct mbuf *m) return; } + /* Handle input from a lagg(4) port */ + if (ifp->if_type == IFT_IEEE8023ADLAG) { + KASSERT(lagg_input_p != NULL, + ("%s: if_lagg not loaded!", __func__)); + m = (*lagg_input_p)(ifp, m); + if (m != NULL) + ifp = m->m_pkthdr.rcvif; + else + return; + } + /* Handle ng_ether(4) processing, if any */ if (IFP2AC(ifp)->ac_netgraph != NULL) { (*ng_ether_input_p)(ifp, &m); diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c index 76e5c88e7424..7839c244e193 100644 --- a/sys/net/if_lagg.c +++ b/sys/net/if_lagg.c @@ -31,12 +31,8 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include -#include -#include -#include #include #include @@ -79,7 +75,7 @@ SLIST_HEAD(__trhead, lagg_softc) lagg_list; /* list of laggs */ static struct mtx lagg_list_mtx; eventhandler_tag lagg_detach_cookie = NULL; -static int lagg_clone_create(struct if_clone *, int, caddr_t); +static int lagg_clone_create(struct if_clone *, int); static void lagg_clone_destroy(struct ifnet *); static void lagg_lladdr(struct lagg_softc *, uint8_t *); static int lagg_capabilities(struct lagg_softc *); @@ -100,6 +96,7 @@ static void lagg_stop(struct lagg_softc *); static int lagg_ioctl(struct ifnet *, u_long, caddr_t); static int lagg_ether_setmulti(struct lagg_softc *); static int lagg_ether_cmdmulti(struct lagg_port *, int); +static void lagg_ether_purgemulti(struct lagg_softc *); static int lagg_setflag(struct lagg_port *, int, int, int (*func)(struct ifnet *, int)); static int lagg_setflags(struct lagg_port *, int status); @@ -158,6 +155,24 @@ static const struct { { LAGG_PROTO_NONE, NULL } }; +/* + * Return a 32-bit hash of the given buffer. + * XXX Taken from sys/sys/hash.h. This file can not be included directly due to + * compiler warnings that need an API change to fix. + */ +#define HASHSTEP(x,c) (((x << 5) + x) + (c)) +static __inline uint32_t +hash32_buf(const void *buf, size_t len, uint32_t hash) +{ + const unsigned char *p = buf; + + while (len--) + hash = HASHSTEP(hash, *p++); + + return hash; +} +#undef HASHSTEP + static int lagg_modevent(module_t mod, int type, void *data) { @@ -177,6 +192,8 @@ lagg_modevent(module_t mod, int type, void *data) EVENTHANDLER_DEREGISTER(ifnet_departure_event, lagg_detach_cookie); if_clone_detach(&lagg_cloner); + while (!SLIST_EMPTY(&lagg_list)) + lagg_clone_destroy(SLIST_FIRST(&lagg_list)->sc_ifp); lagg_input_p = NULL; lagg_linkstate_p = NULL; mtx_destroy(&lagg_list_mtx); @@ -196,7 +213,7 @@ static moduledata_t lagg_mod = { DECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static int -lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) +lagg_clone_create(struct if_clone *ifc, int unit) { struct lagg_softc *sc; struct ifnet *ifp; @@ -264,7 +281,7 @@ lagg_clone_destroy(struct ifnet *ifp) struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; struct lagg_port *lp; - LAGG_WLOCK(sc); + LAGG_LOCK(sc); lagg_stop(sc); ifp->if_flags &= ~IFF_UP; @@ -276,7 +293,10 @@ lagg_clone_destroy(struct ifnet *ifp) if (sc->sc_detach != NULL) (*sc->sc_detach)(sc); - LAGG_WUNLOCK(sc); + /* Remove any multicast groups that we may have joined. */ + lagg_ether_purgemulti(sc); + + LAGG_UNLOCK(sc); ifmedia_removeall(&sc->sc_media); ether_ifdetach(ifp); @@ -311,7 +331,7 @@ lagg_capabilities(struct lagg_softc *sc) struct lagg_port *lp; int cap = ~0, priv; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); /* Preserve private capabilities */ priv = sc->sc_capabilities & IFCAP_LAGG_MASK; @@ -336,7 +356,7 @@ lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr) struct lagg_llq *llq; int pending = 0; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); if (lp->lp_detaching || memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) @@ -378,10 +398,10 @@ lagg_port_setlladdr(void *arg, int pending) int error; /* Grab a local reference of the queue and remove it from the softc */ - LAGG_WLOCK(sc); + LAGG_LOCK(sc); head = SLIST_FIRST(&sc->sc_llq_head); SLIST_FIRST(&sc->sc_llq_head) = NULL; - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); /* * Traverse the queue and set the lladdr on each ifp. It is safe to do @@ -408,7 +428,7 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) struct lagg_port *lp; int error = 0; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); /* Limit the maximal number of lagg ports */ if (sc->sc_count >= LAGG_MAX_PORTS) @@ -502,7 +522,7 @@ lagg_port_checkstacking(struct lagg_softc *sc) struct lagg_port *lp; int m = 0; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { if (lp->lp_flags & LAGG_PORT_STACK) { @@ -522,7 +542,7 @@ lagg_port_destroy(struct lagg_port *lp, int runpd) struct lagg_llq *llq; struct ifnet *ifp = lp->lp_ifp; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); if (runpd && sc->sc_port_destroy != NULL) (*sc->sc_port_destroy)(lp); @@ -603,7 +623,7 @@ lagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) switch (cmd) { case SIOCGLAGGPORT: - LAGG_RLOCK(sc); + LAGG_LOCK(sc); if (rp->rp_portname[0] == '\0' || ifunit(rp->rp_portname) != ifp) { error = EINVAL; @@ -616,7 +636,7 @@ lagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } lagg_port2req(lp, rp); - LAGG_RUNLOCK(sc); + LAGG_UNLOCK(sc); break; default: goto fallback; @@ -672,10 +692,10 @@ lagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) sc = lp->lp_lagg; - LAGG_WLOCK(sc); + LAGG_LOCK(sc); lp->lp_detaching = 1; lagg_port_destroy(lp, 1); - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); } static void @@ -719,7 +739,7 @@ lagg_init(void *xsc) if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; - LAGG_WLOCK(sc); + LAGG_LOCK(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Update the port lladdrs */ @@ -729,7 +749,7 @@ lagg_init(void *xsc) if (sc->sc_init != NULL) (*sc->sc_init)(sc); - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); } static void @@ -737,7 +757,7 @@ lagg_stop(struct lagg_softc *sc) { struct ifnet *ifp = sc->sc_ifp; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; @@ -760,7 +780,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct thread *td = curthread; int i, error = 0, unlock = 1; - LAGG_WLOCK(sc); + LAGG_LOCK(sc); bzero(&rpbuf, sizeof(rpbuf)); @@ -782,7 +802,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } break; case SIOCSLAGG: - error = priv_check(td, PRIV_NET_LAGG); + error = suser(td); if (error) break; if (ra->ra_proto >= LAGG_PROTO_MAX) { @@ -836,7 +856,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) lagg_port2req(lp, rp); break; case SIOCSLAGGPORT: - error = priv_check(td, PRIV_NET_LAGG); + error = suser(td); if (error) break; if (rp->rp_portname[0] == '\0' || @@ -847,7 +867,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = lagg_port_create(sc, tpif); break; case SIOCSLAGGDELPORT: - error = priv_check(td, PRIV_NET_LAGG); + error = suser(td); if (error) break; if (rp->rp_portname[0] == '\0' || @@ -883,7 +903,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) * If interface is marked up and it is stopped, then * start it. */ - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); unlock = 0; (*ifp->if_init)(sc); } @@ -894,12 +914,12 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); unlock = 0; error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; default: - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); unlock = 0; error = ether_ioctl(ifp, cmd, data); break; @@ -907,23 +927,55 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) out: if (unlock) - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); return (error); } static int lagg_ether_setmulti(struct lagg_softc *sc) { - struct lagg_port *lp; + struct ifnet *trifp = sc->sc_ifp; + struct ifnet *ifp; + struct ifmultiaddr *ifma, *rifma = NULL; + struct lagg_port *lp; + struct lagg_mc *mc; + struct sockaddr_dl sdl; + int error; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); - SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { - /* First, remove any existing filter entries. */ - lagg_ether_cmdmulti(lp, 0); - /* copy all addresses from the lagg interface to the port */ - lagg_ether_cmdmulti(lp, 1); + bzero((char *)&sdl, sizeof(sdl)); + sdl.sdl_len = sizeof(sdl); + sdl.sdl_family = AF_LINK; + sdl.sdl_type = IFT_ETHER; + sdl.sdl_alen = ETHER_ADDR_LEN; + + /* First, remove any existing filter entries. */ + lagg_ether_purgemulti(sc); + + /* Now program new ones. */ + TAILQ_FOREACH(ifma, &trifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); + if (mc == NULL) + return (ENOMEM); + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + (char *)&mc->mc_addr, ETHER_ADDR_LEN); + SLIST_INSERT_HEAD(&sc->sc_mc_head, mc, mc_entries); + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + LLADDR(&sdl), ETHER_ADDR_LEN); + + /* do all the ports */ + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { + ifp = lp->lp_ifp; + sdl.sdl_index = ifp->if_index; + error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); + if (error) + return (error); + } } + return (0); } @@ -931,14 +983,13 @@ static int lagg_ether_cmdmulti(struct lagg_port *lp, int set) { struct lagg_softc *sc = lp->lp_lagg; - struct ifnet *ifp = lp->lp_ifp; - struct ifnet *trifp = sc->sc_ifp; - struct lagg_mc *mc; - struct ifmultiaddr *ifma, *rifma = NULL; - struct sockaddr_dl sdl; - int error; + struct ifnet *ifp = lp->lp_ifp;; + struct lagg_mc *mc; + struct ifmultiaddr *rifma = NULL; + struct sockaddr_dl sdl; + int error; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); bzero((char *)&sdl, sizeof(sdl)); sdl.sdl_len = sizeof(sdl); @@ -947,32 +998,41 @@ lagg_ether_cmdmulti(struct lagg_port *lp, int set) sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_index = ifp->if_index; - if (set) { - TAILQ_FOREACH(ifma, &trifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), - LLADDR(&sdl), ETHER_ADDR_LEN); + SLIST_FOREACH(mc, &sc->sc_mc_head, mc_entries) { + bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); + if (set) error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); - if (error) - return (error); - mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); - if (mc == NULL) - return (ENOMEM); - mc->mc_ifma = rifma; - SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); - } - } else { - while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { - SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); - if_delmulti_ifma(mc->mc_ifma); - free(mc, M_DEVBUF); + else + error = if_delmulti(ifp, (struct sockaddr *)&sdl); + + if (error) { + printf("cmdmulti error on %s, set = %d\n", + ifp->if_xname, set); + return (error); } } return (0); } +static void +lagg_ether_purgemulti(struct lagg_softc *sc) +{ + struct lagg_port *lp; + struct lagg_mc *mc; + + LAGG_LOCK_ASSERT(sc); + + /* remove from ports */ + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) + lagg_ether_cmdmulti(lp, 0); + + while ((mc = SLIST_FIRST(&sc->sc_mc_head)) != NULL) { + SLIST_REMOVE(&sc->sc_mc_head, mc, lagg_mc, mc_entries); + free(mc, M_DEVBUF); + } +} + /* Handle a ref counted flag that should be set on the lagg port as well */ static int lagg_setflag(struct lagg_port *lp, int flag, int status, @@ -983,7 +1043,7 @@ lagg_setflag(struct lagg_port *lp, int flag, int status, struct ifnet *ifp = lp->lp_ifp; int error; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); status = status ? (trifp->if_flags & flag) : 0; /* Now "status" contains the flag value or 0 */ @@ -1033,7 +1093,6 @@ lagg_start(struct ifnet *ifp) struct mbuf *m; int error = 0; - LAGG_RLOCK(sc); for (;; error = 0) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) @@ -1041,9 +1100,11 @@ lagg_start(struct ifnet *ifp) BPF_MTAP(ifp, m); - if (sc->sc_proto != LAGG_PROTO_NONE) + if (sc->sc_proto != LAGG_PROTO_NONE) { + LAGG_LOCK(sc); error = (*sc->sc_start)(sc, m); - else + LAGG_UNLOCK(sc); + } else m_free(m); if (error == 0) @@ -1051,7 +1112,6 @@ lagg_start(struct ifnet *ifp) else ifp->if_oerrors++; } - LAGG_RUNLOCK(sc); return; } @@ -1070,7 +1130,7 @@ lagg_input(struct ifnet *ifp, struct mbuf *m) return (NULL); } - LAGG_RLOCK(sc); + LAGG_LOCK(sc); BPF_MTAP(trifp, m); m = (*sc->sc_input)(sc, lp, m); @@ -1082,7 +1142,7 @@ lagg_input(struct ifnet *ifp, struct mbuf *m) trifp->if_ibytes += m->m_pkthdr.len; } - LAGG_RUNLOCK(sc); + LAGG_UNLOCK(sc); return (m); } @@ -1107,12 +1167,12 @@ lagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_ETHER | IFM_AUTO; - LAGG_RLOCK(sc); + LAGG_LOCK(sc); SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { if (LAGG_PORTACTIVE(lp)) imr->ifm_status |= IFM_ACTIVE; } - LAGG_RUNLOCK(sc); + LAGG_UNLOCK(sc); } static void @@ -1126,10 +1186,10 @@ lagg_port_state(struct ifnet *ifp, int state) if (sc == NULL) return; - LAGG_WLOCK(sc); + LAGG_LOCK(sc); if (sc->sc_linkstate != NULL) (*sc->sc_linkstate)(lp); - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); } struct lagg_port * @@ -1138,7 +1198,7 @@ lagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) struct lagg_port *lp_next, *rval = NULL; // int new_link = LINK_STATE_DOWN; - LAGG_WLOCK_ASSERT(sc); + LAGG_LOCK_ASSERT(sc); /* * Search a port which reports an active link state. */ @@ -1204,6 +1264,8 @@ lagg_hashmbuf(struct mbuf *m, uint32_t key) struct ether_header *eh; struct ether_vlan_header vlanbuf; const struct ether_vlan_header *vlan; + struct m_tag *mtag; + u_int tag; #ifdef INET const struct ip *ip; struct ip ipbuf; @@ -1224,8 +1286,11 @@ lagg_hashmbuf(struct mbuf *m, uint32_t key) /* Special handling for encapsulating VLAN frames */ if (m->m_flags & M_VLANTAG) { - p = hash32_buf(&m->m_pkthdr.ether_vtag, - sizeof(m->m_pkthdr.ether_vtag), p); + mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); + KASSERT(mtag != NULL, + ("%s: M_VLANTAG without m_tag", __func__)); + tag = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)); + p = hash32_buf(&tag, sizeof(tag), p); } else if (etype == ETHERTYPE_VLAN) { vlan = lagg_gethdr(m, off, sizeof(*vlan), &vlanbuf); if (vlan == NULL) @@ -1563,9 +1628,9 @@ lagg_lacp_detach(struct lagg_softc *sc) lacp_port_destroy(lp); /* unlocking is safe here */ - LAGG_WUNLOCK(sc); + LAGG_UNLOCK(sc); error = lacp_detach(sc); - LAGG_WLOCK(sc); + LAGG_LOCK(sc); return (error); } @@ -1603,13 +1668,33 @@ lagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) struct ifnet *ifp = sc->sc_ifp; struct ether_header *eh; u_short etype; + uint8_t subtype; eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); /* Tap off LACP control messages */ if (etype == ETHERTYPE_SLOW) { - lacp_input(lp, m); + if (m->m_pkthdr.len < sizeof(*eh) + sizeof(subtype)) { + m_freem(m); + return (NULL); + } + + m_copydata(m, sizeof(*eh), sizeof(subtype), &subtype); + switch (subtype) { + case SLOWPROTOCOLS_SUBTYPE_LACP: + lacp_input(lp, m); + break; + + case SLOWPROTOCOLS_SUBTYPE_MARKER: + lacp_marker_input(lp, m); + break; + + default: + /* Unknown LACP packet type */ + m_freem(m); + break; + } return (NULL); } diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h index db3146d6242b..0ffeab3459f0 100644 --- a/sys/net/if_lagg.h +++ b/sys/net/if_lagg.h @@ -136,8 +136,12 @@ struct lagg_lb { }; struct lagg_mc { - struct ifmultiaddr *mc_ifma; - SLIST_ENTRY(lagg_mc) mc_entries; + union { + struct ether_multi *mcu_enm; + } mc_u; + struct sockaddr_storage mc_addr; + + SLIST_ENTRY(lagg_mc) mc_entries; }; /* List of interfaces to have the MAC address modified */ @@ -149,7 +153,7 @@ struct lagg_llq { struct lagg_softc { struct ifnet *sc_ifp; /* virtual interface */ - struct rwlock sc_mtx; + struct mtx sc_mtx; int sc_proto; /* lagg protocol */ u_int sc_count; /* number of ports */ struct lagg_port *sc_primary; /* primary port */ @@ -159,6 +163,7 @@ struct lagg_softc { SLIST_HEAD(__tplhd, lagg_port) sc_ports; /* list of interfaces */ SLIST_ENTRY(lagg_softc) sc_entries; + SLIST_HEAD(__mclhd, lagg_mc) sc_mc_head; /* multicast addresses */ struct task sc_lladdr_task; SLIST_HEAD(__llqhd, lagg_llq) sc_llq_head; /* interfaces to program the lladdr on */ @@ -189,8 +194,6 @@ struct lagg_port { caddr_t lp_psc; /* protocol data */ int lp_detaching; /* ifnet is detaching */ - SLIST_HEAD(__mclhd, lagg_mc) lp_mc_head; /* multicast addresses */ - /* Redirected callbacks */ int (*lp_ioctl)(struct ifnet *, u_long, caddr_t); int (*lp_output)(struct ifnet *, struct mbuf *, struct sockaddr *, @@ -199,14 +202,13 @@ struct lagg_port { SLIST_ENTRY(lagg_port) lp_entries; }; -#define LAGG_LOCK_INIT(_sc) rw_init(&(_sc)->sc_mtx, "if_lagg rwlock") -#define LAGG_LOCK_DESTROY(_sc) rw_destroy(&(_sc)->sc_mtx) -#define LAGG_RLOCK(_sc) rw_rlock(&(_sc)->sc_mtx) -#define LAGG_WLOCK(_sc) rw_wlock(&(_sc)->sc_mtx) -#define LAGG_RUNLOCK(_sc) rw_runlock(&(_sc)->sc_mtx) -#define LAGG_WUNLOCK(_sc) rw_wunlock(&(_sc)->sc_mtx) -#define LAGG_RLOCK_ASSERT(_sc) rw_assert(&(_sc)->sc_mtx, RA_RLOCKED) -#define LAGG_WLOCK_ASSERT(_sc) rw_assert(&(_sc)->sc_mtx, RA_WLOCKED) +#define LAGG_LOCK_INIT(_tr) mtx_init(&(_tr)->sc_mtx, "if_lagg", NULL, \ + MTX_DEF) +#define LAGG_LOCK_DESTROY(_tr) mtx_destroy(&(_tr)->sc_mtx) +#define LAGG_LOCK(_tr) mtx_lock(&(_tr)->sc_mtx) +#define LAGG_UNLOCK(_tr) mtx_unlock(&(_tr)->sc_mtx) +#define LAGG_LOCKED(_tr) mtx_owned(&(_tr)->sc_mtx) +#define LAGG_LOCK_ASSERT(_tr) mtx_assert(&(_tr)->sc_mtx, MA_OWNED) extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); extern void (*lagg_linkstate_p)(struct ifnet *, int ); diff --git a/sys/net/if_var.h b/sys/net/if_var.h index d5e7fd1659b2..12711884e284 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -158,7 +158,7 @@ struct ifnet { (void *); int (*if_resolvemulti) /* validate/resolve multicast */ (struct ifnet *, struct sockaddr **, struct sockaddr *); - void *if_spare1; /* spare pointer 1 */ + void *if_lagg; /* lagg glue */ void *if_spare2; /* spare pointer 2 */ void *if_spare3; /* spare pointer 3 */ int if_drv_flags; /* driver-managed status flags */