if_lagg.c revision 169688
1168793Sthompsa/* $OpenBSD: if_trunk.c,v 1.30 2007/01/31 06:20:19 reyk Exp $ */ 2168793Sthompsa 3168793Sthompsa/* 4168793Sthompsa * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org> 5168793Sthompsa * 6168793Sthompsa * Permission to use, copy, modify, and distribute this software for any 7168793Sthompsa * purpose with or without fee is hereby granted, provided that the above 8168793Sthompsa * copyright notice and this permission notice appear in all copies. 9168793Sthompsa * 10168793Sthompsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11168793Sthompsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12168793Sthompsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13168793Sthompsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14168793Sthompsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15168793Sthompsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16168793Sthompsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17168793Sthompsa */ 18168793Sthompsa 19168793Sthompsa#include <sys/cdefs.h> 20168793Sthompsa__FBSDID("$FreeBSD: head/sys/net/if_lagg.c 169688 2007-05-18 23:38:35Z thompsa $"); 21168793Sthompsa 22168793Sthompsa#include "opt_inet.h" 23168793Sthompsa#include "opt_inet6.h" 24168793Sthompsa 25168793Sthompsa#include <sys/param.h> 26168793Sthompsa#include <sys/kernel.h> 27168793Sthompsa#include <sys/malloc.h> 28168793Sthompsa#include <sys/mbuf.h> 29168793Sthompsa#include <sys/queue.h> 30168793Sthompsa#include <sys/socket.h> 31168793Sthompsa#include <sys/sockio.h> 32168793Sthompsa#include <sys/sysctl.h> 33168793Sthompsa#include <sys/module.h> 34168793Sthompsa#include <sys/priv.h> 35168793Sthompsa#include <sys/systm.h> 36168793Sthompsa#include <sys/proc.h> 37168793Sthompsa#include <sys/hash.h> 38169569Sthompsa#include <sys/lock.h> 39169569Sthompsa#include <sys/rwlock.h> 40169329Sthompsa#include <sys/taskqueue.h> 41168793Sthompsa 42168793Sthompsa#include <net/ethernet.h> 43168793Sthompsa#include <net/if.h> 44168793Sthompsa#include <net/if_clone.h> 45168793Sthompsa#include <net/if_arp.h> 46168793Sthompsa#include <net/if_dl.h> 47168793Sthompsa#include <net/if_llc.h> 48168793Sthompsa#include <net/if_media.h> 49168793Sthompsa#include <net/if_types.h> 50168793Sthompsa#include <net/if_var.h> 51168793Sthompsa#include <net/bpf.h> 52168793Sthompsa 53168793Sthompsa#ifdef INET 54168793Sthompsa#include <netinet/in.h> 55168793Sthompsa#include <netinet/in_systm.h> 56168793Sthompsa#include <netinet/if_ether.h> 57168793Sthompsa#include <netinet/ip.h> 58168793Sthompsa#endif 59168793Sthompsa 60168793Sthompsa#ifdef INET6 61168793Sthompsa#include <netinet/ip6.h> 62168793Sthompsa#endif 63168793Sthompsa 64168793Sthompsa#include <net/if_vlan_var.h> 65168793Sthompsa#include <net/if_lagg.h> 66168793Sthompsa#include <net/ieee8023ad_lacp.h> 67168793Sthompsa 68168793Sthompsa/* Special flags we should propagate to the lagg ports. */ 69168793Sthompsastatic struct { 70168793Sthompsa int flag; 71168793Sthompsa int (*func)(struct ifnet *, int); 72168793Sthompsa} lagg_pflags[] = { 73168793Sthompsa {IFF_PROMISC, ifpromisc}, 74168793Sthompsa {IFF_ALLMULTI, if_allmulti}, 75168793Sthompsa {0, NULL} 76168793Sthompsa}; 77168793Sthompsa 78168793SthompsaSLIST_HEAD(__trhead, lagg_softc) lagg_list; /* list of laggs */ 79168793Sthompsastatic struct mtx lagg_list_mtx; 80168793Sthompsaeventhandler_tag lagg_detach_cookie = NULL; 81168793Sthompsa 82168793Sthompsastatic int lagg_clone_create(struct if_clone *, int, caddr_t); 83168793Sthompsastatic void lagg_clone_destroy(struct ifnet *); 84168793Sthompsastatic void lagg_lladdr(struct lagg_softc *, uint8_t *); 85168793Sthompsastatic int lagg_capabilities(struct lagg_softc *); 86168793Sthompsastatic void lagg_port_lladdr(struct lagg_port *, uint8_t *); 87169329Sthompsastatic void lagg_port_setlladdr(void *, int); 88168793Sthompsastatic int lagg_port_create(struct lagg_softc *, struct ifnet *); 89168793Sthompsastatic int lagg_port_destroy(struct lagg_port *, int); 90168793Sthompsastatic struct mbuf *lagg_input(struct ifnet *, struct mbuf *); 91168793Sthompsastatic void lagg_port_state(struct ifnet *, int); 92168793Sthompsastatic int lagg_port_ioctl(struct ifnet *, u_long, caddr_t); 93168793Sthompsastatic int lagg_port_output(struct ifnet *, struct mbuf *, 94168793Sthompsa struct sockaddr *, struct rtentry *); 95168793Sthompsastatic void lagg_port_ifdetach(void *arg __unused, struct ifnet *); 96168793Sthompsastatic int lagg_port_checkstacking(struct lagg_softc *); 97168793Sthompsastatic void lagg_port2req(struct lagg_port *, struct lagg_reqport *); 98168793Sthompsastatic void lagg_init(void *); 99168793Sthompsastatic void lagg_stop(struct lagg_softc *); 100168793Sthompsastatic int lagg_ioctl(struct ifnet *, u_long, caddr_t); 101168793Sthompsastatic int lagg_ether_setmulti(struct lagg_softc *); 102168793Sthompsastatic int lagg_ether_cmdmulti(struct lagg_port *, int); 103168793Sthompsastatic int lagg_setflag(struct lagg_port *, int, int, 104168793Sthompsa int (*func)(struct ifnet *, int)); 105168793Sthompsastatic int lagg_setflags(struct lagg_port *, int status); 106168793Sthompsastatic void lagg_start(struct ifnet *); 107168793Sthompsastatic int lagg_media_change(struct ifnet *); 108168793Sthompsastatic void lagg_media_status(struct ifnet *, struct ifmediareq *); 109168793Sthompsastatic struct lagg_port *lagg_link_active(struct lagg_softc *, 110168793Sthompsa struct lagg_port *); 111168793Sthompsastatic const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *); 112168793Sthompsa 113168793SthompsaIFC_SIMPLE_DECLARE(lagg, 0); 114168793Sthompsa 115168793Sthompsa/* Simple round robin */ 116168793Sthompsastatic int lagg_rr_attach(struct lagg_softc *); 117168793Sthompsastatic int lagg_rr_detach(struct lagg_softc *); 118168793Sthompsastatic void lagg_rr_port_destroy(struct lagg_port *); 119168793Sthompsastatic int lagg_rr_start(struct lagg_softc *, struct mbuf *); 120168793Sthompsastatic struct mbuf *lagg_rr_input(struct lagg_softc *, struct lagg_port *, 121168793Sthompsa struct mbuf *); 122168793Sthompsa 123168793Sthompsa/* Active failover */ 124168793Sthompsastatic int lagg_fail_attach(struct lagg_softc *); 125168793Sthompsastatic int lagg_fail_detach(struct lagg_softc *); 126168793Sthompsastatic int lagg_fail_start(struct lagg_softc *, struct mbuf *); 127168793Sthompsastatic struct mbuf *lagg_fail_input(struct lagg_softc *, struct lagg_port *, 128168793Sthompsa struct mbuf *); 129168793Sthompsa 130168793Sthompsa/* Loadbalancing */ 131168793Sthompsastatic int lagg_lb_attach(struct lagg_softc *); 132168793Sthompsastatic int lagg_lb_detach(struct lagg_softc *); 133168793Sthompsastatic int lagg_lb_port_create(struct lagg_port *); 134168793Sthompsastatic void lagg_lb_port_destroy(struct lagg_port *); 135168793Sthompsastatic int lagg_lb_start(struct lagg_softc *, struct mbuf *); 136168793Sthompsastatic struct mbuf *lagg_lb_input(struct lagg_softc *, struct lagg_port *, 137168793Sthompsa struct mbuf *); 138168793Sthompsastatic int lagg_lb_porttable(struct lagg_softc *, struct lagg_port *); 139168793Sthompsa 140168793Sthompsa/* 802.3ad LACP */ 141168793Sthompsastatic int lagg_lacp_attach(struct lagg_softc *); 142168793Sthompsastatic int lagg_lacp_detach(struct lagg_softc *); 143168793Sthompsastatic int lagg_lacp_start(struct lagg_softc *, struct mbuf *); 144168793Sthompsastatic struct mbuf *lagg_lacp_input(struct lagg_softc *, struct lagg_port *, 145168793Sthompsa struct mbuf *); 146168793Sthompsastatic void lagg_lacp_lladdr(struct lagg_softc *); 147168793Sthompsa 148168793Sthompsa/* lagg protocol table */ 149168793Sthompsastatic const struct { 150168793Sthompsa int ti_proto; 151168793Sthompsa int (*ti_attach)(struct lagg_softc *); 152168793Sthompsa} lagg_protos[] = { 153168793Sthompsa { LAGG_PROTO_ROUNDROBIN, lagg_rr_attach }, 154168793Sthompsa { LAGG_PROTO_FAILOVER, lagg_fail_attach }, 155168793Sthompsa { LAGG_PROTO_LOADBALANCE, lagg_lb_attach }, 156168793Sthompsa { LAGG_PROTO_ETHERCHANNEL, lagg_lb_attach }, 157168793Sthompsa { LAGG_PROTO_LACP, lagg_lacp_attach }, 158168793Sthompsa { LAGG_PROTO_NONE, NULL } 159168793Sthompsa}; 160168793Sthompsa 161168793Sthompsastatic int 162168793Sthompsalagg_modevent(module_t mod, int type, void *data) 163168793Sthompsa{ 164168793Sthompsa 165168793Sthompsa switch (type) { 166168793Sthompsa case MOD_LOAD: 167168793Sthompsa mtx_init(&lagg_list_mtx, "if_lagg list", NULL, MTX_DEF); 168168793Sthompsa SLIST_INIT(&lagg_list); 169168793Sthompsa if_clone_attach(&lagg_cloner); 170168793Sthompsa lagg_input_p = lagg_input; 171168793Sthompsa lagg_linkstate_p = lagg_port_state; 172168793Sthompsa lagg_detach_cookie = EVENTHANDLER_REGISTER( 173168793Sthompsa ifnet_departure_event, lagg_port_ifdetach, NULL, 174168793Sthompsa EVENTHANDLER_PRI_ANY); 175168793Sthompsa break; 176168793Sthompsa case MOD_UNLOAD: 177168793Sthompsa EVENTHANDLER_DEREGISTER(ifnet_departure_event, 178168793Sthompsa lagg_detach_cookie); 179168793Sthompsa if_clone_detach(&lagg_cloner); 180168793Sthompsa lagg_input_p = NULL; 181168793Sthompsa lagg_linkstate_p = NULL; 182168793Sthompsa mtx_destroy(&lagg_list_mtx); 183168793Sthompsa break; 184168793Sthompsa default: 185168793Sthompsa return (EOPNOTSUPP); 186168793Sthompsa } 187168793Sthompsa return (0); 188168793Sthompsa} 189168793Sthompsa 190168793Sthompsastatic moduledata_t lagg_mod = { 191168793Sthompsa "if_lagg", 192168793Sthompsa lagg_modevent, 193168793Sthompsa 0 194168793Sthompsa}; 195168793Sthompsa 196168793SthompsaDECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 197168793Sthompsa 198168793Sthompsastatic int 199168793Sthompsalagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) 200168793Sthompsa{ 201168793Sthompsa struct lagg_softc *sc; 202168793Sthompsa struct ifnet *ifp; 203168793Sthompsa int i, error = 0; 204168793Sthompsa static const u_char eaddr[6]; /* 00:00:00:00:00:00 */ 205168793Sthompsa 206168793Sthompsa sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 207168793Sthompsa ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 208168793Sthompsa if (ifp == NULL) { 209168793Sthompsa free(sc, M_DEVBUF); 210168793Sthompsa return (ENOSPC); 211168793Sthompsa } 212168793Sthompsa 213168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 214168793Sthompsa for (i = 0; lagg_protos[i].ti_proto != LAGG_PROTO_NONE; i++) { 215168793Sthompsa if (lagg_protos[i].ti_proto == LAGG_PROTO_DEFAULT) { 216168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 217168793Sthompsa if ((error = lagg_protos[i].ti_attach(sc)) != 0) { 218168793Sthompsa if_free_type(ifp, IFT_ETHER); 219168793Sthompsa free(sc, M_DEVBUF); 220168793Sthompsa return (error); 221168793Sthompsa } 222168793Sthompsa break; 223168793Sthompsa } 224168793Sthompsa } 225168793Sthompsa LAGG_LOCK_INIT(sc); 226168793Sthompsa SLIST_INIT(&sc->sc_ports); 227169329Sthompsa TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc); 228168793Sthompsa 229168793Sthompsa /* Initialise pseudo media types */ 230168793Sthompsa ifmedia_init(&sc->sc_media, 0, lagg_media_change, 231168793Sthompsa lagg_media_status); 232168793Sthompsa ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 233168793Sthompsa ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 234168793Sthompsa 235168793Sthompsa if_initname(ifp, ifc->ifc_name, unit); 236168793Sthompsa ifp->if_type = IFT_ETHER; 237168793Sthompsa ifp->if_softc = sc; 238168793Sthompsa ifp->if_start = lagg_start; 239168793Sthompsa ifp->if_init = lagg_init; 240168793Sthompsa ifp->if_ioctl = lagg_ioctl; 241168793Sthompsa ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 242168793Sthompsa 243168793Sthompsa IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 244168793Sthompsa ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 245168793Sthompsa IFQ_SET_READY(&ifp->if_snd); 246168793Sthompsa 247168793Sthompsa /* 248168793Sthompsa * Attach as an ordinary ethernet device, childs will be attached 249168793Sthompsa * as special device IFT_IEEE8023ADLAG. 250168793Sthompsa */ 251168793Sthompsa ether_ifattach(ifp, eaddr); 252168793Sthompsa 253168793Sthompsa /* Insert into the global list of laggs */ 254168793Sthompsa mtx_lock(&lagg_list_mtx); 255168793Sthompsa SLIST_INSERT_HEAD(&lagg_list, sc, sc_entries); 256168793Sthompsa mtx_unlock(&lagg_list_mtx); 257168793Sthompsa 258168793Sthompsa return (0); 259168793Sthompsa} 260168793Sthompsa 261168793Sthompsastatic void 262168793Sthompsalagg_clone_destroy(struct ifnet *ifp) 263168793Sthompsa{ 264168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 265168793Sthompsa struct lagg_port *lp; 266168793Sthompsa 267169569Sthompsa LAGG_WLOCK(sc); 268168793Sthompsa 269168793Sthompsa lagg_stop(sc); 270168793Sthompsa ifp->if_flags &= ~IFF_UP; 271168793Sthompsa 272168793Sthompsa /* Shutdown and remove lagg ports */ 273168793Sthompsa while ((lp = SLIST_FIRST(&sc->sc_ports)) != NULL) 274168793Sthompsa lagg_port_destroy(lp, 1); 275168793Sthompsa /* Unhook the aggregation protocol */ 276168793Sthompsa if (sc->sc_detach != NULL) 277168793Sthompsa (*sc->sc_detach)(sc); 278168793Sthompsa 279169569Sthompsa LAGG_WUNLOCK(sc); 280168793Sthompsa 281168793Sthompsa ifmedia_removeall(&sc->sc_media); 282168793Sthompsa ether_ifdetach(ifp); 283168793Sthompsa if_free_type(ifp, IFT_ETHER); 284168793Sthompsa 285168793Sthompsa mtx_lock(&lagg_list_mtx); 286168793Sthompsa SLIST_REMOVE(&lagg_list, sc, lagg_softc, sc_entries); 287168793Sthompsa mtx_unlock(&lagg_list_mtx); 288168793Sthompsa 289169329Sthompsa taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task); 290168793Sthompsa LAGG_LOCK_DESTROY(sc); 291168793Sthompsa free(sc, M_DEVBUF); 292168793Sthompsa} 293168793Sthompsa 294168793Sthompsastatic void 295168793Sthompsalagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr) 296168793Sthompsa{ 297168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 298168793Sthompsa 299168793Sthompsa if (memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) 300168793Sthompsa return; 301168793Sthompsa 302168793Sthompsa bcopy(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN); 303168793Sthompsa /* Let the protocol know the MAC has changed */ 304168793Sthompsa if (sc->sc_lladdr != NULL) 305168793Sthompsa (*sc->sc_lladdr)(sc); 306168793Sthompsa} 307168793Sthompsa 308168793Sthompsastatic int 309168793Sthompsalagg_capabilities(struct lagg_softc *sc) 310168793Sthompsa{ 311168793Sthompsa struct lagg_port *lp; 312168793Sthompsa int cap = ~0, priv; 313168793Sthompsa 314169569Sthompsa LAGG_WLOCK_ASSERT(sc); 315168793Sthompsa 316168793Sthompsa /* Preserve private capabilities */ 317168793Sthompsa priv = sc->sc_capabilities & IFCAP_LAGG_MASK; 318168793Sthompsa 319168793Sthompsa /* Get capabilities from the lagg ports */ 320168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 321168793Sthompsa cap &= lp->lp_capabilities; 322168793Sthompsa 323168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) { 324168793Sthompsa printf("%s: capabilities 0x%08x\n", 325168793Sthompsa sc->sc_ifname, cap == ~0 ? priv : (cap | priv)); 326168793Sthompsa } 327168793Sthompsa 328168793Sthompsa return (cap == ~0 ? priv : (cap | priv)); 329168793Sthompsa} 330168793Sthompsa 331168793Sthompsastatic void 332168793Sthompsalagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr) 333168793Sthompsa{ 334169329Sthompsa struct lagg_softc *sc = lp->lp_lagg; 335168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 336169329Sthompsa struct lagg_llq *llq; 337169329Sthompsa int pending = 0; 338168793Sthompsa 339169569Sthompsa LAGG_WLOCK_ASSERT(sc); 340169329Sthompsa 341169328Sthompsa if (lp->lp_detaching || 342169328Sthompsa memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) 343168793Sthompsa return; 344168793Sthompsa 345169329Sthompsa /* Check to make sure its not already queued to be changed */ 346169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 347169329Sthompsa if (llq->llq_ifp == ifp) { 348169329Sthompsa pending = 1; 349169329Sthompsa break; 350169329Sthompsa } 351169329Sthompsa } 352168793Sthompsa 353169329Sthompsa if (!pending) { 354169329Sthompsa llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT); 355169329Sthompsa if (llq == NULL) /* XXX what to do */ 356169329Sthompsa return; 357169329Sthompsa } 358169329Sthompsa 359169329Sthompsa /* Update the lladdr even if pending, it may have changed */ 360169329Sthompsa llq->llq_ifp = ifp; 361169329Sthompsa bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN); 362169329Sthompsa 363169329Sthompsa if (!pending) 364169329Sthompsa SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries); 365169329Sthompsa 366169329Sthompsa taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task); 367168793Sthompsa} 368168793Sthompsa 369169329Sthompsa/* 370169329Sthompsa * Set the interface MAC address from a taskqueue to avoid a LOR. 371169329Sthompsa */ 372169329Sthompsastatic void 373169329Sthompsalagg_port_setlladdr(void *arg, int pending) 374169329Sthompsa{ 375169329Sthompsa struct lagg_softc *sc = (struct lagg_softc *)arg; 376169329Sthompsa struct lagg_llq *llq, *head; 377169329Sthompsa struct ifnet *ifp; 378169329Sthompsa int error; 379169329Sthompsa 380169329Sthompsa /* Grab a local reference of the queue and remove it from the softc */ 381169569Sthompsa LAGG_WLOCK(sc); 382169329Sthompsa head = SLIST_FIRST(&sc->sc_llq_head); 383169329Sthompsa SLIST_FIRST(&sc->sc_llq_head) = NULL; 384169569Sthompsa LAGG_WUNLOCK(sc); 385169329Sthompsa 386169329Sthompsa /* 387169329Sthompsa * Traverse the queue and set the lladdr on each ifp. It is safe to do 388169329Sthompsa * unlocked as we have the only reference to it. 389169329Sthompsa */ 390169329Sthompsa for (llq = head; llq != NULL; llq = head) { 391169329Sthompsa ifp = llq->llq_ifp; 392169329Sthompsa 393169329Sthompsa /* Set the link layer address */ 394169329Sthompsa error = if_setlladdr(ifp, llq->llq_lladdr, ETHER_ADDR_LEN); 395169329Sthompsa if (error) 396169329Sthompsa printf("%s: setlladdr failed on %s\n", __func__, 397169329Sthompsa ifp->if_xname); 398169329Sthompsa 399169329Sthompsa head = SLIST_NEXT(llq, llq_entries); 400169329Sthompsa free(llq, M_DEVBUF); 401169329Sthompsa } 402169329Sthompsa} 403169329Sthompsa 404168793Sthompsastatic int 405168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) 406168793Sthompsa{ 407168793Sthompsa struct lagg_softc *sc_ptr; 408168793Sthompsa struct lagg_port *lp; 409168793Sthompsa int error = 0; 410168793Sthompsa 411169569Sthompsa LAGG_WLOCK_ASSERT(sc); 412168793Sthompsa 413168793Sthompsa /* Limit the maximal number of lagg ports */ 414168793Sthompsa if (sc->sc_count >= LAGG_MAX_PORTS) 415168793Sthompsa return (ENOSPC); 416168793Sthompsa 417168793Sthompsa /* New lagg port has to be in an idle state */ 418168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 419168793Sthompsa return (EBUSY); 420168793Sthompsa 421168793Sthompsa /* Check if port has already been associated to a lagg */ 422168793Sthompsa if (ifp->if_lagg != NULL) 423168793Sthompsa return (EBUSY); 424168793Sthompsa 425168793Sthompsa /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 426168793Sthompsa if (ifp->if_type != IFT_ETHER) 427168793Sthompsa return (EPROTONOSUPPORT); 428168793Sthompsa 429168793Sthompsa if ((lp = malloc(sizeof(struct lagg_port), 430168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 431168793Sthompsa return (ENOMEM); 432168793Sthompsa 433168793Sthompsa /* Check if port is a stacked lagg */ 434168793Sthompsa mtx_lock(&lagg_list_mtx); 435168793Sthompsa SLIST_FOREACH(sc_ptr, &lagg_list, sc_entries) { 436168793Sthompsa if (ifp == sc_ptr->sc_ifp) { 437168793Sthompsa mtx_unlock(&lagg_list_mtx); 438168793Sthompsa free(lp, M_DEVBUF); 439168793Sthompsa return (EINVAL); 440168793Sthompsa /* XXX disable stacking for the moment, its untested 441168793Sthompsa lp->lp_flags |= LAGG_PORT_STACK; 442168793Sthompsa if (lagg_port_checkstacking(sc_ptr) >= 443168793Sthompsa LAGG_MAX_STACKING) { 444168793Sthompsa mtx_unlock(&lagg_list_mtx); 445168793Sthompsa free(lp, M_DEVBUF); 446168793Sthompsa return (E2BIG); 447168793Sthompsa } 448168793Sthompsa */ 449168793Sthompsa } 450168793Sthompsa } 451168793Sthompsa mtx_unlock(&lagg_list_mtx); 452168793Sthompsa 453168793Sthompsa /* Change the interface type */ 454168793Sthompsa lp->lp_iftype = ifp->if_type; 455168793Sthompsa ifp->if_type = IFT_IEEE8023ADLAG; 456168793Sthompsa ifp->if_lagg = lp; 457168793Sthompsa lp->lp_ioctl = ifp->if_ioctl; 458168793Sthompsa ifp->if_ioctl = lagg_port_ioctl; 459168793Sthompsa lp->lp_output = ifp->if_output; 460168793Sthompsa ifp->if_output = lagg_port_output; 461168793Sthompsa 462168793Sthompsa lp->lp_ifp = ifp; 463168793Sthompsa lp->lp_lagg = sc; 464168793Sthompsa 465168793Sthompsa /* Save port link layer address */ 466168793Sthompsa bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN); 467168793Sthompsa 468168793Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) { 469168793Sthompsa sc->sc_primary = lp; 470168793Sthompsa lagg_lladdr(sc, IF_LLADDR(ifp)); 471168793Sthompsa } else { 472168793Sthompsa /* Update link layer address for this port */ 473168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp)); 474168793Sthompsa } 475168793Sthompsa 476168793Sthompsa /* Insert into the list of ports */ 477168793Sthompsa SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries); 478168793Sthompsa sc->sc_count++; 479168793Sthompsa 480168793Sthompsa /* Update lagg capabilities */ 481168793Sthompsa sc->sc_capabilities = lagg_capabilities(sc); 482168793Sthompsa 483168793Sthompsa /* Add multicast addresses and interface flags to this port */ 484168793Sthompsa lagg_ether_cmdmulti(lp, 1); 485168793Sthompsa lagg_setflags(lp, 1); 486168793Sthompsa 487168793Sthompsa if (sc->sc_port_create != NULL) 488168793Sthompsa error = (*sc->sc_port_create)(lp); 489168793Sthompsa if (error) { 490168793Sthompsa /* remove the port again, without calling sc_port_destroy */ 491168793Sthompsa lagg_port_destroy(lp, 0); 492168793Sthompsa return (error); 493168793Sthompsa } 494168793Sthompsa 495168793Sthompsa return (error); 496168793Sthompsa} 497168793Sthompsa 498168793Sthompsastatic int 499168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc) 500168793Sthompsa{ 501168793Sthompsa struct lagg_softc *sc_ptr; 502168793Sthompsa struct lagg_port *lp; 503168793Sthompsa int m = 0; 504168793Sthompsa 505169569Sthompsa LAGG_WLOCK_ASSERT(sc); 506168793Sthompsa 507168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 508168793Sthompsa if (lp->lp_flags & LAGG_PORT_STACK) { 509168793Sthompsa sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc; 510168793Sthompsa m = MAX(m, lagg_port_checkstacking(sc_ptr)); 511168793Sthompsa } 512168793Sthompsa } 513168793Sthompsa 514168793Sthompsa return (m + 1); 515168793Sthompsa} 516168793Sthompsa 517168793Sthompsastatic int 518168793Sthompsalagg_port_destroy(struct lagg_port *lp, int runpd) 519168793Sthompsa{ 520168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 521168793Sthompsa struct lagg_port *lp_ptr; 522169329Sthompsa struct lagg_llq *llq; 523168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 524168793Sthompsa 525169569Sthompsa LAGG_WLOCK_ASSERT(sc); 526168793Sthompsa 527168793Sthompsa if (runpd && sc->sc_port_destroy != NULL) 528168793Sthompsa (*sc->sc_port_destroy)(lp); 529168793Sthompsa 530169328Sthompsa /* 531169328Sthompsa * Remove multicast addresses and interface flags from this port and 532169328Sthompsa * reset the MAC address, skip if the interface is being detached. 533169328Sthompsa */ 534169328Sthompsa if (!lp->lp_detaching) { 535169328Sthompsa lagg_ether_cmdmulti(lp, 0); 536169328Sthompsa lagg_setflags(lp, 0); 537169328Sthompsa lagg_port_lladdr(lp, lp->lp_lladdr); 538169328Sthompsa } 539168793Sthompsa 540168793Sthompsa /* Restore interface */ 541168793Sthompsa ifp->if_type = lp->lp_iftype; 542168793Sthompsa ifp->if_ioctl = lp->lp_ioctl; 543168793Sthompsa ifp->if_output = lp->lp_output; 544168793Sthompsa ifp->if_lagg = NULL; 545168793Sthompsa 546168793Sthompsa /* Finally, remove the port from the lagg */ 547168793Sthompsa SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries); 548168793Sthompsa sc->sc_count--; 549168793Sthompsa 550168793Sthompsa /* Update the primary interface */ 551168793Sthompsa if (lp == sc->sc_primary) { 552168793Sthompsa uint8_t lladdr[ETHER_ADDR_LEN]; 553168793Sthompsa 554168793Sthompsa if ((lp_ptr = SLIST_FIRST(&sc->sc_ports)) == NULL) { 555168793Sthompsa bzero(&lladdr, ETHER_ADDR_LEN); 556168793Sthompsa } else { 557168793Sthompsa bcopy(lp_ptr->lp_lladdr, 558168793Sthompsa lladdr, ETHER_ADDR_LEN); 559168793Sthompsa } 560168793Sthompsa lagg_lladdr(sc, lladdr); 561168793Sthompsa sc->sc_primary = lp_ptr; 562168793Sthompsa 563168793Sthompsa /* Update link layer address for each port */ 564168793Sthompsa SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries) 565168793Sthompsa lagg_port_lladdr(lp_ptr, lladdr); 566168793Sthompsa } 567168793Sthompsa 568169329Sthompsa /* Remove any pending lladdr changes from the queue */ 569169329Sthompsa if (lp->lp_detaching) { 570169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 571169329Sthompsa if (llq->llq_ifp == ifp) { 572169329Sthompsa SLIST_REMOVE(&sc->sc_llq_head, llq, lagg_llq, 573169329Sthompsa llq_entries); 574169329Sthompsa free(llq, M_DEVBUF); 575169329Sthompsa break; /* Only appears once */ 576169329Sthompsa } 577169329Sthompsa } 578169329Sthompsa } 579169329Sthompsa 580168793Sthompsa if (lp->lp_ifflags) 581168793Sthompsa if_printf(ifp, "%s: lp_ifflags unclean\n", __func__); 582168793Sthompsa 583168793Sthompsa free(lp, M_DEVBUF); 584168793Sthompsa 585168793Sthompsa /* Update lagg capabilities */ 586168793Sthompsa sc->sc_capabilities = lagg_capabilities(sc); 587168793Sthompsa 588168793Sthompsa return (0); 589168793Sthompsa} 590168793Sthompsa 591168793Sthompsastatic int 592168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 593168793Sthompsa{ 594168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data; 595168793Sthompsa struct lagg_softc *sc; 596168793Sthompsa struct lagg_port *lp = NULL; 597168793Sthompsa int error = 0; 598168793Sthompsa 599168793Sthompsa /* Should be checked by the caller */ 600168793Sthompsa if (ifp->if_type != IFT_IEEE8023ADLAG || 601168793Sthompsa (lp = ifp->if_lagg) == NULL || (sc = lp->lp_lagg) == NULL) 602168793Sthompsa goto fallback; 603168793Sthompsa 604168793Sthompsa switch (cmd) { 605168793Sthompsa case SIOCGLAGGPORT: 606169569Sthompsa LAGG_RLOCK(sc); 607168793Sthompsa if (rp->rp_portname[0] == '\0' || 608168793Sthompsa ifunit(rp->rp_portname) != ifp) { 609168793Sthompsa error = EINVAL; 610168793Sthompsa break; 611168793Sthompsa } 612168793Sthompsa 613168793Sthompsa if (lp->lp_lagg != sc) { 614168793Sthompsa error = ENOENT; 615168793Sthompsa break; 616168793Sthompsa } 617168793Sthompsa 618168793Sthompsa lagg_port2req(lp, rp); 619169569Sthompsa LAGG_RUNLOCK(sc); 620168793Sthompsa break; 621168793Sthompsa default: 622168793Sthompsa goto fallback; 623168793Sthompsa } 624168793Sthompsa 625168793Sthompsa return (error); 626168793Sthompsa 627168793Sthompsafallback: 628169340Sthompsa if (lp->lp_ioctl != NULL) 629168793Sthompsa return ((*lp->lp_ioctl)(ifp, cmd, data)); 630168793Sthompsa 631168793Sthompsa return (EINVAL); 632168793Sthompsa} 633168793Sthompsa 634168793Sthompsastatic int 635168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m, 636168793Sthompsa struct sockaddr *dst, struct rtentry *rt0) 637168793Sthompsa{ 638168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 639168793Sthompsa struct ether_header *eh; 640168793Sthompsa short type = 0; 641168793Sthompsa 642168793Sthompsa switch (dst->sa_family) { 643168793Sthompsa case pseudo_AF_HDRCMPLT: 644168793Sthompsa case AF_UNSPEC: 645168793Sthompsa eh = (struct ether_header *)dst->sa_data; 646168793Sthompsa type = eh->ether_type; 647168793Sthompsa break; 648168793Sthompsa } 649168793Sthompsa 650168793Sthompsa /* 651168793Sthompsa * Only allow ethernet types required to initiate or maintain the link, 652168793Sthompsa * aggregated frames take a different path. 653168793Sthompsa */ 654168793Sthompsa switch (ntohs(type)) { 655168793Sthompsa case ETHERTYPE_PAE: /* EAPOL PAE/802.1x */ 656168793Sthompsa return ((*lp->lp_output)(ifp, m, dst, rt0)); 657168793Sthompsa } 658168793Sthompsa 659168793Sthompsa /* drop any other frames */ 660168793Sthompsa m_freem(m); 661168793Sthompsa return (EBUSY); 662168793Sthompsa} 663168793Sthompsa 664168793Sthompsastatic void 665168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) 666168793Sthompsa{ 667168793Sthompsa struct lagg_port *lp; 668168793Sthompsa struct lagg_softc *sc; 669168793Sthompsa 670168793Sthompsa if ((lp = ifp->if_lagg) == NULL) 671168793Sthompsa return; 672168793Sthompsa 673168793Sthompsa sc = lp->lp_lagg; 674168793Sthompsa 675169569Sthompsa LAGG_WLOCK(sc); 676169328Sthompsa lp->lp_detaching = 1; 677168793Sthompsa lagg_port_destroy(lp, 1); 678169569Sthompsa LAGG_WUNLOCK(sc); 679168793Sthompsa} 680168793Sthompsa 681168793Sthompsastatic void 682168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp) 683168793Sthompsa{ 684168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 685168793Sthompsa strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname)); 686168793Sthompsa strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname)); 687168793Sthompsa rp->rp_prio = lp->lp_prio; 688168793Sthompsa rp->rp_flags = lp->lp_flags; 689168793Sthompsa 690168793Sthompsa /* Add protocol specific flags */ 691168793Sthompsa switch (sc->sc_proto) { 692168793Sthompsa case LAGG_PROTO_FAILOVER: 693168793Sthompsa if (lp == sc->sc_primary) 694169204Sthompsa rp->rp_flags |= LAGG_PORT_MASTER; 695168793Sthompsa /* FALLTHROUGH */ 696168793Sthompsa case LAGG_PROTO_ROUNDROBIN: 697168793Sthompsa case LAGG_PROTO_LOADBALANCE: 698168793Sthompsa case LAGG_PROTO_ETHERCHANNEL: 699168793Sthompsa if (LAGG_PORTACTIVE(lp)) 700168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 701168793Sthompsa break; 702168793Sthompsa 703168793Sthompsa case LAGG_PROTO_LACP: 704168793Sthompsa /* LACP has a different definition of active */ 705168793Sthompsa if (lacp_port_isactive(lp)) 706168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 707168793Sthompsa break; 708168793Sthompsa } 709168793Sthompsa 710168793Sthompsa} 711168793Sthompsa 712168793Sthompsastatic void 713168793Sthompsalagg_init(void *xsc) 714168793Sthompsa{ 715168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)xsc; 716168793Sthompsa struct lagg_port *lp; 717168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 718168793Sthompsa 719168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 720168793Sthompsa return; 721168793Sthompsa 722169569Sthompsa LAGG_WLOCK(sc); 723168793Sthompsa 724168793Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 725168793Sthompsa /* Update the port lladdrs */ 726168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 727168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(ifp)); 728168793Sthompsa 729168793Sthompsa if (sc->sc_init != NULL) 730168793Sthompsa (*sc->sc_init)(sc); 731168793Sthompsa 732169569Sthompsa LAGG_WUNLOCK(sc); 733168793Sthompsa} 734168793Sthompsa 735168793Sthompsastatic void 736168793Sthompsalagg_stop(struct lagg_softc *sc) 737168793Sthompsa{ 738168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 739168793Sthompsa 740169569Sthompsa LAGG_WLOCK_ASSERT(sc); 741168793Sthompsa 742168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 743168793Sthompsa return; 744168793Sthompsa 745168793Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 746168793Sthompsa 747168793Sthompsa if (sc->sc_stop != NULL) 748168793Sthompsa (*sc->sc_stop)(sc); 749168793Sthompsa} 750168793Sthompsa 751168793Sthompsastatic int 752168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 753168793Sthompsa{ 754168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 755168793Sthompsa struct lagg_reqall *ra = (struct lagg_reqall *)data; 756168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf; 757168793Sthompsa struct ifreq *ifr = (struct ifreq *)data; 758168793Sthompsa struct lagg_port *lp; 759168793Sthompsa struct ifnet *tpif; 760168793Sthompsa struct thread *td = curthread; 761168793Sthompsa int i, error = 0, unlock = 1; 762168793Sthompsa 763169569Sthompsa LAGG_WLOCK(sc); 764168793Sthompsa 765168793Sthompsa bzero(&rpbuf, sizeof(rpbuf)); 766168793Sthompsa 767168793Sthompsa switch (cmd) { 768168793Sthompsa case SIOCGLAGG: 769168793Sthompsa ra->ra_proto = sc->sc_proto; 770168793Sthompsa ra->ra_ports = i = 0; 771168793Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 772168793Sthompsa while (lp && ra->ra_size >= 773168793Sthompsa i + sizeof(struct lagg_reqport)) { 774168793Sthompsa lagg_port2req(lp, &rpbuf); 775168793Sthompsa error = copyout(&rpbuf, (caddr_t)ra->ra_port + i, 776168793Sthompsa sizeof(struct lagg_reqport)); 777168793Sthompsa if (error) 778168793Sthompsa break; 779168793Sthompsa i += sizeof(struct lagg_reqport); 780168793Sthompsa ra->ra_ports++; 781168793Sthompsa lp = SLIST_NEXT(lp, lp_entries); 782168793Sthompsa } 783168793Sthompsa break; 784168793Sthompsa case SIOCSLAGG: 785168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 786168793Sthompsa if (error) 787168793Sthompsa break; 788168793Sthompsa if (ra->ra_proto >= LAGG_PROTO_MAX) { 789168793Sthompsa error = EPROTONOSUPPORT; 790168793Sthompsa break; 791168793Sthompsa } 792168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) { 793168793Sthompsa error = sc->sc_detach(sc); 794168793Sthompsa /* Reset protocol and pointers */ 795168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 796168793Sthompsa sc->sc_detach = NULL; 797168793Sthompsa sc->sc_start = NULL; 798168793Sthompsa sc->sc_input = NULL; 799168793Sthompsa sc->sc_port_create = NULL; 800168793Sthompsa sc->sc_port_destroy = NULL; 801168793Sthompsa sc->sc_linkstate = NULL; 802168793Sthompsa sc->sc_init = NULL; 803168793Sthompsa sc->sc_stop = NULL; 804168793Sthompsa sc->sc_lladdr = NULL; 805168793Sthompsa } 806168793Sthompsa if (error != 0) 807168793Sthompsa break; 808168793Sthompsa for (i = 0; i < (sizeof(lagg_protos) / 809168793Sthompsa sizeof(lagg_protos[0])); i++) { 810168793Sthompsa if (lagg_protos[i].ti_proto == ra->ra_proto) { 811168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 812168793Sthompsa printf("%s: using proto %u\n", 813168793Sthompsa sc->sc_ifname, 814168793Sthompsa lagg_protos[i].ti_proto); 815168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 816168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) 817168793Sthompsa error = lagg_protos[i].ti_attach(sc); 818168793Sthompsa goto out; 819168793Sthompsa } 820168793Sthompsa } 821168793Sthompsa error = EPROTONOSUPPORT; 822168793Sthompsa break; 823168793Sthompsa case SIOCGLAGGPORT: 824168793Sthompsa if (rp->rp_portname[0] == '\0' || 825168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 826168793Sthompsa error = EINVAL; 827168793Sthompsa break; 828168793Sthompsa } 829168793Sthompsa 830168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 831168793Sthompsa lp->lp_lagg != sc) { 832168793Sthompsa error = ENOENT; 833168793Sthompsa break; 834168793Sthompsa } 835168793Sthompsa 836168793Sthompsa lagg_port2req(lp, rp); 837168793Sthompsa break; 838168793Sthompsa case SIOCSLAGGPORT: 839168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 840168793Sthompsa if (error) 841168793Sthompsa break; 842168793Sthompsa if (rp->rp_portname[0] == '\0' || 843168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 844168793Sthompsa error = EINVAL; 845168793Sthompsa break; 846168793Sthompsa } 847168793Sthompsa error = lagg_port_create(sc, tpif); 848168793Sthompsa break; 849168793Sthompsa case SIOCSLAGGDELPORT: 850168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 851168793Sthompsa if (error) 852168793Sthompsa break; 853168793Sthompsa if (rp->rp_portname[0] == '\0' || 854168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 855168793Sthompsa error = EINVAL; 856168793Sthompsa break; 857168793Sthompsa } 858168793Sthompsa 859168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 860168793Sthompsa lp->lp_lagg != sc) { 861168793Sthompsa error = ENOENT; 862168793Sthompsa break; 863168793Sthompsa } 864168793Sthompsa 865168793Sthompsa error = lagg_port_destroy(lp, 1); 866168793Sthompsa break; 867168793Sthompsa case SIOCSIFFLAGS: 868168793Sthompsa /* Set flags on ports too */ 869168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 870168793Sthompsa lagg_setflags(lp, 1); 871168793Sthompsa } 872168793Sthompsa 873168793Sthompsa if (!(ifp->if_flags & IFF_UP) && 874168793Sthompsa (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 875168793Sthompsa /* 876168793Sthompsa * If interface is marked down and it is running, 877168793Sthompsa * then stop and disable it. 878168793Sthompsa */ 879168793Sthompsa lagg_stop(sc); 880168793Sthompsa } else if ((ifp->if_flags & IFF_UP) && 881168793Sthompsa !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 882168793Sthompsa /* 883168793Sthompsa * If interface is marked up and it is stopped, then 884168793Sthompsa * start it. 885168793Sthompsa */ 886169569Sthompsa LAGG_WUNLOCK(sc); 887168793Sthompsa unlock = 0; 888168793Sthompsa (*ifp->if_init)(sc); 889168793Sthompsa } 890168793Sthompsa break; 891168793Sthompsa case SIOCADDMULTI: 892168793Sthompsa case SIOCDELMULTI: 893168793Sthompsa error = lagg_ether_setmulti(sc); 894168793Sthompsa break; 895168793Sthompsa case SIOCSIFMEDIA: 896168793Sthompsa case SIOCGIFMEDIA: 897169569Sthompsa LAGG_WUNLOCK(sc); 898168793Sthompsa unlock = 0; 899168793Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 900168793Sthompsa break; 901168793Sthompsa default: 902169569Sthompsa LAGG_WUNLOCK(sc); 903168793Sthompsa unlock = 0; 904168793Sthompsa error = ether_ioctl(ifp, cmd, data); 905168793Sthompsa break; 906168793Sthompsa } 907168793Sthompsa 908168793Sthompsaout: 909168793Sthompsa if (unlock) 910169569Sthompsa LAGG_WUNLOCK(sc); 911168793Sthompsa return (error); 912168793Sthompsa} 913168793Sthompsa 914168793Sthompsastatic int 915168793Sthompsalagg_ether_setmulti(struct lagg_softc *sc) 916168793Sthompsa{ 917169327Sthompsa struct lagg_port *lp; 918168793Sthompsa 919169569Sthompsa LAGG_WLOCK_ASSERT(sc); 920168793Sthompsa 921169327Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 922169340Sthompsa /* First, remove any existing filter entries. */ 923169340Sthompsa lagg_ether_cmdmulti(lp, 0); 924169340Sthompsa /* copy all addresses from the lagg interface to the port */ 925169327Sthompsa lagg_ether_cmdmulti(lp, 1); 926169340Sthompsa } 927168793Sthompsa return (0); 928168793Sthompsa} 929168793Sthompsa 930168793Sthompsastatic int 931168793Sthompsalagg_ether_cmdmulti(struct lagg_port *lp, int set) 932168793Sthompsa{ 933168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 934169327Sthompsa struct ifnet *ifp = lp->lp_ifp; 935169327Sthompsa struct ifnet *trifp = sc->sc_ifp; 936169327Sthompsa struct lagg_mc *mc; 937169327Sthompsa struct ifmultiaddr *ifma, *rifma = NULL; 938169327Sthompsa struct sockaddr_dl sdl; 939169327Sthompsa int error; 940168793Sthompsa 941169569Sthompsa LAGG_WLOCK_ASSERT(sc); 942168793Sthompsa 943168793Sthompsa bzero((char *)&sdl, sizeof(sdl)); 944168793Sthompsa sdl.sdl_len = sizeof(sdl); 945168793Sthompsa sdl.sdl_family = AF_LINK; 946168793Sthompsa sdl.sdl_type = IFT_ETHER; 947168793Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 948168793Sthompsa sdl.sdl_index = ifp->if_index; 949168793Sthompsa 950169327Sthompsa if (set) { 951169327Sthompsa TAILQ_FOREACH(ifma, &trifp->if_multiaddrs, ifma_link) { 952169327Sthompsa if (ifma->ifma_addr->sa_family != AF_LINK) 953169327Sthompsa continue; 954169327Sthompsa bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 955169327Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 956168793Sthompsa 957168793Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 958169327Sthompsa if (error) 959169327Sthompsa return (error); 960169327Sthompsa mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); 961169327Sthompsa if (mc == NULL) 962169327Sthompsa return (ENOMEM); 963169327Sthompsa mc->mc_ifma = rifma; 964169327Sthompsa SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); 965168793Sthompsa } 966169327Sthompsa } else { 967169327Sthompsa while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { 968169327Sthompsa SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); 969169327Sthompsa if_delmulti_ifma(mc->mc_ifma); 970169327Sthompsa free(mc, M_DEVBUF); 971169327Sthompsa } 972168793Sthompsa } 973168793Sthompsa return (0); 974168793Sthompsa} 975168793Sthompsa 976168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */ 977168793Sthompsastatic int 978168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status, 979168793Sthompsa int (*func)(struct ifnet *, int)) 980168793Sthompsa{ 981168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 982168793Sthompsa struct ifnet *trifp = sc->sc_ifp; 983168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 984168793Sthompsa int error; 985168793Sthompsa 986169569Sthompsa LAGG_WLOCK_ASSERT(sc); 987168793Sthompsa 988168793Sthompsa status = status ? (trifp->if_flags & flag) : 0; 989168793Sthompsa /* Now "status" contains the flag value or 0 */ 990168793Sthompsa 991168793Sthompsa /* 992168793Sthompsa * See if recorded ports status is different from what 993168793Sthompsa * we want it to be. If it is, flip it. We record ports 994168793Sthompsa * status in lp_ifflags so that we won't clear ports flag 995168793Sthompsa * we haven't set. In fact, we don't clear or set ports 996168793Sthompsa * flags directly, but get or release references to them. 997168793Sthompsa * That's why we can be sure that recorded flags still are 998168793Sthompsa * in accord with actual ports flags. 999168793Sthompsa */ 1000168793Sthompsa if (status != (lp->lp_ifflags & flag)) { 1001168793Sthompsa error = (*func)(ifp, status); 1002168793Sthompsa if (error) 1003168793Sthompsa return (error); 1004168793Sthompsa lp->lp_ifflags &= ~flag; 1005168793Sthompsa lp->lp_ifflags |= status; 1006168793Sthompsa } 1007168793Sthompsa return (0); 1008168793Sthompsa} 1009168793Sthompsa 1010168793Sthompsa/* 1011168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port 1012168793Sthompsa * if "status" is true, update ports flags respective to the lagg 1013168793Sthompsa * if "status" is false, forcedly clear the flags set on port. 1014168793Sthompsa */ 1015168793Sthompsastatic int 1016168793Sthompsalagg_setflags(struct lagg_port *lp, int status) 1017168793Sthompsa{ 1018168793Sthompsa int error, i; 1019168793Sthompsa 1020168793Sthompsa for (i = 0; lagg_pflags[i].flag; i++) { 1021168793Sthompsa error = lagg_setflag(lp, lagg_pflags[i].flag, 1022168793Sthompsa status, lagg_pflags[i].func); 1023168793Sthompsa if (error) 1024168793Sthompsa return (error); 1025168793Sthompsa } 1026168793Sthompsa return (0); 1027168793Sthompsa} 1028168793Sthompsa 1029168793Sthompsastatic void 1030168793Sthompsalagg_start(struct ifnet *ifp) 1031168793Sthompsa{ 1032168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1033168793Sthompsa struct mbuf *m; 1034168793Sthompsa int error = 0; 1035168793Sthompsa 1036169569Sthompsa LAGG_RLOCK(sc); 1037168793Sthompsa for (;; error = 0) { 1038168793Sthompsa IFQ_DEQUEUE(&ifp->if_snd, m); 1039168793Sthompsa if (m == NULL) 1040168793Sthompsa break; 1041168793Sthompsa 1042168793Sthompsa BPF_MTAP(ifp, m); 1043168793Sthompsa 1044169569Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) 1045168793Sthompsa error = (*sc->sc_start)(sc, m); 1046169569Sthompsa else 1047168793Sthompsa m_free(m); 1048168793Sthompsa 1049168793Sthompsa if (error == 0) 1050168793Sthompsa ifp->if_opackets++; 1051168793Sthompsa else 1052168793Sthompsa ifp->if_oerrors++; 1053168793Sthompsa } 1054169569Sthompsa LAGG_RUNLOCK(sc); 1055168793Sthompsa 1056168793Sthompsa return; 1057168793Sthompsa} 1058168793Sthompsa 1059168793Sthompsastatic struct mbuf * 1060168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m) 1061168793Sthompsa{ 1062168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1063168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 1064168793Sthompsa struct ifnet *trifp = sc->sc_ifp; 1065168793Sthompsa 1066168793Sthompsa if ((trifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 1067169227Sthompsa (lp->lp_flags & LAGG_PORT_DISABLED) || 1068168793Sthompsa sc->sc_proto == LAGG_PROTO_NONE) { 1069168793Sthompsa m_freem(m); 1070168793Sthompsa return (NULL); 1071168793Sthompsa } 1072168793Sthompsa 1073169569Sthompsa LAGG_RLOCK(sc); 1074168793Sthompsa BPF_MTAP(trifp, m); 1075168793Sthompsa 1076168793Sthompsa m = (*sc->sc_input)(sc, lp, m); 1077168793Sthompsa 1078168793Sthompsa if (m != NULL) { 1079168793Sthompsa ifp->if_ipackets++; 1080168793Sthompsa ifp->if_ibytes += m->m_pkthdr.len; 1081168793Sthompsa trifp->if_ipackets++; 1082168793Sthompsa trifp->if_ibytes += m->m_pkthdr.len; 1083168793Sthompsa } 1084168793Sthompsa 1085169569Sthompsa LAGG_RUNLOCK(sc); 1086168793Sthompsa return (m); 1087168793Sthompsa} 1088168793Sthompsa 1089168793Sthompsastatic int 1090168793Sthompsalagg_media_change(struct ifnet *ifp) 1091168793Sthompsa{ 1092168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1093168793Sthompsa 1094168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1095168793Sthompsa printf("%s\n", __func__); 1096168793Sthompsa 1097168793Sthompsa /* Ignore */ 1098168793Sthompsa return (0); 1099168793Sthompsa} 1100168793Sthompsa 1101168793Sthompsastatic void 1102168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1103168793Sthompsa{ 1104168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1105168793Sthompsa struct lagg_port *lp; 1106168793Sthompsa 1107168793Sthompsa imr->ifm_status = IFM_AVALID; 1108168793Sthompsa imr->ifm_active = IFM_ETHER | IFM_AUTO; 1109168793Sthompsa 1110169569Sthompsa LAGG_RLOCK(sc); 1111169340Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1112169340Sthompsa if (LAGG_PORTACTIVE(lp)) 1113169340Sthompsa imr->ifm_status |= IFM_ACTIVE; 1114169340Sthompsa } 1115169569Sthompsa LAGG_RUNLOCK(sc); 1116168793Sthompsa} 1117168793Sthompsa 1118168793Sthompsastatic void 1119168793Sthompsalagg_port_state(struct ifnet *ifp, int state) 1120168793Sthompsa{ 1121168793Sthompsa struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; 1122168793Sthompsa struct lagg_softc *sc = NULL; 1123168793Sthompsa 1124168793Sthompsa if (lp != NULL) 1125168793Sthompsa sc = lp->lp_lagg; 1126168793Sthompsa if (sc == NULL) 1127168793Sthompsa return; 1128168793Sthompsa 1129169569Sthompsa LAGG_WLOCK(sc); 1130168793Sthompsa if (sc->sc_linkstate != NULL) 1131168793Sthompsa (*sc->sc_linkstate)(lp); 1132169569Sthompsa LAGG_WUNLOCK(sc); 1133168793Sthompsa} 1134168793Sthompsa 1135168793Sthompsastruct lagg_port * 1136168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) 1137168793Sthompsa{ 1138168793Sthompsa struct lagg_port *lp_next, *rval = NULL; 1139168793Sthompsa // int new_link = LINK_STATE_DOWN; 1140168793Sthompsa 1141169688Sthompsa LAGG_RLOCK_ASSERT(sc); 1142168793Sthompsa /* 1143168793Sthompsa * Search a port which reports an active link state. 1144168793Sthompsa */ 1145168793Sthompsa 1146168793Sthompsa if (lp == NULL) 1147168793Sthompsa goto search; 1148168793Sthompsa if (LAGG_PORTACTIVE(lp)) { 1149168793Sthompsa rval = lp; 1150168793Sthompsa goto found; 1151168793Sthompsa } 1152168793Sthompsa if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL && 1153168793Sthompsa LAGG_PORTACTIVE(lp_next)) { 1154168793Sthompsa rval = lp_next; 1155168793Sthompsa goto found; 1156168793Sthompsa } 1157168793Sthompsa 1158168793Sthompsasearch: 1159168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1160168793Sthompsa if (LAGG_PORTACTIVE(lp_next)) { 1161168793Sthompsa rval = lp_next; 1162168793Sthompsa goto found; 1163168793Sthompsa } 1164168793Sthompsa } 1165168793Sthompsa 1166168793Sthompsafound: 1167168793Sthompsa if (rval != NULL) { 1168168793Sthompsa /* 1169168793Sthompsa * The IEEE 802.1D standard assumes that a lagg with 1170168793Sthompsa * multiple ports is always full duplex. This is valid 1171168793Sthompsa * for load sharing laggs and if at least two links 1172168793Sthompsa * are active. Unfortunately, checking the latter would 1173168793Sthompsa * be too expensive at this point. 1174168793Sthompsa XXX 1175168793Sthompsa if ((sc->sc_capabilities & IFCAP_LAGG_FULLDUPLEX) && 1176168793Sthompsa (sc->sc_count > 1)) 1177168793Sthompsa new_link = LINK_STATE_FULL_DUPLEX; 1178168793Sthompsa else 1179168793Sthompsa new_link = rval->lp_link_state; 1180168793Sthompsa */ 1181168793Sthompsa } 1182168793Sthompsa 1183168793Sthompsa return (rval); 1184168793Sthompsa} 1185168793Sthompsa 1186168793Sthompsastatic const void * 1187168793Sthompsalagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf) 1188168793Sthompsa{ 1189168793Sthompsa if (m->m_pkthdr.len < (off + len)) { 1190168793Sthompsa return (NULL); 1191168793Sthompsa } else if (m->m_len < (off + len)) { 1192168793Sthompsa m_copydata(m, off, len, buf); 1193168793Sthompsa return (buf); 1194168793Sthompsa } 1195168793Sthompsa return (mtod(m, char *) + off); 1196168793Sthompsa} 1197168793Sthompsa 1198168793Sthompsauint32_t 1199168793Sthompsalagg_hashmbuf(struct mbuf *m, uint32_t key) 1200168793Sthompsa{ 1201168793Sthompsa uint16_t etype; 1202169583Sthompsa uint32_t p = 0; 1203168793Sthompsa int off; 1204168793Sthompsa struct ether_header *eh; 1205168793Sthompsa struct ether_vlan_header vlanbuf; 1206168793Sthompsa const struct ether_vlan_header *vlan; 1207168793Sthompsa#ifdef INET 1208168793Sthompsa const struct ip *ip; 1209168793Sthompsa struct ip ipbuf; 1210168793Sthompsa#endif 1211168793Sthompsa#ifdef INET6 1212168793Sthompsa const struct ip6_hdr *ip6; 1213168793Sthompsa struct ip6_hdr ip6buf; 1214169583Sthompsa uint32_t flow; 1215168793Sthompsa#endif 1216168793Sthompsa 1217168793Sthompsa off = sizeof(*eh); 1218168793Sthompsa if (m->m_len < off) 1219168793Sthompsa goto out; 1220168793Sthompsa eh = mtod(m, struct ether_header *); 1221168793Sthompsa etype = ntohs(eh->ether_type); 1222168793Sthompsa p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key); 1223168793Sthompsa p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 1224168793Sthompsa 1225168793Sthompsa /* Special handling for encapsulating VLAN frames */ 1226168793Sthompsa if (m->m_flags & M_VLANTAG) { 1227168793Sthompsa p = hash32_buf(&m->m_pkthdr.ether_vtag, 1228168793Sthompsa sizeof(m->m_pkthdr.ether_vtag), p); 1229168793Sthompsa } else if (etype == ETHERTYPE_VLAN) { 1230168793Sthompsa vlan = lagg_gethdr(m, off, sizeof(*vlan), &vlanbuf); 1231168793Sthompsa if (vlan == NULL) 1232168793Sthompsa goto out; 1233168793Sthompsa 1234168793Sthompsa p = hash32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 1235168793Sthompsa etype = ntohs(vlan->evl_proto); 1236168793Sthompsa off += sizeof(*vlan) - sizeof(*eh); 1237168793Sthompsa } 1238168793Sthompsa 1239168793Sthompsa switch (etype) { 1240168793Sthompsa#ifdef INET 1241168793Sthompsa case ETHERTYPE_IP: 1242168793Sthompsa ip = lagg_gethdr(m, off, sizeof(*ip), &ipbuf); 1243168793Sthompsa if (ip == NULL) 1244168793Sthompsa goto out; 1245168793Sthompsa 1246168793Sthompsa p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p); 1247168793Sthompsa p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 1248168793Sthompsa break; 1249168793Sthompsa#endif 1250168793Sthompsa#ifdef INET6 1251168793Sthompsa case ETHERTYPE_IPV6: 1252168793Sthompsa ip6 = lagg_gethdr(m, off, sizeof(*ip6), &ip6buf); 1253168793Sthompsa if (ip6 == NULL) 1254168793Sthompsa goto out; 1255168793Sthompsa 1256168793Sthompsa p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 1257168793Sthompsa p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 1258169570Sthompsa flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 1259169570Sthompsa p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */ 1260168793Sthompsa break; 1261168793Sthompsa#endif 1262168793Sthompsa } 1263168793Sthompsaout: 1264168793Sthompsa return (p); 1265168793Sthompsa} 1266168793Sthompsa 1267168793Sthompsaint 1268168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m) 1269168793Sthompsa{ 1270168793Sthompsa int error = 0; 1271168793Sthompsa 1272168793Sthompsa /* Send mbuf */ 1273168793Sthompsa IFQ_ENQUEUE(&ifp->if_snd, m, error); 1274168793Sthompsa if (error) 1275168793Sthompsa return (error); 1276168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 1277168793Sthompsa (*ifp->if_start)(ifp); 1278168793Sthompsa 1279168793Sthompsa ifp->if_obytes += m->m_pkthdr.len; 1280168793Sthompsa if (m->m_flags & M_MCAST) 1281168793Sthompsa ifp->if_omcasts++; 1282168793Sthompsa 1283168793Sthompsa return (error); 1284168793Sthompsa} 1285168793Sthompsa 1286168793Sthompsa/* 1287168793Sthompsa * Simple round robin aggregation 1288168793Sthompsa */ 1289168793Sthompsa 1290168793Sthompsastatic int 1291168793Sthompsalagg_rr_attach(struct lagg_softc *sc) 1292168793Sthompsa{ 1293168793Sthompsa struct lagg_port *lp; 1294168793Sthompsa 1295168793Sthompsa sc->sc_detach = lagg_rr_detach; 1296168793Sthompsa sc->sc_start = lagg_rr_start; 1297168793Sthompsa sc->sc_input = lagg_rr_input; 1298168793Sthompsa sc->sc_port_create = NULL; 1299168793Sthompsa sc->sc_port_destroy = lagg_rr_port_destroy; 1300168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1301168793Sthompsa 1302168793Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 1303168793Sthompsa sc->sc_psc = (caddr_t)lp; 1304168793Sthompsa 1305168793Sthompsa return (0); 1306168793Sthompsa} 1307168793Sthompsa 1308168793Sthompsastatic int 1309168793Sthompsalagg_rr_detach(struct lagg_softc *sc) 1310168793Sthompsa{ 1311168793Sthompsa sc->sc_psc = NULL; 1312168793Sthompsa return (0); 1313168793Sthompsa} 1314168793Sthompsa 1315168793Sthompsastatic void 1316168793Sthompsalagg_rr_port_destroy(struct lagg_port *lp) 1317168793Sthompsa{ 1318168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 1319168793Sthompsa 1320168793Sthompsa if (lp == (struct lagg_port *)sc->sc_psc) 1321168793Sthompsa sc->sc_psc = NULL; 1322168793Sthompsa} 1323168793Sthompsa 1324168793Sthompsastatic int 1325168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m) 1326168793Sthompsa{ 1327168793Sthompsa struct lagg_port *lp = (struct lagg_port *)sc->sc_psc, *lp_next; 1328168793Sthompsa int error = 0; 1329168793Sthompsa 1330168793Sthompsa if (lp == NULL && (lp = lagg_link_active(sc, NULL)) == NULL) 1331168793Sthompsa return (ENOENT); 1332168793Sthompsa 1333168793Sthompsa /* Send mbuf */ 1334168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 1335168793Sthompsa 1336168793Sthompsa /* Get next active port */ 1337168793Sthompsa lp_next = lagg_link_active(sc, SLIST_NEXT(lp, lp_entries)); 1338168793Sthompsa sc->sc_psc = (caddr_t)lp_next; 1339168793Sthompsa 1340168793Sthompsa return (error); 1341168793Sthompsa} 1342168793Sthompsa 1343168793Sthompsastatic struct mbuf * 1344168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1345168793Sthompsa{ 1346168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1347168793Sthompsa 1348168793Sthompsa /* Just pass in the packet to our lagg device */ 1349168793Sthompsa m->m_pkthdr.rcvif = ifp; 1350168793Sthompsa 1351168793Sthompsa return (m); 1352168793Sthompsa} 1353168793Sthompsa 1354168793Sthompsa/* 1355168793Sthompsa * Active failover 1356168793Sthompsa */ 1357168793Sthompsa 1358168793Sthompsastatic int 1359168793Sthompsalagg_fail_attach(struct lagg_softc *sc) 1360168793Sthompsa{ 1361168793Sthompsa sc->sc_detach = lagg_fail_detach; 1362168793Sthompsa sc->sc_start = lagg_fail_start; 1363168793Sthompsa sc->sc_input = lagg_fail_input; 1364168793Sthompsa sc->sc_port_create = NULL; 1365168793Sthompsa sc->sc_port_destroy = NULL; 1366168793Sthompsa 1367168793Sthompsa return (0); 1368168793Sthompsa} 1369168793Sthompsa 1370168793Sthompsastatic int 1371168793Sthompsalagg_fail_detach(struct lagg_softc *sc) 1372168793Sthompsa{ 1373168793Sthompsa return (0); 1374168793Sthompsa} 1375168793Sthompsa 1376168793Sthompsastatic int 1377168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m) 1378168793Sthompsa{ 1379168793Sthompsa struct lagg_port *lp; 1380168793Sthompsa 1381168793Sthompsa /* Use the master port if active or the next available port */ 1382168793Sthompsa if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) 1383168793Sthompsa return (ENOENT); 1384168793Sthompsa 1385168793Sthompsa /* Send mbuf */ 1386168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1387168793Sthompsa} 1388168793Sthompsa 1389168793Sthompsastatic struct mbuf * 1390168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1391168793Sthompsa{ 1392168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1393168793Sthompsa struct lagg_port *tmp_tp; 1394168793Sthompsa 1395168793Sthompsa if (lp == sc->sc_primary) { 1396168793Sthompsa m->m_pkthdr.rcvif = ifp; 1397168793Sthompsa return (m); 1398168793Sthompsa } 1399168793Sthompsa 1400168793Sthompsa if (sc->sc_primary->lp_link_state == LINK_STATE_DOWN) { 1401168793Sthompsa tmp_tp = lagg_link_active(sc, NULL); 1402168793Sthompsa /* 1403168793Sthompsa * If tmp_tp is null, we've recieved a packet when all 1404168793Sthompsa * our links are down. Weird, but process it anyways. 1405168793Sthompsa */ 1406168793Sthompsa if ((tmp_tp == NULL || tmp_tp == lp)) { 1407168793Sthompsa m->m_pkthdr.rcvif = ifp; 1408168793Sthompsa return (m); 1409168793Sthompsa } 1410168793Sthompsa } 1411168793Sthompsa 1412168793Sthompsa m_freem(m); 1413168793Sthompsa return (NULL); 1414168793Sthompsa} 1415168793Sthompsa 1416168793Sthompsa/* 1417168793Sthompsa * Loadbalancing 1418168793Sthompsa */ 1419168793Sthompsa 1420168793Sthompsastatic int 1421168793Sthompsalagg_lb_attach(struct lagg_softc *sc) 1422168793Sthompsa{ 1423168793Sthompsa struct lagg_port *lp; 1424168793Sthompsa struct lagg_lb *lb; 1425168793Sthompsa 1426168793Sthompsa if ((lb = (struct lagg_lb *)malloc(sizeof(struct lagg_lb), 1427168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 1428168793Sthompsa return (ENOMEM); 1429168793Sthompsa 1430168793Sthompsa sc->sc_detach = lagg_lb_detach; 1431168793Sthompsa sc->sc_start = lagg_lb_start; 1432168793Sthompsa sc->sc_input = lagg_lb_input; 1433168793Sthompsa sc->sc_port_create = lagg_lb_port_create; 1434168793Sthompsa sc->sc_port_destroy = lagg_lb_port_destroy; 1435168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1436168793Sthompsa 1437168793Sthompsa lb->lb_key = arc4random(); 1438168793Sthompsa sc->sc_psc = (caddr_t)lb; 1439168793Sthompsa 1440168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1441168793Sthompsa lagg_lb_port_create(lp); 1442168793Sthompsa 1443168793Sthompsa return (0); 1444168793Sthompsa} 1445168793Sthompsa 1446168793Sthompsastatic int 1447168793Sthompsalagg_lb_detach(struct lagg_softc *sc) 1448168793Sthompsa{ 1449168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1450168793Sthompsa if (lb != NULL) 1451168793Sthompsa free(lb, M_DEVBUF); 1452168793Sthompsa return (0); 1453168793Sthompsa} 1454168793Sthompsa 1455168793Sthompsastatic int 1456168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp) 1457168793Sthompsa{ 1458168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1459168793Sthompsa struct lagg_port *lp_next; 1460168793Sthompsa int i = 0; 1461168793Sthompsa 1462168793Sthompsa bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 1463168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1464168793Sthompsa if (lp_next == lp) 1465168793Sthompsa continue; 1466168793Sthompsa if (i >= LAGG_MAX_PORTS) 1467168793Sthompsa return (EINVAL); 1468168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1469168793Sthompsa printf("%s: port %s at index %d\n", 1470168793Sthompsa sc->sc_ifname, lp_next->lp_ifname, i); 1471168793Sthompsa lb->lb_ports[i++] = lp_next; 1472168793Sthompsa } 1473168793Sthompsa 1474168793Sthompsa return (0); 1475168793Sthompsa} 1476168793Sthompsa 1477168793Sthompsastatic int 1478168793Sthompsalagg_lb_port_create(struct lagg_port *lp) 1479168793Sthompsa{ 1480168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 1481168793Sthompsa return (lagg_lb_porttable(sc, NULL)); 1482168793Sthompsa} 1483168793Sthompsa 1484168793Sthompsastatic void 1485168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp) 1486168793Sthompsa{ 1487168793Sthompsa struct lagg_softc *sc = lp->lp_lagg; 1488168793Sthompsa lagg_lb_porttable(sc, lp); 1489168793Sthompsa} 1490168793Sthompsa 1491168793Sthompsastatic int 1492168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m) 1493168793Sthompsa{ 1494168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1495168793Sthompsa struct lagg_port *lp = NULL; 1496168793Sthompsa uint32_t p = 0; 1497168793Sthompsa int idx; 1498168793Sthompsa 1499168793Sthompsa p = lagg_hashmbuf(m, lb->lb_key); 1500168793Sthompsa if ((idx = p % sc->sc_count) >= LAGG_MAX_PORTS) 1501168793Sthompsa return (EINVAL); 1502168793Sthompsa lp = lb->lb_ports[idx]; 1503168793Sthompsa 1504168793Sthompsa /* 1505168793Sthompsa * Check the port's link state. This will return the next active 1506168793Sthompsa * port if the link is down or the port is NULL. 1507168793Sthompsa */ 1508168793Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) 1509168793Sthompsa return (ENOENT); 1510168793Sthompsa 1511168793Sthompsa /* Send mbuf */ 1512168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1513168793Sthompsa} 1514168793Sthompsa 1515168793Sthompsastatic struct mbuf * 1516168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1517168793Sthompsa{ 1518168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1519168793Sthompsa 1520168793Sthompsa /* Just pass in the packet to our lagg device */ 1521168793Sthompsa m->m_pkthdr.rcvif = ifp; 1522168793Sthompsa 1523168793Sthompsa return (m); 1524168793Sthompsa} 1525168793Sthompsa 1526168793Sthompsa/* 1527168793Sthompsa * 802.3ad LACP 1528168793Sthompsa */ 1529168793Sthompsa 1530168793Sthompsastatic int 1531168793Sthompsalagg_lacp_attach(struct lagg_softc *sc) 1532168793Sthompsa{ 1533168793Sthompsa struct lagg_port *lp; 1534168793Sthompsa int error; 1535168793Sthompsa 1536168793Sthompsa sc->sc_detach = lagg_lacp_detach; 1537168793Sthompsa sc->sc_port_create = lacp_port_create; 1538168793Sthompsa sc->sc_port_destroy = lacp_port_destroy; 1539168793Sthompsa sc->sc_linkstate = lacp_linkstate; 1540168793Sthompsa sc->sc_start = lagg_lacp_start; 1541168793Sthompsa sc->sc_input = lagg_lacp_input; 1542168793Sthompsa sc->sc_init = lacp_init; 1543168793Sthompsa sc->sc_stop = lacp_stop; 1544168793Sthompsa sc->sc_lladdr = lagg_lacp_lladdr; 1545168793Sthompsa 1546168793Sthompsa error = lacp_attach(sc); 1547168793Sthompsa if (error) 1548168793Sthompsa return (error); 1549168793Sthompsa 1550168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1551168793Sthompsa lacp_port_create(lp); 1552168793Sthompsa 1553168793Sthompsa return (error); 1554168793Sthompsa} 1555168793Sthompsa 1556168793Sthompsastatic int 1557168793Sthompsalagg_lacp_detach(struct lagg_softc *sc) 1558168793Sthompsa{ 1559168793Sthompsa struct lagg_port *lp; 1560168793Sthompsa int error; 1561168793Sthompsa 1562168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1563168793Sthompsa lacp_port_destroy(lp); 1564168793Sthompsa 1565168793Sthompsa /* unlocking is safe here */ 1566169569Sthompsa LAGG_WUNLOCK(sc); 1567168793Sthompsa error = lacp_detach(sc); 1568169569Sthompsa LAGG_WLOCK(sc); 1569168793Sthompsa 1570168793Sthompsa return (error); 1571168793Sthompsa} 1572168793Sthompsa 1573168793Sthompsastatic void 1574168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc) 1575168793Sthompsa{ 1576168793Sthompsa struct lagg_port *lp; 1577168793Sthompsa 1578168793Sthompsa /* purge all the lacp ports */ 1579168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1580168793Sthompsa lacp_port_destroy(lp); 1581168793Sthompsa 1582168793Sthompsa /* add them back in */ 1583168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1584168793Sthompsa lacp_port_create(lp); 1585168793Sthompsa} 1586168793Sthompsa 1587168793Sthompsastatic int 1588168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m) 1589168793Sthompsa{ 1590168793Sthompsa struct lagg_port *lp; 1591168793Sthompsa 1592168793Sthompsa lp = lacp_select_tx_port(sc, m); 1593168793Sthompsa if (lp == NULL) 1594168793Sthompsa return (EBUSY); 1595168793Sthompsa 1596168793Sthompsa /* Send mbuf */ 1597168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1598168793Sthompsa} 1599168793Sthompsa 1600168793Sthompsastatic struct mbuf * 1601168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1602168793Sthompsa{ 1603168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1604168793Sthompsa struct ether_header *eh; 1605168793Sthompsa u_short etype; 1606168793Sthompsa 1607168793Sthompsa eh = mtod(m, struct ether_header *); 1608168793Sthompsa etype = ntohs(eh->ether_type); 1609168793Sthompsa 1610168793Sthompsa /* Tap off LACP control messages */ 1611168793Sthompsa if (etype == ETHERTYPE_SLOW) { 1612169569Sthompsa lacp_input(lp, m); 1613168793Sthompsa return (NULL); 1614168793Sthompsa } 1615168793Sthompsa 1616168793Sthompsa /* 1617168793Sthompsa * If the port is not collecting or not in the active aggregator then 1618168793Sthompsa * free and return. 1619168793Sthompsa */ 1620168793Sthompsa if ((lp->lp_flags & LAGG_PORT_COLLECTING) == 0 || 1621168793Sthompsa lacp_port_isactive(lp) == 0) { 1622168793Sthompsa m_freem(m); 1623168793Sthompsa return (NULL); 1624168793Sthompsa } 1625168793Sthompsa 1626168793Sthompsa m->m_pkthdr.rcvif = ifp; 1627168793Sthompsa return (m); 1628168793Sthompsa} 1629