MFC the lagg(4) driver which provides link aggregation, failover and fault

tolerance.
This commit is contained in:
thompsa 2007-05-17 01:49:41 +00:00
parent 6e8d84bf2d
commit 1a4d003586
8 changed files with 234 additions and 194 deletions

View File

@ -1,7 +1,5 @@
# $FreeBSD$
.include <bsd.own.mk>
.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

View File

@ -38,9 +38,6 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h> /* for net/if.h */
#include <sys/sockio.h>
#include <machine/stdarg.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/taskqueue.h>
#include <net/if.h>
#include <net/if_dl.h>
@ -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(&ethermulticastaddr_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) {

View File

@ -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 *);

View File

@ -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:

View File

@ -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);

View File

@ -31,12 +31,8 @@ __FBSDID("$FreeBSD$");
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/hash.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/taskqueue.h>
#include <net/ethernet.h>
@ -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);
}

View File

@ -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 );

View File

@ -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 */