if_lagg.c revision 186195
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> 5174721Sthompsa * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org> 6168793Sthompsa * 7168793Sthompsa * Permission to use, copy, modify, and distribute this software for any 8168793Sthompsa * purpose with or without fee is hereby granted, provided that the above 9168793Sthompsa * copyright notice and this permission notice appear in all copies. 10168793Sthompsa * 11168793Sthompsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12168793Sthompsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13168793Sthompsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14168793Sthompsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15168793Sthompsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16168793Sthompsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17168793Sthompsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18168793Sthompsa */ 19168793Sthompsa 20168793Sthompsa#include <sys/cdefs.h> 21168793Sthompsa__FBSDID("$FreeBSD: head/sys/net/if_lagg.c 186195 2008-12-16 22:16:34Z thompsa $"); 22168793Sthompsa 23168793Sthompsa#include "opt_inet.h" 24168793Sthompsa#include "opt_inet6.h" 25168793Sthompsa 26168793Sthompsa#include <sys/param.h> 27168793Sthompsa#include <sys/kernel.h> 28168793Sthompsa#include <sys/malloc.h> 29168793Sthompsa#include <sys/mbuf.h> 30168793Sthompsa#include <sys/queue.h> 31168793Sthompsa#include <sys/socket.h> 32168793Sthompsa#include <sys/sockio.h> 33168793Sthompsa#include <sys/sysctl.h> 34168793Sthompsa#include <sys/module.h> 35168793Sthompsa#include <sys/priv.h> 36168793Sthompsa#include <sys/systm.h> 37168793Sthompsa#include <sys/proc.h> 38168793Sthompsa#include <sys/hash.h> 39169569Sthompsa#include <sys/lock.h> 40169569Sthompsa#include <sys/rwlock.h> 41169329Sthompsa#include <sys/taskqueue.h> 42168793Sthompsa 43168793Sthompsa#include <net/ethernet.h> 44168793Sthompsa#include <net/if.h> 45168793Sthompsa#include <net/if_clone.h> 46168793Sthompsa#include <net/if_arp.h> 47168793Sthompsa#include <net/if_dl.h> 48168793Sthompsa#include <net/if_llc.h> 49168793Sthompsa#include <net/if_media.h> 50168793Sthompsa#include <net/if_types.h> 51168793Sthompsa#include <net/if_var.h> 52168793Sthompsa#include <net/bpf.h> 53168793Sthompsa 54168793Sthompsa#ifdef INET 55168793Sthompsa#include <netinet/in.h> 56168793Sthompsa#include <netinet/in_systm.h> 57168793Sthompsa#include <netinet/if_ether.h> 58168793Sthompsa#include <netinet/ip.h> 59168793Sthompsa#endif 60168793Sthompsa 61168793Sthompsa#ifdef INET6 62168793Sthompsa#include <netinet/ip6.h> 63168793Sthompsa#endif 64168793Sthompsa 65168793Sthompsa#include <net/if_vlan_var.h> 66168793Sthompsa#include <net/if_lagg.h> 67168793Sthompsa#include <net/ieee8023ad_lacp.h> 68168793Sthompsa 69168793Sthompsa/* Special flags we should propagate to the lagg ports. */ 70168793Sthompsastatic struct { 71168793Sthompsa int flag; 72168793Sthompsa int (*func)(struct ifnet *, int); 73168793Sthompsa} lagg_pflags[] = { 74168793Sthompsa {IFF_PROMISC, ifpromisc}, 75168793Sthompsa {IFF_ALLMULTI, if_allmulti}, 76168793Sthompsa {0, NULL} 77168793Sthompsa}; 78168793Sthompsa 79168793SthompsaSLIST_HEAD(__trhead, lagg_softc) lagg_list; /* list of laggs */ 80170599Sthompsastatic struct mtx lagg_list_mtx; 81168793Sthompsaeventhandler_tag lagg_detach_cookie = NULL; 82168793Sthompsa 83168793Sthompsastatic int lagg_clone_create(struct if_clone *, int, caddr_t); 84168793Sthompsastatic void lagg_clone_destroy(struct ifnet *); 85168793Sthompsastatic void lagg_lladdr(struct lagg_softc *, uint8_t *); 86171661Sthompsastatic void lagg_capabilities(struct lagg_softc *); 87168793Sthompsastatic void lagg_port_lladdr(struct lagg_port *, uint8_t *); 88169329Sthompsastatic void lagg_port_setlladdr(void *, int); 89168793Sthompsastatic int lagg_port_create(struct lagg_softc *, struct ifnet *); 90168793Sthompsastatic int lagg_port_destroy(struct lagg_port *, int); 91168793Sthompsastatic struct mbuf *lagg_input(struct ifnet *, struct mbuf *); 92173895Sthompsastatic void lagg_linkstate(struct lagg_softc *); 93168793Sthompsastatic void lagg_port_state(struct ifnet *, int); 94168793Sthompsastatic int lagg_port_ioctl(struct ifnet *, u_long, caddr_t); 95168793Sthompsastatic int lagg_port_output(struct ifnet *, struct mbuf *, 96168793Sthompsa struct sockaddr *, struct rtentry *); 97168793Sthompsastatic void lagg_port_ifdetach(void *arg __unused, struct ifnet *); 98168793Sthompsastatic int lagg_port_checkstacking(struct lagg_softc *); 99168793Sthompsastatic void lagg_port2req(struct lagg_port *, struct lagg_reqport *); 100168793Sthompsastatic void lagg_init(void *); 101168793Sthompsastatic void lagg_stop(struct lagg_softc *); 102168793Sthompsastatic int lagg_ioctl(struct ifnet *, u_long, caddr_t); 103168793Sthompsastatic int lagg_ether_setmulti(struct lagg_softc *); 104168793Sthompsastatic int lagg_ether_cmdmulti(struct lagg_port *, int); 105168793Sthompsastatic int lagg_setflag(struct lagg_port *, int, int, 106168793Sthompsa int (*func)(struct ifnet *, int)); 107168793Sthompsastatic int lagg_setflags(struct lagg_port *, int status); 108168793Sthompsastatic void lagg_start(struct ifnet *); 109168793Sthompsastatic int lagg_media_change(struct ifnet *); 110168793Sthompsastatic void lagg_media_status(struct ifnet *, struct ifmediareq *); 111168793Sthompsastatic struct lagg_port *lagg_link_active(struct lagg_softc *, 112168793Sthompsa struct lagg_port *); 113168793Sthompsastatic const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *); 114168793Sthompsa 115168793SthompsaIFC_SIMPLE_DECLARE(lagg, 0); 116168793Sthompsa 117168793Sthompsa/* Simple round robin */ 118168793Sthompsastatic int lagg_rr_attach(struct lagg_softc *); 119168793Sthompsastatic int lagg_rr_detach(struct lagg_softc *); 120168793Sthompsastatic int lagg_rr_start(struct lagg_softc *, struct mbuf *); 121168793Sthompsastatic struct mbuf *lagg_rr_input(struct lagg_softc *, struct lagg_port *, 122168793Sthompsa struct mbuf *); 123168793Sthompsa 124168793Sthompsa/* Active failover */ 125168793Sthompsastatic int lagg_fail_attach(struct lagg_softc *); 126168793Sthompsastatic int lagg_fail_detach(struct lagg_softc *); 127168793Sthompsastatic int lagg_fail_start(struct lagg_softc *, struct mbuf *); 128168793Sthompsastatic struct mbuf *lagg_fail_input(struct lagg_softc *, struct lagg_port *, 129168793Sthompsa struct mbuf *); 130168793Sthompsa 131168793Sthompsa/* Loadbalancing */ 132168793Sthompsastatic int lagg_lb_attach(struct lagg_softc *); 133168793Sthompsastatic int lagg_lb_detach(struct lagg_softc *); 134168793Sthompsastatic int lagg_lb_port_create(struct lagg_port *); 135168793Sthompsastatic void lagg_lb_port_destroy(struct lagg_port *); 136168793Sthompsastatic int lagg_lb_start(struct lagg_softc *, struct mbuf *); 137168793Sthompsastatic struct mbuf *lagg_lb_input(struct lagg_softc *, struct lagg_port *, 138168793Sthompsa struct mbuf *); 139168793Sthompsastatic int lagg_lb_porttable(struct lagg_softc *, struct lagg_port *); 140168793Sthompsa 141168793Sthompsa/* 802.3ad LACP */ 142168793Sthompsastatic int lagg_lacp_attach(struct lagg_softc *); 143168793Sthompsastatic int lagg_lacp_detach(struct lagg_softc *); 144168793Sthompsastatic int lagg_lacp_start(struct lagg_softc *, struct mbuf *); 145168793Sthompsastatic struct mbuf *lagg_lacp_input(struct lagg_softc *, struct lagg_port *, 146168793Sthompsa struct mbuf *); 147168793Sthompsastatic void lagg_lacp_lladdr(struct lagg_softc *); 148168793Sthompsa 149168793Sthompsa/* lagg protocol table */ 150168793Sthompsastatic const struct { 151168793Sthompsa int ti_proto; 152168793Sthompsa int (*ti_attach)(struct lagg_softc *); 153168793Sthompsa} lagg_protos[] = { 154168793Sthompsa { LAGG_PROTO_ROUNDROBIN, lagg_rr_attach }, 155168793Sthompsa { LAGG_PROTO_FAILOVER, lagg_fail_attach }, 156168793Sthompsa { LAGG_PROTO_LOADBALANCE, lagg_lb_attach }, 157168793Sthompsa { LAGG_PROTO_ETHERCHANNEL, lagg_lb_attach }, 158168793Sthompsa { LAGG_PROTO_LACP, lagg_lacp_attach }, 159168793Sthompsa { LAGG_PROTO_NONE, NULL } 160168793Sthompsa}; 161168793Sthompsa 162168793Sthompsastatic int 163168793Sthompsalagg_modevent(module_t mod, int type, void *data) 164168793Sthompsa{ 165168793Sthompsa 166168793Sthompsa switch (type) { 167168793Sthompsa case MOD_LOAD: 168168793Sthompsa mtx_init(&lagg_list_mtx, "if_lagg list", NULL, MTX_DEF); 169168793Sthompsa SLIST_INIT(&lagg_list); 170168793Sthompsa if_clone_attach(&lagg_cloner); 171168793Sthompsa lagg_input_p = lagg_input; 172168793Sthompsa lagg_linkstate_p = lagg_port_state; 173168793Sthompsa lagg_detach_cookie = EVENTHANDLER_REGISTER( 174168793Sthompsa ifnet_departure_event, lagg_port_ifdetach, NULL, 175168793Sthompsa EVENTHANDLER_PRI_ANY); 176168793Sthompsa break; 177168793Sthompsa case MOD_UNLOAD: 178168793Sthompsa EVENTHANDLER_DEREGISTER(ifnet_departure_event, 179168793Sthompsa lagg_detach_cookie); 180168793Sthompsa if_clone_detach(&lagg_cloner); 181168793Sthompsa lagg_input_p = NULL; 182168793Sthompsa lagg_linkstate_p = NULL; 183168793Sthompsa mtx_destroy(&lagg_list_mtx); 184168793Sthompsa break; 185168793Sthompsa default: 186168793Sthompsa return (EOPNOTSUPP); 187168793Sthompsa } 188168793Sthompsa return (0); 189168793Sthompsa} 190168793Sthompsa 191168793Sthompsastatic moduledata_t lagg_mod = { 192168793Sthompsa "if_lagg", 193168793Sthompsa lagg_modevent, 194168793Sthompsa 0 195168793Sthompsa}; 196168793Sthompsa 197168793SthompsaDECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 198168793Sthompsa 199168793Sthompsastatic int 200168793Sthompsalagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) 201168793Sthompsa{ 202168793Sthompsa struct lagg_softc *sc; 203168793Sthompsa struct ifnet *ifp; 204168793Sthompsa int i, error = 0; 205168793Sthompsa static const u_char eaddr[6]; /* 00:00:00:00:00:00 */ 206168793Sthompsa 207168793Sthompsa sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 208168793Sthompsa ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 209168793Sthompsa if (ifp == NULL) { 210168793Sthompsa free(sc, M_DEVBUF); 211168793Sthompsa return (ENOSPC); 212168793Sthompsa } 213168793Sthompsa 214168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 215168793Sthompsa for (i = 0; lagg_protos[i].ti_proto != LAGG_PROTO_NONE; i++) { 216168793Sthompsa if (lagg_protos[i].ti_proto == LAGG_PROTO_DEFAULT) { 217168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 218168793Sthompsa if ((error = lagg_protos[i].ti_attach(sc)) != 0) { 219168793Sthompsa if_free_type(ifp, IFT_ETHER); 220168793Sthompsa free(sc, M_DEVBUF); 221168793Sthompsa return (error); 222168793Sthompsa } 223168793Sthompsa break; 224168793Sthompsa } 225168793Sthompsa } 226168793Sthompsa LAGG_LOCK_INIT(sc); 227168793Sthompsa SLIST_INIT(&sc->sc_ports); 228169329Sthompsa TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc); 229168793Sthompsa 230168793Sthompsa /* Initialise pseudo media types */ 231168793Sthompsa ifmedia_init(&sc->sc_media, 0, lagg_media_change, 232168793Sthompsa lagg_media_status); 233168793Sthompsa ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 234168793Sthompsa ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 235168793Sthompsa 236168793Sthompsa if_initname(ifp, ifc->ifc_name, unit); 237168793Sthompsa ifp->if_type = IFT_ETHER; 238168793Sthompsa ifp->if_softc = sc; 239168793Sthompsa ifp->if_start = lagg_start; 240168793Sthompsa ifp->if_init = lagg_init; 241168793Sthompsa ifp->if_ioctl = lagg_ioctl; 242168793Sthompsa ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 243168793Sthompsa 244168793Sthompsa IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 245168793Sthompsa ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 246168793Sthompsa IFQ_SET_READY(&ifp->if_snd); 247168793Sthompsa 248168793Sthompsa /* 249168793Sthompsa * Attach as an ordinary ethernet device, childs will be attached 250168793Sthompsa * as special device IFT_IEEE8023ADLAG. 251168793Sthompsa */ 252168793Sthompsa ether_ifattach(ifp, eaddr); 253168793Sthompsa 254168793Sthompsa /* Insert into the global list of laggs */ 255168793Sthompsa mtx_lock(&lagg_list_mtx); 256168793Sthompsa SLIST_INSERT_HEAD(&lagg_list, sc, sc_entries); 257168793Sthompsa mtx_unlock(&lagg_list_mtx); 258168793Sthompsa 259168793Sthompsa return (0); 260168793Sthompsa} 261168793Sthompsa 262168793Sthompsastatic void 263168793Sthompsalagg_clone_destroy(struct ifnet *ifp) 264168793Sthompsa{ 265168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 266168793Sthompsa struct lagg_port *lp; 267168793Sthompsa 268169569Sthompsa LAGG_WLOCK(sc); 269168793Sthompsa 270168793Sthompsa lagg_stop(sc); 271168793Sthompsa ifp->if_flags &= ~IFF_UP; 272168793Sthompsa 273168793Sthompsa /* Shutdown and remove lagg ports */ 274168793Sthompsa while ((lp = SLIST_FIRST(&sc->sc_ports)) != NULL) 275168793Sthompsa lagg_port_destroy(lp, 1); 276168793Sthompsa /* Unhook the aggregation protocol */ 277172554Sthompsa (*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 308171661Sthompsastatic void 309168793Sthompsalagg_capabilities(struct lagg_softc *sc) 310168793Sthompsa{ 311168793Sthompsa struct lagg_port *lp; 312171661Sthompsa int cap = ~0, ena = ~0; 313186195Sthompsa u_long hwa = ~0UL; 314168793Sthompsa 315169569Sthompsa LAGG_WLOCK_ASSERT(sc); 316168793Sthompsa 317168793Sthompsa /* Get capabilities from the lagg ports */ 318171661Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 319171661Sthompsa cap &= lp->lp_ifp->if_capabilities; 320171661Sthompsa ena &= lp->lp_ifp->if_capenable; 321186195Sthompsa hwa &= lp->lp_ifp->if_hwassist; 322171661Sthompsa } 323171661Sthompsa cap = (cap == ~0 ? 0 : cap); 324171661Sthompsa ena = (ena == ~0 ? 0 : ena); 325186195Sthompsa hwa = (hwa == ~0 ? 0 : hwa); 326168793Sthompsa 327171661Sthompsa if (sc->sc_ifp->if_capabilities != cap || 328186195Sthompsa sc->sc_ifp->if_capenable != ena || 329186195Sthompsa sc->sc_ifp->if_hwassist != hwa) { 330171661Sthompsa sc->sc_ifp->if_capabilities = cap; 331171661Sthompsa sc->sc_ifp->if_capenable = ena; 332186195Sthompsa sc->sc_ifp->if_hwassist = hwa; 333171661Sthompsa getmicrotime(&sc->sc_ifp->if_lastchange); 334171661Sthompsa 335171661Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 336171661Sthompsa if_printf(sc->sc_ifp, 337171661Sthompsa "capabilities 0x%08x enabled 0x%08x\n", cap, ena); 338168793Sthompsa } 339168793Sthompsa} 340168793Sthompsa 341168793Sthompsastatic void 342168793Sthompsalagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr) 343168793Sthompsa{ 344170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 345168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 346169329Sthompsa struct lagg_llq *llq; 347169329Sthompsa int pending = 0; 348168793Sthompsa 349169569Sthompsa LAGG_WLOCK_ASSERT(sc); 350169329Sthompsa 351169328Sthompsa if (lp->lp_detaching || 352169328Sthompsa memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) 353168793Sthompsa return; 354168793Sthompsa 355169329Sthompsa /* Check to make sure its not already queued to be changed */ 356169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 357169329Sthompsa if (llq->llq_ifp == ifp) { 358169329Sthompsa pending = 1; 359169329Sthompsa break; 360169329Sthompsa } 361169329Sthompsa } 362168793Sthompsa 363169329Sthompsa if (!pending) { 364169329Sthompsa llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT); 365169329Sthompsa if (llq == NULL) /* XXX what to do */ 366169329Sthompsa return; 367169329Sthompsa } 368169329Sthompsa 369169329Sthompsa /* Update the lladdr even if pending, it may have changed */ 370169329Sthompsa llq->llq_ifp = ifp; 371169329Sthompsa bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN); 372169329Sthompsa 373169329Sthompsa if (!pending) 374169329Sthompsa SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries); 375169329Sthompsa 376169329Sthompsa taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task); 377168793Sthompsa} 378168793Sthompsa 379169329Sthompsa/* 380169329Sthompsa * Set the interface MAC address from a taskqueue to avoid a LOR. 381169329Sthompsa */ 382169329Sthompsastatic void 383169329Sthompsalagg_port_setlladdr(void *arg, int pending) 384169329Sthompsa{ 385169329Sthompsa struct lagg_softc *sc = (struct lagg_softc *)arg; 386169329Sthompsa struct lagg_llq *llq, *head; 387169329Sthompsa struct ifnet *ifp; 388169329Sthompsa int error; 389169329Sthompsa 390169329Sthompsa /* Grab a local reference of the queue and remove it from the softc */ 391169569Sthompsa LAGG_WLOCK(sc); 392169329Sthompsa head = SLIST_FIRST(&sc->sc_llq_head); 393169329Sthompsa SLIST_FIRST(&sc->sc_llq_head) = NULL; 394169569Sthompsa LAGG_WUNLOCK(sc); 395169329Sthompsa 396169329Sthompsa /* 397169329Sthompsa * Traverse the queue and set the lladdr on each ifp. It is safe to do 398169329Sthompsa * unlocked as we have the only reference to it. 399169329Sthompsa */ 400169329Sthompsa for (llq = head; llq != NULL; llq = head) { 401169329Sthompsa ifp = llq->llq_ifp; 402169329Sthompsa 403169329Sthompsa /* Set the link layer address */ 404169329Sthompsa error = if_setlladdr(ifp, llq->llq_lladdr, ETHER_ADDR_LEN); 405169329Sthompsa if (error) 406169329Sthompsa printf("%s: setlladdr failed on %s\n", __func__, 407169329Sthompsa ifp->if_xname); 408169329Sthompsa 409169329Sthompsa head = SLIST_NEXT(llq, llq_entries); 410169329Sthompsa free(llq, M_DEVBUF); 411169329Sthompsa } 412169329Sthompsa} 413169329Sthompsa 414168793Sthompsastatic int 415168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) 416168793Sthompsa{ 417168793Sthompsa struct lagg_softc *sc_ptr; 418168793Sthompsa struct lagg_port *lp; 419168793Sthompsa int error = 0; 420168793Sthompsa 421169569Sthompsa LAGG_WLOCK_ASSERT(sc); 422168793Sthompsa 423168793Sthompsa /* Limit the maximal number of lagg ports */ 424168793Sthompsa if (sc->sc_count >= LAGG_MAX_PORTS) 425168793Sthompsa return (ENOSPC); 426168793Sthompsa 427168793Sthompsa /* New lagg port has to be in an idle state */ 428168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 429168793Sthompsa return (EBUSY); 430168793Sthompsa 431168793Sthompsa /* Check if port has already been associated to a lagg */ 432168793Sthompsa if (ifp->if_lagg != NULL) 433168793Sthompsa return (EBUSY); 434168793Sthompsa 435168793Sthompsa /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 436168793Sthompsa if (ifp->if_type != IFT_ETHER) 437168793Sthompsa return (EPROTONOSUPPORT); 438168793Sthompsa 439171661Sthompsa /* Allow the first Ethernet member to define the MTU */ 440171661Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) 441171661Sthompsa sc->sc_ifp->if_mtu = ifp->if_mtu; 442171661Sthompsa else if (sc->sc_ifp->if_mtu != ifp->if_mtu) { 443171661Sthompsa if_printf(sc->sc_ifp, "invalid MTU for %s\n", 444171661Sthompsa ifp->if_xname); 445171661Sthompsa return (EINVAL); 446171661Sthompsa } 447171661Sthompsa 448168793Sthompsa if ((lp = malloc(sizeof(struct lagg_port), 449168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 450168793Sthompsa return (ENOMEM); 451168793Sthompsa 452168793Sthompsa /* Check if port is a stacked lagg */ 453168793Sthompsa mtx_lock(&lagg_list_mtx); 454168793Sthompsa SLIST_FOREACH(sc_ptr, &lagg_list, sc_entries) { 455168793Sthompsa if (ifp == sc_ptr->sc_ifp) { 456168793Sthompsa mtx_unlock(&lagg_list_mtx); 457168793Sthompsa free(lp, M_DEVBUF); 458168793Sthompsa return (EINVAL); 459168793Sthompsa /* XXX disable stacking for the moment, its untested 460168793Sthompsa lp->lp_flags |= LAGG_PORT_STACK; 461168793Sthompsa if (lagg_port_checkstacking(sc_ptr) >= 462168793Sthompsa LAGG_MAX_STACKING) { 463168793Sthompsa mtx_unlock(&lagg_list_mtx); 464168793Sthompsa free(lp, M_DEVBUF); 465168793Sthompsa return (E2BIG); 466168793Sthompsa } 467168793Sthompsa */ 468168793Sthompsa } 469168793Sthompsa } 470168793Sthompsa mtx_unlock(&lagg_list_mtx); 471168793Sthompsa 472168793Sthompsa /* Change the interface type */ 473168793Sthompsa lp->lp_iftype = ifp->if_type; 474168793Sthompsa ifp->if_type = IFT_IEEE8023ADLAG; 475168793Sthompsa ifp->if_lagg = lp; 476168793Sthompsa lp->lp_ioctl = ifp->if_ioctl; 477168793Sthompsa ifp->if_ioctl = lagg_port_ioctl; 478168793Sthompsa lp->lp_output = ifp->if_output; 479168793Sthompsa ifp->if_output = lagg_port_output; 480168793Sthompsa 481168793Sthompsa lp->lp_ifp = ifp; 482170599Sthompsa lp->lp_softc = sc; 483168793Sthompsa 484168793Sthompsa /* Save port link layer address */ 485168793Sthompsa bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN); 486168793Sthompsa 487168793Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) { 488168793Sthompsa sc->sc_primary = lp; 489168793Sthompsa lagg_lladdr(sc, IF_LLADDR(ifp)); 490168793Sthompsa } else { 491168793Sthompsa /* Update link layer address for this port */ 492168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp)); 493168793Sthompsa } 494168793Sthompsa 495168793Sthompsa /* Insert into the list of ports */ 496168793Sthompsa SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries); 497168793Sthompsa sc->sc_count++; 498168793Sthompsa 499168793Sthompsa /* Update lagg capabilities */ 500171661Sthompsa lagg_capabilities(sc); 501173895Sthompsa lagg_linkstate(sc); 502168793Sthompsa 503168793Sthompsa /* Add multicast addresses and interface flags to this port */ 504168793Sthompsa lagg_ether_cmdmulti(lp, 1); 505168793Sthompsa lagg_setflags(lp, 1); 506168793Sthompsa 507168793Sthompsa if (sc->sc_port_create != NULL) 508168793Sthompsa error = (*sc->sc_port_create)(lp); 509168793Sthompsa if (error) { 510170599Sthompsa /* remove the port again, without calling sc_port_destroy */ 511168793Sthompsa lagg_port_destroy(lp, 0); 512168793Sthompsa return (error); 513168793Sthompsa } 514168793Sthompsa 515168793Sthompsa return (error); 516168793Sthompsa} 517168793Sthompsa 518168793Sthompsastatic int 519168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc) 520168793Sthompsa{ 521168793Sthompsa struct lagg_softc *sc_ptr; 522168793Sthompsa struct lagg_port *lp; 523168793Sthompsa int m = 0; 524168793Sthompsa 525169569Sthompsa LAGG_WLOCK_ASSERT(sc); 526168793Sthompsa 527168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 528168793Sthompsa if (lp->lp_flags & LAGG_PORT_STACK) { 529168793Sthompsa sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc; 530168793Sthompsa m = MAX(m, lagg_port_checkstacking(sc_ptr)); 531168793Sthompsa } 532168793Sthompsa } 533168793Sthompsa 534168793Sthompsa return (m + 1); 535168793Sthompsa} 536168793Sthompsa 537168793Sthompsastatic int 538168793Sthompsalagg_port_destroy(struct lagg_port *lp, int runpd) 539168793Sthompsa{ 540170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 541168793Sthompsa struct lagg_port *lp_ptr; 542169329Sthompsa struct lagg_llq *llq; 543168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 544168793Sthompsa 545169569Sthompsa LAGG_WLOCK_ASSERT(sc); 546168793Sthompsa 547168793Sthompsa if (runpd && sc->sc_port_destroy != NULL) 548168793Sthompsa (*sc->sc_port_destroy)(lp); 549168793Sthompsa 550169328Sthompsa /* 551169328Sthompsa * Remove multicast addresses and interface flags from this port and 552169328Sthompsa * reset the MAC address, skip if the interface is being detached. 553169328Sthompsa */ 554169328Sthompsa if (!lp->lp_detaching) { 555169328Sthompsa lagg_ether_cmdmulti(lp, 0); 556169328Sthompsa lagg_setflags(lp, 0); 557169328Sthompsa lagg_port_lladdr(lp, lp->lp_lladdr); 558169328Sthompsa } 559168793Sthompsa 560168793Sthompsa /* Restore interface */ 561168793Sthompsa ifp->if_type = lp->lp_iftype; 562168793Sthompsa ifp->if_ioctl = lp->lp_ioctl; 563168793Sthompsa ifp->if_output = lp->lp_output; 564168793Sthompsa ifp->if_lagg = NULL; 565168793Sthompsa 566168793Sthompsa /* Finally, remove the port from the lagg */ 567168793Sthompsa SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries); 568168793Sthompsa sc->sc_count--; 569168793Sthompsa 570168793Sthompsa /* Update the primary interface */ 571168793Sthompsa if (lp == sc->sc_primary) { 572168793Sthompsa uint8_t lladdr[ETHER_ADDR_LEN]; 573168793Sthompsa 574168793Sthompsa if ((lp_ptr = SLIST_FIRST(&sc->sc_ports)) == NULL) { 575168793Sthompsa bzero(&lladdr, ETHER_ADDR_LEN); 576168793Sthompsa } else { 577168793Sthompsa bcopy(lp_ptr->lp_lladdr, 578168793Sthompsa lladdr, ETHER_ADDR_LEN); 579168793Sthompsa } 580168793Sthompsa lagg_lladdr(sc, lladdr); 581168793Sthompsa sc->sc_primary = lp_ptr; 582168793Sthompsa 583168793Sthompsa /* Update link layer address for each port */ 584168793Sthompsa SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries) 585168793Sthompsa lagg_port_lladdr(lp_ptr, lladdr); 586168793Sthompsa } 587168793Sthompsa 588169329Sthompsa /* Remove any pending lladdr changes from the queue */ 589169329Sthompsa if (lp->lp_detaching) { 590169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 591169329Sthompsa if (llq->llq_ifp == ifp) { 592169329Sthompsa SLIST_REMOVE(&sc->sc_llq_head, llq, lagg_llq, 593169329Sthompsa llq_entries); 594169329Sthompsa free(llq, M_DEVBUF); 595169329Sthompsa break; /* Only appears once */ 596169329Sthompsa } 597169329Sthompsa } 598169329Sthompsa } 599169329Sthompsa 600168793Sthompsa if (lp->lp_ifflags) 601168793Sthompsa if_printf(ifp, "%s: lp_ifflags unclean\n", __func__); 602168793Sthompsa 603168793Sthompsa free(lp, M_DEVBUF); 604168793Sthompsa 605168793Sthompsa /* Update lagg capabilities */ 606171661Sthompsa lagg_capabilities(sc); 607173895Sthompsa lagg_linkstate(sc); 608168793Sthompsa 609168793Sthompsa return (0); 610168793Sthompsa} 611168793Sthompsa 612168793Sthompsastatic int 613168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 614168793Sthompsa{ 615168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data; 616168793Sthompsa struct lagg_softc *sc; 617168793Sthompsa struct lagg_port *lp = NULL; 618168793Sthompsa int error = 0; 619168793Sthompsa 620168793Sthompsa /* Should be checked by the caller */ 621168793Sthompsa if (ifp->if_type != IFT_IEEE8023ADLAG || 622170599Sthompsa (lp = ifp->if_lagg) == NULL || (sc = lp->lp_softc) == NULL) 623168793Sthompsa goto fallback; 624168793Sthompsa 625168793Sthompsa switch (cmd) { 626168793Sthompsa case SIOCGLAGGPORT: 627168793Sthompsa if (rp->rp_portname[0] == '\0' || 628168793Sthompsa ifunit(rp->rp_portname) != ifp) { 629168793Sthompsa error = EINVAL; 630168793Sthompsa break; 631168793Sthompsa } 632168793Sthompsa 633171603Sthompsa LAGG_RLOCK(sc); 634171603Sthompsa if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) { 635168793Sthompsa error = ENOENT; 636171603Sthompsa LAGG_RUNLOCK(sc); 637168793Sthompsa break; 638168793Sthompsa } 639168793Sthompsa 640168793Sthompsa lagg_port2req(lp, rp); 641169569Sthompsa LAGG_RUNLOCK(sc); 642168793Sthompsa break; 643171661Sthompsa 644171661Sthompsa case SIOCSIFCAP: 645171661Sthompsa if (lp->lp_ioctl == NULL) { 646171661Sthompsa error = EINVAL; 647171661Sthompsa break; 648171661Sthompsa } 649171661Sthompsa error = (*lp->lp_ioctl)(ifp, cmd, data); 650171661Sthompsa if (error) 651171661Sthompsa break; 652171661Sthompsa 653171661Sthompsa /* Update lagg interface capabilities */ 654171661Sthompsa LAGG_WLOCK(sc); 655171661Sthompsa lagg_capabilities(sc); 656171661Sthompsa LAGG_WUNLOCK(sc); 657171661Sthompsa break; 658171661Sthompsa 659171661Sthompsa case SIOCSIFMTU: 660171661Sthompsa /* Do not allow the MTU to be changed once joined */ 661171661Sthompsa error = EINVAL; 662171661Sthompsa break; 663171661Sthompsa 664168793Sthompsa default: 665168793Sthompsa goto fallback; 666168793Sthompsa } 667168793Sthompsa 668168793Sthompsa return (error); 669168793Sthompsa 670168793Sthompsafallback: 671169340Sthompsa if (lp->lp_ioctl != NULL) 672168793Sthompsa return ((*lp->lp_ioctl)(ifp, cmd, data)); 673168793Sthompsa 674168793Sthompsa return (EINVAL); 675168793Sthompsa} 676168793Sthompsa 677168793Sthompsastatic int 678168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m, 679168793Sthompsa struct sockaddr *dst, struct rtentry *rt0) 680168793Sthompsa{ 681168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 682168793Sthompsa struct ether_header *eh; 683168793Sthompsa short type = 0; 684168793Sthompsa 685168793Sthompsa switch (dst->sa_family) { 686168793Sthompsa case pseudo_AF_HDRCMPLT: 687168793Sthompsa case AF_UNSPEC: 688168793Sthompsa eh = (struct ether_header *)dst->sa_data; 689168793Sthompsa type = eh->ether_type; 690168793Sthompsa break; 691168793Sthompsa } 692168793Sthompsa 693168793Sthompsa /* 694168793Sthompsa * Only allow ethernet types required to initiate or maintain the link, 695168793Sthompsa * aggregated frames take a different path. 696168793Sthompsa */ 697168793Sthompsa switch (ntohs(type)) { 698168793Sthompsa case ETHERTYPE_PAE: /* EAPOL PAE/802.1x */ 699168793Sthompsa return ((*lp->lp_output)(ifp, m, dst, rt0)); 700168793Sthompsa } 701168793Sthompsa 702168793Sthompsa /* drop any other frames */ 703168793Sthompsa m_freem(m); 704168793Sthompsa return (EBUSY); 705168793Sthompsa} 706168793Sthompsa 707168793Sthompsastatic void 708168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) 709168793Sthompsa{ 710168793Sthompsa struct lagg_port *lp; 711168793Sthompsa struct lagg_softc *sc; 712168793Sthompsa 713168793Sthompsa if ((lp = ifp->if_lagg) == NULL) 714168793Sthompsa return; 715168793Sthompsa 716170599Sthompsa sc = lp->lp_softc; 717168793Sthompsa 718169569Sthompsa LAGG_WLOCK(sc); 719169328Sthompsa lp->lp_detaching = 1; 720168793Sthompsa lagg_port_destroy(lp, 1); 721169569Sthompsa LAGG_WUNLOCK(sc); 722168793Sthompsa} 723168793Sthompsa 724168793Sthompsastatic void 725168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp) 726168793Sthompsa{ 727170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 728172020Sthompsa 729168793Sthompsa strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname)); 730168793Sthompsa strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname)); 731168793Sthompsa rp->rp_prio = lp->lp_prio; 732168793Sthompsa rp->rp_flags = lp->lp_flags; 733171247Sthompsa if (sc->sc_portreq != NULL) 734171247Sthompsa (*sc->sc_portreq)(lp, (caddr_t)&rp->rp_psc); 735168793Sthompsa 736168793Sthompsa /* Add protocol specific flags */ 737168793Sthompsa switch (sc->sc_proto) { 738168793Sthompsa case LAGG_PROTO_FAILOVER: 739168793Sthompsa if (lp == sc->sc_primary) 740169204Sthompsa rp->rp_flags |= LAGG_PORT_MASTER; 741172020Sthompsa if (lp == lagg_link_active(sc, sc->sc_primary)) 742172020Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 743172020Sthompsa break; 744172020Sthompsa 745168793Sthompsa case LAGG_PROTO_ROUNDROBIN: 746168793Sthompsa case LAGG_PROTO_LOADBALANCE: 747168793Sthompsa case LAGG_PROTO_ETHERCHANNEL: 748168793Sthompsa if (LAGG_PORTACTIVE(lp)) 749168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 750168793Sthompsa break; 751168793Sthompsa 752168793Sthompsa case LAGG_PROTO_LACP: 753168793Sthompsa /* LACP has a different definition of active */ 754177274Sthompsa if (lacp_isactive(lp)) 755168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 756177274Sthompsa if (lacp_iscollecting(lp)) 757177274Sthompsa rp->rp_flags |= LAGG_PORT_COLLECTING; 758177274Sthompsa if (lacp_isdistributing(lp)) 759177274Sthompsa rp->rp_flags |= LAGG_PORT_DISTRIBUTING; 760168793Sthompsa break; 761168793Sthompsa } 762168793Sthompsa 763168793Sthompsa} 764168793Sthompsa 765168793Sthompsastatic void 766168793Sthompsalagg_init(void *xsc) 767168793Sthompsa{ 768168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)xsc; 769168793Sthompsa struct lagg_port *lp; 770168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 771168793Sthompsa 772168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 773168793Sthompsa return; 774168793Sthompsa 775169569Sthompsa LAGG_WLOCK(sc); 776168793Sthompsa 777168793Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 778168793Sthompsa /* Update the port lladdrs */ 779168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 780168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(ifp)); 781168793Sthompsa 782168793Sthompsa if (sc->sc_init != NULL) 783168793Sthompsa (*sc->sc_init)(sc); 784168793Sthompsa 785169569Sthompsa LAGG_WUNLOCK(sc); 786168793Sthompsa} 787168793Sthompsa 788168793Sthompsastatic void 789168793Sthompsalagg_stop(struct lagg_softc *sc) 790168793Sthompsa{ 791168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 792168793Sthompsa 793169569Sthompsa LAGG_WLOCK_ASSERT(sc); 794168793Sthompsa 795168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 796168793Sthompsa return; 797168793Sthompsa 798168793Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 799168793Sthompsa 800168793Sthompsa if (sc->sc_stop != NULL) 801168793Sthompsa (*sc->sc_stop)(sc); 802168793Sthompsa} 803168793Sthompsa 804168793Sthompsastatic int 805168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 806168793Sthompsa{ 807168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 808168793Sthompsa struct lagg_reqall *ra = (struct lagg_reqall *)data; 809168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf; 810168793Sthompsa struct ifreq *ifr = (struct ifreq *)data; 811168793Sthompsa struct lagg_port *lp; 812168793Sthompsa struct ifnet *tpif; 813168793Sthompsa struct thread *td = curthread; 814171603Sthompsa char *buf, *outbuf; 815171603Sthompsa int count, buflen, len, error = 0; 816168793Sthompsa 817168793Sthompsa bzero(&rpbuf, sizeof(rpbuf)); 818168793Sthompsa 819168793Sthompsa switch (cmd) { 820168793Sthompsa case SIOCGLAGG: 821171603Sthompsa LAGG_RLOCK(sc); 822171603Sthompsa count = 0; 823171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 824171603Sthompsa count++; 825171603Sthompsa buflen = count * sizeof(struct lagg_reqport); 826171603Sthompsa LAGG_RUNLOCK(sc); 827171603Sthompsa 828171603Sthompsa outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); 829171603Sthompsa 830171603Sthompsa LAGG_RLOCK(sc); 831168793Sthompsa ra->ra_proto = sc->sc_proto; 832171247Sthompsa if (sc->sc_req != NULL) 833171247Sthompsa (*sc->sc_req)(sc, (caddr_t)&ra->ra_psc); 834171603Sthompsa 835171603Sthompsa count = 0; 836171603Sthompsa buf = outbuf; 837171603Sthompsa len = min(ra->ra_size, buflen); 838171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 839171603Sthompsa if (len < sizeof(rpbuf)) 840171603Sthompsa break; 841171603Sthompsa 842168793Sthompsa lagg_port2req(lp, &rpbuf); 843171603Sthompsa memcpy(buf, &rpbuf, sizeof(rpbuf)); 844171603Sthompsa count++; 845171603Sthompsa buf += sizeof(rpbuf); 846171603Sthompsa len -= sizeof(rpbuf); 847168793Sthompsa } 848171603Sthompsa LAGG_RUNLOCK(sc); 849171603Sthompsa ra->ra_ports = count; 850171603Sthompsa ra->ra_size = count * sizeof(rpbuf); 851171603Sthompsa error = copyout(outbuf, ra->ra_port, ra->ra_size); 852171603Sthompsa free(outbuf, M_TEMP); 853168793Sthompsa break; 854168793Sthompsa case SIOCSLAGG: 855168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 856168793Sthompsa if (error) 857168793Sthompsa break; 858168793Sthompsa if (ra->ra_proto >= LAGG_PROTO_MAX) { 859168793Sthompsa error = EPROTONOSUPPORT; 860168793Sthompsa break; 861168793Sthompsa } 862168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) { 863171603Sthompsa LAGG_WLOCK(sc); 864168793Sthompsa error = sc->sc_detach(sc); 865168793Sthompsa /* Reset protocol and pointers */ 866168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 867168793Sthompsa sc->sc_detach = NULL; 868168793Sthompsa sc->sc_start = NULL; 869168793Sthompsa sc->sc_input = NULL; 870168793Sthompsa sc->sc_port_create = NULL; 871168793Sthompsa sc->sc_port_destroy = NULL; 872168793Sthompsa sc->sc_linkstate = NULL; 873168793Sthompsa sc->sc_init = NULL; 874168793Sthompsa sc->sc_stop = NULL; 875168793Sthompsa sc->sc_lladdr = NULL; 876171247Sthompsa sc->sc_req = NULL; 877171247Sthompsa sc->sc_portreq = NULL; 878171603Sthompsa LAGG_WUNLOCK(sc); 879168793Sthompsa } 880168793Sthompsa if (error != 0) 881168793Sthompsa break; 882171603Sthompsa for (int i = 0; i < (sizeof(lagg_protos) / 883168793Sthompsa sizeof(lagg_protos[0])); i++) { 884168793Sthompsa if (lagg_protos[i].ti_proto == ra->ra_proto) { 885168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 886168793Sthompsa printf("%s: using proto %u\n", 887168793Sthompsa sc->sc_ifname, 888168793Sthompsa lagg_protos[i].ti_proto); 889171603Sthompsa LAGG_WLOCK(sc); 890168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 891168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) 892168793Sthompsa error = lagg_protos[i].ti_attach(sc); 893171603Sthompsa LAGG_WUNLOCK(sc); 894171603Sthompsa return (error); 895168793Sthompsa } 896168793Sthompsa } 897168793Sthompsa error = EPROTONOSUPPORT; 898168793Sthompsa break; 899168793Sthompsa case SIOCGLAGGPORT: 900168793Sthompsa if (rp->rp_portname[0] == '\0' || 901168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 902168793Sthompsa error = EINVAL; 903168793Sthompsa break; 904168793Sthompsa } 905168793Sthompsa 906171603Sthompsa LAGG_RLOCK(sc); 907168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 908170599Sthompsa lp->lp_softc != sc) { 909168793Sthompsa error = ENOENT; 910171603Sthompsa LAGG_RUNLOCK(sc); 911168793Sthompsa break; 912168793Sthompsa } 913168793Sthompsa 914168793Sthompsa lagg_port2req(lp, rp); 915171603Sthompsa LAGG_RUNLOCK(sc); 916168793Sthompsa break; 917168793Sthompsa case SIOCSLAGGPORT: 918168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 919168793Sthompsa if (error) 920168793Sthompsa break; 921168793Sthompsa if (rp->rp_portname[0] == '\0' || 922168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 923168793Sthompsa error = EINVAL; 924168793Sthompsa break; 925168793Sthompsa } 926171603Sthompsa LAGG_WLOCK(sc); 927168793Sthompsa error = lagg_port_create(sc, tpif); 928171603Sthompsa LAGG_WUNLOCK(sc); 929168793Sthompsa break; 930168793Sthompsa case SIOCSLAGGDELPORT: 931168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 932168793Sthompsa if (error) 933168793Sthompsa break; 934168793Sthompsa if (rp->rp_portname[0] == '\0' || 935168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 936168793Sthompsa error = EINVAL; 937168793Sthompsa break; 938168793Sthompsa } 939168793Sthompsa 940171603Sthompsa LAGG_WLOCK(sc); 941168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 942170599Sthompsa lp->lp_softc != sc) { 943168793Sthompsa error = ENOENT; 944171603Sthompsa LAGG_WUNLOCK(sc); 945168793Sthompsa break; 946168793Sthompsa } 947168793Sthompsa 948168793Sthompsa error = lagg_port_destroy(lp, 1); 949171603Sthompsa LAGG_WUNLOCK(sc); 950168793Sthompsa break; 951168793Sthompsa case SIOCSIFFLAGS: 952168793Sthompsa /* Set flags on ports too */ 953171603Sthompsa LAGG_WLOCK(sc); 954168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 955168793Sthompsa lagg_setflags(lp, 1); 956168793Sthompsa } 957171603Sthompsa LAGG_WUNLOCK(sc); 958168793Sthompsa 959168793Sthompsa if (!(ifp->if_flags & IFF_UP) && 960168793Sthompsa (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 961168793Sthompsa /* 962168793Sthompsa * If interface is marked down and it is running, 963168793Sthompsa * then stop and disable it. 964168793Sthompsa */ 965171603Sthompsa LAGG_WLOCK(sc); 966168793Sthompsa lagg_stop(sc); 967171603Sthompsa LAGG_WUNLOCK(sc); 968168793Sthompsa } else if ((ifp->if_flags & IFF_UP) && 969168793Sthompsa !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 970168793Sthompsa /* 971168793Sthompsa * If interface is marked up and it is stopped, then 972168793Sthompsa * start it. 973168793Sthompsa */ 974168793Sthompsa (*ifp->if_init)(sc); 975168793Sthompsa } 976168793Sthompsa break; 977168793Sthompsa case SIOCADDMULTI: 978168793Sthompsa case SIOCDELMULTI: 979171603Sthompsa LAGG_WLOCK(sc); 980168793Sthompsa error = lagg_ether_setmulti(sc); 981171603Sthompsa LAGG_WUNLOCK(sc); 982168793Sthompsa break; 983168793Sthompsa case SIOCSIFMEDIA: 984168793Sthompsa case SIOCGIFMEDIA: 985168793Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 986168793Sthompsa break; 987171661Sthompsa 988171661Sthompsa case SIOCSIFCAP: 989171661Sthompsa case SIOCSIFMTU: 990171661Sthompsa /* Do not allow the MTU or caps to be directly changed */ 991171661Sthompsa error = EINVAL; 992171661Sthompsa break; 993171661Sthompsa 994168793Sthompsa default: 995168793Sthompsa error = ether_ioctl(ifp, cmd, data); 996168793Sthompsa break; 997168793Sthompsa } 998168793Sthompsa return (error); 999168793Sthompsa} 1000168793Sthompsa 1001168793Sthompsastatic int 1002168793Sthompsalagg_ether_setmulti(struct lagg_softc *sc) 1003168793Sthompsa{ 1004169327Sthompsa struct lagg_port *lp; 1005168793Sthompsa 1006169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1007168793Sthompsa 1008169327Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1009169340Sthompsa /* First, remove any existing filter entries. */ 1010169340Sthompsa lagg_ether_cmdmulti(lp, 0); 1011169340Sthompsa /* copy all addresses from the lagg interface to the port */ 1012169327Sthompsa lagg_ether_cmdmulti(lp, 1); 1013169340Sthompsa } 1014168793Sthompsa return (0); 1015168793Sthompsa} 1016168793Sthompsa 1017168793Sthompsastatic int 1018168793Sthompsalagg_ether_cmdmulti(struct lagg_port *lp, int set) 1019168793Sthompsa{ 1020170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1021169327Sthompsa struct ifnet *ifp = lp->lp_ifp; 1022170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1023169327Sthompsa struct lagg_mc *mc; 1024169327Sthompsa struct ifmultiaddr *ifma, *rifma = NULL; 1025169327Sthompsa struct sockaddr_dl sdl; 1026169327Sthompsa int error; 1027168793Sthompsa 1028169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1029168793Sthompsa 1030168793Sthompsa bzero((char *)&sdl, sizeof(sdl)); 1031168793Sthompsa sdl.sdl_len = sizeof(sdl); 1032168793Sthompsa sdl.sdl_family = AF_LINK; 1033168793Sthompsa sdl.sdl_type = IFT_ETHER; 1034168793Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 1035168793Sthompsa sdl.sdl_index = ifp->if_index; 1036168793Sthompsa 1037169327Sthompsa if (set) { 1038170599Sthompsa TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) { 1039169327Sthompsa if (ifma->ifma_addr->sa_family != AF_LINK) 1040169327Sthompsa continue; 1041169327Sthompsa bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 1042169327Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 1043168793Sthompsa 1044168793Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 1045169327Sthompsa if (error) 1046169327Sthompsa return (error); 1047169327Sthompsa mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); 1048169327Sthompsa if (mc == NULL) 1049169327Sthompsa return (ENOMEM); 1050169327Sthompsa mc->mc_ifma = rifma; 1051169327Sthompsa SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); 1052168793Sthompsa } 1053169327Sthompsa } else { 1054169327Sthompsa while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { 1055169327Sthompsa SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); 1056169327Sthompsa if_delmulti_ifma(mc->mc_ifma); 1057169327Sthompsa free(mc, M_DEVBUF); 1058169327Sthompsa } 1059168793Sthompsa } 1060168793Sthompsa return (0); 1061168793Sthompsa} 1062168793Sthompsa 1063168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */ 1064168793Sthompsastatic int 1065168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status, 1066168793Sthompsa int (*func)(struct ifnet *, int)) 1067168793Sthompsa{ 1068170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1069170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1070168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 1071168793Sthompsa int error; 1072168793Sthompsa 1073169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1074168793Sthompsa 1075170599Sthompsa status = status ? (scifp->if_flags & flag) : 0; 1076168793Sthompsa /* Now "status" contains the flag value or 0 */ 1077168793Sthompsa 1078168793Sthompsa /* 1079168793Sthompsa * See if recorded ports status is different from what 1080168793Sthompsa * we want it to be. If it is, flip it. We record ports 1081168793Sthompsa * status in lp_ifflags so that we won't clear ports flag 1082168793Sthompsa * we haven't set. In fact, we don't clear or set ports 1083168793Sthompsa * flags directly, but get or release references to them. 1084168793Sthompsa * That's why we can be sure that recorded flags still are 1085168793Sthompsa * in accord with actual ports flags. 1086168793Sthompsa */ 1087168793Sthompsa if (status != (lp->lp_ifflags & flag)) { 1088168793Sthompsa error = (*func)(ifp, status); 1089168793Sthompsa if (error) 1090168793Sthompsa return (error); 1091168793Sthompsa lp->lp_ifflags &= ~flag; 1092168793Sthompsa lp->lp_ifflags |= status; 1093168793Sthompsa } 1094168793Sthompsa return (0); 1095168793Sthompsa} 1096168793Sthompsa 1097168793Sthompsa/* 1098168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port 1099168793Sthompsa * if "status" is true, update ports flags respective to the lagg 1100168793Sthompsa * if "status" is false, forcedly clear the flags set on port. 1101168793Sthompsa */ 1102168793Sthompsastatic int 1103168793Sthompsalagg_setflags(struct lagg_port *lp, int status) 1104168793Sthompsa{ 1105168793Sthompsa int error, i; 1106170599Sthompsa 1107168793Sthompsa for (i = 0; lagg_pflags[i].flag; i++) { 1108168793Sthompsa error = lagg_setflag(lp, lagg_pflags[i].flag, 1109168793Sthompsa status, lagg_pflags[i].func); 1110168793Sthompsa if (error) 1111168793Sthompsa return (error); 1112168793Sthompsa } 1113168793Sthompsa return (0); 1114168793Sthompsa} 1115168793Sthompsa 1116168793Sthompsastatic void 1117168793Sthompsalagg_start(struct ifnet *ifp) 1118168793Sthompsa{ 1119168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1120168793Sthompsa struct mbuf *m; 1121168793Sthompsa int error = 0; 1122168793Sthompsa 1123169569Sthompsa LAGG_RLOCK(sc); 1124183160Sthompsa /* We need a Tx algorithm and at least one port */ 1125183160Sthompsa if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) { 1126183160Sthompsa IF_DRAIN(&ifp->if_snd); 1127183160Sthompsa LAGG_RUNLOCK(sc); 1128183160Sthompsa return; 1129183160Sthompsa } 1130183160Sthompsa 1131168793Sthompsa for (;; error = 0) { 1132168793Sthompsa IFQ_DEQUEUE(&ifp->if_snd, m); 1133168793Sthompsa if (m == NULL) 1134168793Sthompsa break; 1135168793Sthompsa 1136172825Sthompsa ETHER_BPF_MTAP(ifp, m); 1137168793Sthompsa 1138183160Sthompsa error = (*sc->sc_start)(sc, m); 1139168793Sthompsa if (error == 0) 1140168793Sthompsa ifp->if_opackets++; 1141172554Sthompsa else 1142168793Sthompsa ifp->if_oerrors++; 1143168793Sthompsa } 1144169569Sthompsa LAGG_RUNLOCK(sc); 1145168793Sthompsa} 1146168793Sthompsa 1147168793Sthompsastatic struct mbuf * 1148168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m) 1149168793Sthompsa{ 1150168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1151170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1152170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1153168793Sthompsa 1154170599Sthompsa if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 1155169227Sthompsa (lp->lp_flags & LAGG_PORT_DISABLED) || 1156168793Sthompsa sc->sc_proto == LAGG_PROTO_NONE) { 1157168793Sthompsa m_freem(m); 1158168793Sthompsa return (NULL); 1159168793Sthompsa } 1160168793Sthompsa 1161169569Sthompsa LAGG_RLOCK(sc); 1162172825Sthompsa ETHER_BPF_MTAP(scifp, m); 1163168793Sthompsa 1164168793Sthompsa m = (*sc->sc_input)(sc, lp, m); 1165168793Sthompsa 1166168793Sthompsa if (m != NULL) { 1167170599Sthompsa scifp->if_ipackets++; 1168170599Sthompsa scifp->if_ibytes += m->m_pkthdr.len; 1169174278Sthompsa 1170174278Sthompsa if (scifp->if_flags & IFF_MONITOR) { 1171174278Sthompsa m_freem(m); 1172174278Sthompsa m = NULL; 1173174278Sthompsa } 1174168793Sthompsa } 1175168793Sthompsa 1176169569Sthompsa LAGG_RUNLOCK(sc); 1177168793Sthompsa return (m); 1178168793Sthompsa} 1179168793Sthompsa 1180168793Sthompsastatic int 1181168793Sthompsalagg_media_change(struct ifnet *ifp) 1182168793Sthompsa{ 1183168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1184168793Sthompsa 1185168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1186168793Sthompsa printf("%s\n", __func__); 1187168793Sthompsa 1188168793Sthompsa /* Ignore */ 1189168793Sthompsa return (0); 1190168793Sthompsa} 1191168793Sthompsa 1192168793Sthompsastatic void 1193168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1194168793Sthompsa{ 1195168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1196168793Sthompsa struct lagg_port *lp; 1197168793Sthompsa 1198168793Sthompsa imr->ifm_status = IFM_AVALID; 1199168793Sthompsa imr->ifm_active = IFM_ETHER | IFM_AUTO; 1200168793Sthompsa 1201169569Sthompsa LAGG_RLOCK(sc); 1202169340Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1203169340Sthompsa if (LAGG_PORTACTIVE(lp)) 1204169340Sthompsa imr->ifm_status |= IFM_ACTIVE; 1205169340Sthompsa } 1206169569Sthompsa LAGG_RUNLOCK(sc); 1207168793Sthompsa} 1208168793Sthompsa 1209168793Sthompsastatic void 1210173895Sthompsalagg_linkstate(struct lagg_softc *sc) 1211173895Sthompsa{ 1212173895Sthompsa struct lagg_port *lp; 1213173895Sthompsa int new_link = LINK_STATE_DOWN; 1214173895Sthompsa 1215173895Sthompsa /* Our link is considered up if at least one of our ports is active */ 1216173895Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1217173895Sthompsa if (lp->lp_link_state == LINK_STATE_UP) { 1218173895Sthompsa new_link = LINK_STATE_UP; 1219173895Sthompsa break; 1220173895Sthompsa } 1221173895Sthompsa } 1222173895Sthompsa if_link_state_change(sc->sc_ifp, new_link); 1223173895Sthompsa} 1224173895Sthompsa 1225173895Sthompsastatic void 1226168793Sthompsalagg_port_state(struct ifnet *ifp, int state) 1227168793Sthompsa{ 1228168793Sthompsa struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; 1229168793Sthompsa struct lagg_softc *sc = NULL; 1230168793Sthompsa 1231168793Sthompsa if (lp != NULL) 1232170599Sthompsa sc = lp->lp_softc; 1233168793Sthompsa if (sc == NULL) 1234168793Sthompsa return; 1235168793Sthompsa 1236169569Sthompsa LAGG_WLOCK(sc); 1237173895Sthompsa lagg_linkstate(sc); 1238168793Sthompsa if (sc->sc_linkstate != NULL) 1239168793Sthompsa (*sc->sc_linkstate)(lp); 1240169569Sthompsa LAGG_WUNLOCK(sc); 1241168793Sthompsa} 1242168793Sthompsa 1243168793Sthompsastruct lagg_port * 1244168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) 1245168793Sthompsa{ 1246168793Sthompsa struct lagg_port *lp_next, *rval = NULL; 1247168793Sthompsa // int new_link = LINK_STATE_DOWN; 1248168793Sthompsa 1249169688Sthompsa LAGG_RLOCK_ASSERT(sc); 1250168793Sthompsa /* 1251168793Sthompsa * Search a port which reports an active link state. 1252168793Sthompsa */ 1253168793Sthompsa 1254168793Sthompsa if (lp == NULL) 1255168793Sthompsa goto search; 1256168793Sthompsa if (LAGG_PORTACTIVE(lp)) { 1257168793Sthompsa rval = lp; 1258168793Sthompsa goto found; 1259168793Sthompsa } 1260168793Sthompsa if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL && 1261168793Sthompsa LAGG_PORTACTIVE(lp_next)) { 1262168793Sthompsa rval = lp_next; 1263168793Sthompsa goto found; 1264168793Sthompsa } 1265168793Sthompsa 1266168793Sthompsasearch: 1267168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1268168793Sthompsa if (LAGG_PORTACTIVE(lp_next)) { 1269168793Sthompsa rval = lp_next; 1270168793Sthompsa goto found; 1271168793Sthompsa } 1272168793Sthompsa } 1273168793Sthompsa 1274168793Sthompsafound: 1275168793Sthompsa if (rval != NULL) { 1276168793Sthompsa /* 1277168793Sthompsa * The IEEE 802.1D standard assumes that a lagg with 1278168793Sthompsa * multiple ports is always full duplex. This is valid 1279168793Sthompsa * for load sharing laggs and if at least two links 1280168793Sthompsa * are active. Unfortunately, checking the latter would 1281168793Sthompsa * be too expensive at this point. 1282168793Sthompsa XXX 1283168793Sthompsa if ((sc->sc_capabilities & IFCAP_LAGG_FULLDUPLEX) && 1284168793Sthompsa (sc->sc_count > 1)) 1285168793Sthompsa new_link = LINK_STATE_FULL_DUPLEX; 1286168793Sthompsa else 1287168793Sthompsa new_link = rval->lp_link_state; 1288168793Sthompsa */ 1289168793Sthompsa } 1290168793Sthompsa 1291168793Sthompsa return (rval); 1292168793Sthompsa} 1293168793Sthompsa 1294168793Sthompsastatic const void * 1295168793Sthompsalagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf) 1296168793Sthompsa{ 1297168793Sthompsa if (m->m_pkthdr.len < (off + len)) { 1298168793Sthompsa return (NULL); 1299168793Sthompsa } else if (m->m_len < (off + len)) { 1300168793Sthompsa m_copydata(m, off, len, buf); 1301168793Sthompsa return (buf); 1302168793Sthompsa } 1303168793Sthompsa return (mtod(m, char *) + off); 1304168793Sthompsa} 1305168793Sthompsa 1306168793Sthompsauint32_t 1307168793Sthompsalagg_hashmbuf(struct mbuf *m, uint32_t key) 1308168793Sthompsa{ 1309168793Sthompsa uint16_t etype; 1310169583Sthompsa uint32_t p = 0; 1311168793Sthompsa int off; 1312168793Sthompsa struct ether_header *eh; 1313168793Sthompsa struct ether_vlan_header vlanbuf; 1314168793Sthompsa const struct ether_vlan_header *vlan; 1315168793Sthompsa#ifdef INET 1316168793Sthompsa const struct ip *ip; 1317168793Sthompsa struct ip ipbuf; 1318168793Sthompsa#endif 1319168793Sthompsa#ifdef INET6 1320168793Sthompsa const struct ip6_hdr *ip6; 1321168793Sthompsa struct ip6_hdr ip6buf; 1322169583Sthompsa uint32_t flow; 1323168793Sthompsa#endif 1324168793Sthompsa 1325168793Sthompsa off = sizeof(*eh); 1326168793Sthompsa if (m->m_len < off) 1327168793Sthompsa goto out; 1328168793Sthompsa eh = mtod(m, struct ether_header *); 1329168793Sthompsa etype = ntohs(eh->ether_type); 1330168793Sthompsa p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key); 1331168793Sthompsa p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 1332168793Sthompsa 1333168793Sthompsa /* Special handling for encapsulating VLAN frames */ 1334168793Sthompsa if (m->m_flags & M_VLANTAG) { 1335168793Sthompsa p = hash32_buf(&m->m_pkthdr.ether_vtag, 1336168793Sthompsa sizeof(m->m_pkthdr.ether_vtag), p); 1337168793Sthompsa } else if (etype == ETHERTYPE_VLAN) { 1338168793Sthompsa vlan = lagg_gethdr(m, off, sizeof(*vlan), &vlanbuf); 1339170599Sthompsa if (vlan == NULL) 1340168793Sthompsa goto out; 1341168793Sthompsa 1342168793Sthompsa p = hash32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 1343168793Sthompsa etype = ntohs(vlan->evl_proto); 1344168793Sthompsa off += sizeof(*vlan) - sizeof(*eh); 1345168793Sthompsa } 1346168793Sthompsa 1347168793Sthompsa switch (etype) { 1348168793Sthompsa#ifdef INET 1349168793Sthompsa case ETHERTYPE_IP: 1350168793Sthompsa ip = lagg_gethdr(m, off, sizeof(*ip), &ipbuf); 1351168793Sthompsa if (ip == NULL) 1352168793Sthompsa goto out; 1353168793Sthompsa 1354168793Sthompsa p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p); 1355168793Sthompsa p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 1356168793Sthompsa break; 1357168793Sthompsa#endif 1358168793Sthompsa#ifdef INET6 1359168793Sthompsa case ETHERTYPE_IPV6: 1360168793Sthompsa ip6 = lagg_gethdr(m, off, sizeof(*ip6), &ip6buf); 1361168793Sthompsa if (ip6 == NULL) 1362168793Sthompsa goto out; 1363168793Sthompsa 1364168793Sthompsa p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 1365168793Sthompsa p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 1366169570Sthompsa flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 1367169570Sthompsa p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */ 1368168793Sthompsa break; 1369168793Sthompsa#endif 1370168793Sthompsa } 1371168793Sthompsaout: 1372168793Sthompsa return (p); 1373168793Sthompsa} 1374168793Sthompsa 1375168793Sthompsaint 1376168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m) 1377168793Sthompsa{ 1378168793Sthompsa 1379185164Skmacy return (ifp->if_transmit)(ifp, m); 1380168793Sthompsa} 1381168793Sthompsa 1382168793Sthompsa/* 1383168793Sthompsa * Simple round robin aggregation 1384168793Sthompsa */ 1385168793Sthompsa 1386168793Sthompsastatic int 1387168793Sthompsalagg_rr_attach(struct lagg_softc *sc) 1388168793Sthompsa{ 1389168793Sthompsa sc->sc_detach = lagg_rr_detach; 1390168793Sthompsa sc->sc_start = lagg_rr_start; 1391168793Sthompsa sc->sc_input = lagg_rr_input; 1392168793Sthompsa sc->sc_port_create = NULL; 1393168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1394172554Sthompsa sc->sc_seq = 0; 1395168793Sthompsa 1396168793Sthompsa return (0); 1397168793Sthompsa} 1398168793Sthompsa 1399168793Sthompsastatic int 1400168793Sthompsalagg_rr_detach(struct lagg_softc *sc) 1401168793Sthompsa{ 1402168793Sthompsa return (0); 1403168793Sthompsa} 1404168793Sthompsa 1405168793Sthompsastatic int 1406168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m) 1407168793Sthompsa{ 1408172554Sthompsa struct lagg_port *lp; 1409172554Sthompsa uint32_t p; 1410168793Sthompsa 1411172554Sthompsa p = atomic_fetchadd_32(&sc->sc_seq, 1); 1412172554Sthompsa p %= sc->sc_count; 1413172554Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 1414172554Sthompsa while (p--) 1415172554Sthompsa lp = SLIST_NEXT(lp, lp_entries); 1416172554Sthompsa 1417172554Sthompsa /* 1418172554Sthompsa * Check the port's link state. This will return the next active 1419172554Sthompsa * port if the link is down or the port is NULL. 1420172554Sthompsa */ 1421172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1422172554Sthompsa m_freem(m); 1423168793Sthompsa return (ENOENT); 1424172554Sthompsa } 1425168793Sthompsa 1426168793Sthompsa /* Send mbuf */ 1427172554Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1428168793Sthompsa} 1429168793Sthompsa 1430168793Sthompsastatic struct mbuf * 1431168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1432168793Sthompsa{ 1433168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1434168793Sthompsa 1435168793Sthompsa /* Just pass in the packet to our lagg device */ 1436168793Sthompsa m->m_pkthdr.rcvif = ifp; 1437168793Sthompsa 1438168793Sthompsa return (m); 1439168793Sthompsa} 1440168793Sthompsa 1441168793Sthompsa/* 1442168793Sthompsa * Active failover 1443168793Sthompsa */ 1444168793Sthompsa 1445168793Sthompsastatic int 1446168793Sthompsalagg_fail_attach(struct lagg_softc *sc) 1447168793Sthompsa{ 1448168793Sthompsa sc->sc_detach = lagg_fail_detach; 1449168793Sthompsa sc->sc_start = lagg_fail_start; 1450168793Sthompsa sc->sc_input = lagg_fail_input; 1451168793Sthompsa sc->sc_port_create = NULL; 1452168793Sthompsa sc->sc_port_destroy = NULL; 1453168793Sthompsa 1454168793Sthompsa return (0); 1455168793Sthompsa} 1456168793Sthompsa 1457168793Sthompsastatic int 1458168793Sthompsalagg_fail_detach(struct lagg_softc *sc) 1459168793Sthompsa{ 1460168793Sthompsa return (0); 1461168793Sthompsa} 1462168793Sthompsa 1463168793Sthompsastatic int 1464168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m) 1465168793Sthompsa{ 1466168793Sthompsa struct lagg_port *lp; 1467168793Sthompsa 1468168793Sthompsa /* Use the master port if active or the next available port */ 1469172554Sthompsa if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) { 1470172554Sthompsa m_freem(m); 1471168793Sthompsa return (ENOENT); 1472172554Sthompsa } 1473168793Sthompsa 1474168793Sthompsa /* Send mbuf */ 1475168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1476168793Sthompsa} 1477168793Sthompsa 1478168793Sthompsastatic struct mbuf * 1479168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1480168793Sthompsa{ 1481168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1482168793Sthompsa struct lagg_port *tmp_tp; 1483168793Sthompsa 1484168793Sthompsa if (lp == sc->sc_primary) { 1485168793Sthompsa m->m_pkthdr.rcvif = ifp; 1486168793Sthompsa return (m); 1487168793Sthompsa } 1488168793Sthompsa 1489174742Sthompsa if (!LAGG_PORTACTIVE(sc->sc_primary)) { 1490174742Sthompsa tmp_tp = lagg_link_active(sc, sc->sc_primary); 1491168793Sthompsa /* 1492168793Sthompsa * If tmp_tp is null, we've recieved a packet when all 1493168793Sthompsa * our links are down. Weird, but process it anyways. 1494168793Sthompsa */ 1495168793Sthompsa if ((tmp_tp == NULL || tmp_tp == lp)) { 1496168793Sthompsa m->m_pkthdr.rcvif = ifp; 1497168793Sthompsa return (m); 1498168793Sthompsa } 1499168793Sthompsa } 1500168793Sthompsa 1501168793Sthompsa m_freem(m); 1502168793Sthompsa return (NULL); 1503168793Sthompsa} 1504168793Sthompsa 1505168793Sthompsa/* 1506168793Sthompsa * Loadbalancing 1507168793Sthompsa */ 1508168793Sthompsa 1509168793Sthompsastatic int 1510168793Sthompsalagg_lb_attach(struct lagg_softc *sc) 1511168793Sthompsa{ 1512168793Sthompsa struct lagg_port *lp; 1513168793Sthompsa struct lagg_lb *lb; 1514168793Sthompsa 1515168793Sthompsa if ((lb = (struct lagg_lb *)malloc(sizeof(struct lagg_lb), 1516168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 1517168793Sthompsa return (ENOMEM); 1518168793Sthompsa 1519168793Sthompsa sc->sc_detach = lagg_lb_detach; 1520168793Sthompsa sc->sc_start = lagg_lb_start; 1521168793Sthompsa sc->sc_input = lagg_lb_input; 1522168793Sthompsa sc->sc_port_create = lagg_lb_port_create; 1523168793Sthompsa sc->sc_port_destroy = lagg_lb_port_destroy; 1524168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1525168793Sthompsa 1526168793Sthompsa lb->lb_key = arc4random(); 1527168793Sthompsa sc->sc_psc = (caddr_t)lb; 1528168793Sthompsa 1529168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1530168793Sthompsa lagg_lb_port_create(lp); 1531168793Sthompsa 1532168793Sthompsa return (0); 1533168793Sthompsa} 1534168793Sthompsa 1535168793Sthompsastatic int 1536168793Sthompsalagg_lb_detach(struct lagg_softc *sc) 1537168793Sthompsa{ 1538168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1539168793Sthompsa if (lb != NULL) 1540168793Sthompsa free(lb, M_DEVBUF); 1541168793Sthompsa return (0); 1542168793Sthompsa} 1543168793Sthompsa 1544168793Sthompsastatic int 1545168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp) 1546168793Sthompsa{ 1547168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1548168793Sthompsa struct lagg_port *lp_next; 1549168793Sthompsa int i = 0; 1550168793Sthompsa 1551168793Sthompsa bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 1552168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1553168793Sthompsa if (lp_next == lp) 1554168793Sthompsa continue; 1555168793Sthompsa if (i >= LAGG_MAX_PORTS) 1556168793Sthompsa return (EINVAL); 1557168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1558168793Sthompsa printf("%s: port %s at index %d\n", 1559168793Sthompsa sc->sc_ifname, lp_next->lp_ifname, i); 1560168793Sthompsa lb->lb_ports[i++] = lp_next; 1561168793Sthompsa } 1562168793Sthompsa 1563168793Sthompsa return (0); 1564168793Sthompsa} 1565168793Sthompsa 1566168793Sthompsastatic int 1567168793Sthompsalagg_lb_port_create(struct lagg_port *lp) 1568168793Sthompsa{ 1569170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1570168793Sthompsa return (lagg_lb_porttable(sc, NULL)); 1571168793Sthompsa} 1572168793Sthompsa 1573168793Sthompsastatic void 1574168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp) 1575168793Sthompsa{ 1576170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1577168793Sthompsa lagg_lb_porttable(sc, lp); 1578168793Sthompsa} 1579168793Sthompsa 1580168793Sthompsastatic int 1581168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m) 1582168793Sthompsa{ 1583168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1584168793Sthompsa struct lagg_port *lp = NULL; 1585168793Sthompsa uint32_t p = 0; 1586168793Sthompsa 1587168793Sthompsa p = lagg_hashmbuf(m, lb->lb_key); 1588180249Sthompsa p %= sc->sc_count; 1589180249Sthompsa lp = lb->lb_ports[p]; 1590168793Sthompsa 1591168793Sthompsa /* 1592168793Sthompsa * Check the port's link state. This will return the next active 1593168793Sthompsa * port if the link is down or the port is NULL. 1594168793Sthompsa */ 1595172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1596172554Sthompsa m_freem(m); 1597168793Sthompsa return (ENOENT); 1598172554Sthompsa } 1599168793Sthompsa 1600168793Sthompsa /* Send mbuf */ 1601168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1602168793Sthompsa} 1603168793Sthompsa 1604168793Sthompsastatic struct mbuf * 1605168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1606168793Sthompsa{ 1607168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1608168793Sthompsa 1609168793Sthompsa /* Just pass in the packet to our lagg device */ 1610168793Sthompsa m->m_pkthdr.rcvif = ifp; 1611168793Sthompsa 1612168793Sthompsa return (m); 1613168793Sthompsa} 1614168793Sthompsa 1615168793Sthompsa/* 1616168793Sthompsa * 802.3ad LACP 1617168793Sthompsa */ 1618168793Sthompsa 1619168793Sthompsastatic int 1620168793Sthompsalagg_lacp_attach(struct lagg_softc *sc) 1621168793Sthompsa{ 1622168793Sthompsa struct lagg_port *lp; 1623168793Sthompsa int error; 1624168793Sthompsa 1625168793Sthompsa sc->sc_detach = lagg_lacp_detach; 1626168793Sthompsa sc->sc_port_create = lacp_port_create; 1627168793Sthompsa sc->sc_port_destroy = lacp_port_destroy; 1628168793Sthompsa sc->sc_linkstate = lacp_linkstate; 1629168793Sthompsa sc->sc_start = lagg_lacp_start; 1630168793Sthompsa sc->sc_input = lagg_lacp_input; 1631168793Sthompsa sc->sc_init = lacp_init; 1632168793Sthompsa sc->sc_stop = lacp_stop; 1633168793Sthompsa sc->sc_lladdr = lagg_lacp_lladdr; 1634171247Sthompsa sc->sc_req = lacp_req; 1635171247Sthompsa sc->sc_portreq = lacp_portreq; 1636168793Sthompsa 1637168793Sthompsa error = lacp_attach(sc); 1638168793Sthompsa if (error) 1639168793Sthompsa return (error); 1640168793Sthompsa 1641168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1642168793Sthompsa lacp_port_create(lp); 1643168793Sthompsa 1644168793Sthompsa return (error); 1645168793Sthompsa} 1646168793Sthompsa 1647168793Sthompsastatic int 1648168793Sthompsalagg_lacp_detach(struct lagg_softc *sc) 1649168793Sthompsa{ 1650168793Sthompsa struct lagg_port *lp; 1651168793Sthompsa int error; 1652168793Sthompsa 1653168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1654168793Sthompsa lacp_port_destroy(lp); 1655168793Sthompsa 1656168793Sthompsa /* unlocking is safe here */ 1657169569Sthompsa LAGG_WUNLOCK(sc); 1658168793Sthompsa error = lacp_detach(sc); 1659169569Sthompsa LAGG_WLOCK(sc); 1660168793Sthompsa 1661168793Sthompsa return (error); 1662168793Sthompsa} 1663168793Sthompsa 1664168793Sthompsastatic void 1665168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc) 1666168793Sthompsa{ 1667168793Sthompsa struct lagg_port *lp; 1668168793Sthompsa 1669168793Sthompsa /* purge all the lacp ports */ 1670168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1671168793Sthompsa lacp_port_destroy(lp); 1672168793Sthompsa 1673168793Sthompsa /* add them back in */ 1674168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1675168793Sthompsa lacp_port_create(lp); 1676168793Sthompsa} 1677168793Sthompsa 1678168793Sthompsastatic int 1679168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m) 1680168793Sthompsa{ 1681168793Sthompsa struct lagg_port *lp; 1682168793Sthompsa 1683168793Sthompsa lp = lacp_select_tx_port(sc, m); 1684172554Sthompsa if (lp == NULL) { 1685172554Sthompsa m_freem(m); 1686168793Sthompsa return (EBUSY); 1687172554Sthompsa } 1688168793Sthompsa 1689168793Sthompsa /* Send mbuf */ 1690168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1691168793Sthompsa} 1692168793Sthompsa 1693168793Sthompsastatic struct mbuf * 1694168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1695168793Sthompsa{ 1696168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1697168793Sthompsa struct ether_header *eh; 1698168793Sthompsa u_short etype; 1699168793Sthompsa 1700168793Sthompsa eh = mtod(m, struct ether_header *); 1701168793Sthompsa etype = ntohs(eh->ether_type); 1702168793Sthompsa 1703168793Sthompsa /* Tap off LACP control messages */ 1704168793Sthompsa if (etype == ETHERTYPE_SLOW) { 1705175005Sthompsa m = lacp_input(lp, m); 1706175005Sthompsa if (m == NULL) 1707175005Sthompsa return (NULL); 1708168793Sthompsa } 1709168793Sthompsa 1710168793Sthompsa /* 1711168793Sthompsa * If the port is not collecting or not in the active aggregator then 1712168793Sthompsa * free and return. 1713168793Sthompsa */ 1714177274Sthompsa if (lacp_iscollecting(lp) == 0 || lacp_isactive(lp) == 0) { 1715168793Sthompsa m_freem(m); 1716168793Sthompsa return (NULL); 1717168793Sthompsa } 1718168793Sthompsa 1719168793Sthompsa m->m_pkthdr.rcvif = ifp; 1720168793Sthompsa return (m); 1721168793Sthompsa} 1722