if_lagg.c revision 185164
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 185164 2008-11-22 07:35:45Z kmacy $"); 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; 313168793Sthompsa 314169569Sthompsa LAGG_WLOCK_ASSERT(sc); 315168793Sthompsa 316168793Sthompsa /* Get capabilities from the lagg ports */ 317171661Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 318171661Sthompsa cap &= lp->lp_ifp->if_capabilities; 319171661Sthompsa ena &= lp->lp_ifp->if_capenable; 320171661Sthompsa } 321171661Sthompsa cap = (cap == ~0 ? 0 : cap); 322171661Sthompsa ena = (ena == ~0 ? 0 : ena); 323168793Sthompsa 324171661Sthompsa if (sc->sc_ifp->if_capabilities != cap || 325171661Sthompsa sc->sc_ifp->if_capenable != ena) { 326171661Sthompsa sc->sc_ifp->if_capabilities = cap; 327171661Sthompsa sc->sc_ifp->if_capenable = ena; 328171661Sthompsa getmicrotime(&sc->sc_ifp->if_lastchange); 329171661Sthompsa 330171661Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 331171661Sthompsa if_printf(sc->sc_ifp, 332171661Sthompsa "capabilities 0x%08x enabled 0x%08x\n", cap, ena); 333168793Sthompsa } 334168793Sthompsa} 335168793Sthompsa 336168793Sthompsastatic void 337168793Sthompsalagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr) 338168793Sthompsa{ 339170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 340168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 341169329Sthompsa struct lagg_llq *llq; 342169329Sthompsa int pending = 0; 343168793Sthompsa 344169569Sthompsa LAGG_WLOCK_ASSERT(sc); 345169329Sthompsa 346169328Sthompsa if (lp->lp_detaching || 347169328Sthompsa memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) 348168793Sthompsa return; 349168793Sthompsa 350169329Sthompsa /* Check to make sure its not already queued to be changed */ 351169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 352169329Sthompsa if (llq->llq_ifp == ifp) { 353169329Sthompsa pending = 1; 354169329Sthompsa break; 355169329Sthompsa } 356169329Sthompsa } 357168793Sthompsa 358169329Sthompsa if (!pending) { 359169329Sthompsa llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT); 360169329Sthompsa if (llq == NULL) /* XXX what to do */ 361169329Sthompsa return; 362169329Sthompsa } 363169329Sthompsa 364169329Sthompsa /* Update the lladdr even if pending, it may have changed */ 365169329Sthompsa llq->llq_ifp = ifp; 366169329Sthompsa bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN); 367169329Sthompsa 368169329Sthompsa if (!pending) 369169329Sthompsa SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries); 370169329Sthompsa 371169329Sthompsa taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task); 372168793Sthompsa} 373168793Sthompsa 374169329Sthompsa/* 375169329Sthompsa * Set the interface MAC address from a taskqueue to avoid a LOR. 376169329Sthompsa */ 377169329Sthompsastatic void 378169329Sthompsalagg_port_setlladdr(void *arg, int pending) 379169329Sthompsa{ 380169329Sthompsa struct lagg_softc *sc = (struct lagg_softc *)arg; 381169329Sthompsa struct lagg_llq *llq, *head; 382169329Sthompsa struct ifnet *ifp; 383169329Sthompsa int error; 384169329Sthompsa 385169329Sthompsa /* Grab a local reference of the queue and remove it from the softc */ 386169569Sthompsa LAGG_WLOCK(sc); 387169329Sthompsa head = SLIST_FIRST(&sc->sc_llq_head); 388169329Sthompsa SLIST_FIRST(&sc->sc_llq_head) = NULL; 389169569Sthompsa LAGG_WUNLOCK(sc); 390169329Sthompsa 391169329Sthompsa /* 392169329Sthompsa * Traverse the queue and set the lladdr on each ifp. It is safe to do 393169329Sthompsa * unlocked as we have the only reference to it. 394169329Sthompsa */ 395169329Sthompsa for (llq = head; llq != NULL; llq = head) { 396169329Sthompsa ifp = llq->llq_ifp; 397169329Sthompsa 398169329Sthompsa /* Set the link layer address */ 399169329Sthompsa error = if_setlladdr(ifp, llq->llq_lladdr, ETHER_ADDR_LEN); 400169329Sthompsa if (error) 401169329Sthompsa printf("%s: setlladdr failed on %s\n", __func__, 402169329Sthompsa ifp->if_xname); 403169329Sthompsa 404169329Sthompsa head = SLIST_NEXT(llq, llq_entries); 405169329Sthompsa free(llq, M_DEVBUF); 406169329Sthompsa } 407169329Sthompsa} 408169329Sthompsa 409168793Sthompsastatic int 410168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) 411168793Sthompsa{ 412168793Sthompsa struct lagg_softc *sc_ptr; 413168793Sthompsa struct lagg_port *lp; 414168793Sthompsa int error = 0; 415168793Sthompsa 416169569Sthompsa LAGG_WLOCK_ASSERT(sc); 417168793Sthompsa 418168793Sthompsa /* Limit the maximal number of lagg ports */ 419168793Sthompsa if (sc->sc_count >= LAGG_MAX_PORTS) 420168793Sthompsa return (ENOSPC); 421168793Sthompsa 422168793Sthompsa /* New lagg port has to be in an idle state */ 423168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 424168793Sthompsa return (EBUSY); 425168793Sthompsa 426168793Sthompsa /* Check if port has already been associated to a lagg */ 427168793Sthompsa if (ifp->if_lagg != NULL) 428168793Sthompsa return (EBUSY); 429168793Sthompsa 430168793Sthompsa /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 431168793Sthompsa if (ifp->if_type != IFT_ETHER) 432168793Sthompsa return (EPROTONOSUPPORT); 433168793Sthompsa 434171661Sthompsa /* Allow the first Ethernet member to define the MTU */ 435171661Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) 436171661Sthompsa sc->sc_ifp->if_mtu = ifp->if_mtu; 437171661Sthompsa else if (sc->sc_ifp->if_mtu != ifp->if_mtu) { 438171661Sthompsa if_printf(sc->sc_ifp, "invalid MTU for %s\n", 439171661Sthompsa ifp->if_xname); 440171661Sthompsa return (EINVAL); 441171661Sthompsa } 442171661Sthompsa 443168793Sthompsa if ((lp = malloc(sizeof(struct lagg_port), 444168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 445168793Sthompsa return (ENOMEM); 446168793Sthompsa 447168793Sthompsa /* Check if port is a stacked lagg */ 448168793Sthompsa mtx_lock(&lagg_list_mtx); 449168793Sthompsa SLIST_FOREACH(sc_ptr, &lagg_list, sc_entries) { 450168793Sthompsa if (ifp == sc_ptr->sc_ifp) { 451168793Sthompsa mtx_unlock(&lagg_list_mtx); 452168793Sthompsa free(lp, M_DEVBUF); 453168793Sthompsa return (EINVAL); 454168793Sthompsa /* XXX disable stacking for the moment, its untested 455168793Sthompsa lp->lp_flags |= LAGG_PORT_STACK; 456168793Sthompsa if (lagg_port_checkstacking(sc_ptr) >= 457168793Sthompsa LAGG_MAX_STACKING) { 458168793Sthompsa mtx_unlock(&lagg_list_mtx); 459168793Sthompsa free(lp, M_DEVBUF); 460168793Sthompsa return (E2BIG); 461168793Sthompsa } 462168793Sthompsa */ 463168793Sthompsa } 464168793Sthompsa } 465168793Sthompsa mtx_unlock(&lagg_list_mtx); 466168793Sthompsa 467168793Sthompsa /* Change the interface type */ 468168793Sthompsa lp->lp_iftype = ifp->if_type; 469168793Sthompsa ifp->if_type = IFT_IEEE8023ADLAG; 470168793Sthompsa ifp->if_lagg = lp; 471168793Sthompsa lp->lp_ioctl = ifp->if_ioctl; 472168793Sthompsa ifp->if_ioctl = lagg_port_ioctl; 473168793Sthompsa lp->lp_output = ifp->if_output; 474168793Sthompsa ifp->if_output = lagg_port_output; 475168793Sthompsa 476168793Sthompsa lp->lp_ifp = ifp; 477170599Sthompsa lp->lp_softc = sc; 478168793Sthompsa 479168793Sthompsa /* Save port link layer address */ 480168793Sthompsa bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN); 481168793Sthompsa 482168793Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) { 483168793Sthompsa sc->sc_primary = lp; 484168793Sthompsa lagg_lladdr(sc, IF_LLADDR(ifp)); 485168793Sthompsa } else { 486168793Sthompsa /* Update link layer address for this port */ 487168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp)); 488168793Sthompsa } 489168793Sthompsa 490168793Sthompsa /* Insert into the list of ports */ 491168793Sthompsa SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries); 492168793Sthompsa sc->sc_count++; 493168793Sthompsa 494168793Sthompsa /* Update lagg capabilities */ 495171661Sthompsa lagg_capabilities(sc); 496173895Sthompsa lagg_linkstate(sc); 497168793Sthompsa 498168793Sthompsa /* Add multicast addresses and interface flags to this port */ 499168793Sthompsa lagg_ether_cmdmulti(lp, 1); 500168793Sthompsa lagg_setflags(lp, 1); 501168793Sthompsa 502168793Sthompsa if (sc->sc_port_create != NULL) 503168793Sthompsa error = (*sc->sc_port_create)(lp); 504168793Sthompsa if (error) { 505170599Sthompsa /* remove the port again, without calling sc_port_destroy */ 506168793Sthompsa lagg_port_destroy(lp, 0); 507168793Sthompsa return (error); 508168793Sthompsa } 509168793Sthompsa 510168793Sthompsa return (error); 511168793Sthompsa} 512168793Sthompsa 513168793Sthompsastatic int 514168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc) 515168793Sthompsa{ 516168793Sthompsa struct lagg_softc *sc_ptr; 517168793Sthompsa struct lagg_port *lp; 518168793Sthompsa int m = 0; 519168793Sthompsa 520169569Sthompsa LAGG_WLOCK_ASSERT(sc); 521168793Sthompsa 522168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 523168793Sthompsa if (lp->lp_flags & LAGG_PORT_STACK) { 524168793Sthompsa sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc; 525168793Sthompsa m = MAX(m, lagg_port_checkstacking(sc_ptr)); 526168793Sthompsa } 527168793Sthompsa } 528168793Sthompsa 529168793Sthompsa return (m + 1); 530168793Sthompsa} 531168793Sthompsa 532168793Sthompsastatic int 533168793Sthompsalagg_port_destroy(struct lagg_port *lp, int runpd) 534168793Sthompsa{ 535170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 536168793Sthompsa struct lagg_port *lp_ptr; 537169329Sthompsa struct lagg_llq *llq; 538168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 539168793Sthompsa 540169569Sthompsa LAGG_WLOCK_ASSERT(sc); 541168793Sthompsa 542168793Sthompsa if (runpd && sc->sc_port_destroy != NULL) 543168793Sthompsa (*sc->sc_port_destroy)(lp); 544168793Sthompsa 545169328Sthompsa /* 546169328Sthompsa * Remove multicast addresses and interface flags from this port and 547169328Sthompsa * reset the MAC address, skip if the interface is being detached. 548169328Sthompsa */ 549169328Sthompsa if (!lp->lp_detaching) { 550169328Sthompsa lagg_ether_cmdmulti(lp, 0); 551169328Sthompsa lagg_setflags(lp, 0); 552169328Sthompsa lagg_port_lladdr(lp, lp->lp_lladdr); 553169328Sthompsa } 554168793Sthompsa 555168793Sthompsa /* Restore interface */ 556168793Sthompsa ifp->if_type = lp->lp_iftype; 557168793Sthompsa ifp->if_ioctl = lp->lp_ioctl; 558168793Sthompsa ifp->if_output = lp->lp_output; 559168793Sthompsa ifp->if_lagg = NULL; 560168793Sthompsa 561168793Sthompsa /* Finally, remove the port from the lagg */ 562168793Sthompsa SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries); 563168793Sthompsa sc->sc_count--; 564168793Sthompsa 565168793Sthompsa /* Update the primary interface */ 566168793Sthompsa if (lp == sc->sc_primary) { 567168793Sthompsa uint8_t lladdr[ETHER_ADDR_LEN]; 568168793Sthompsa 569168793Sthompsa if ((lp_ptr = SLIST_FIRST(&sc->sc_ports)) == NULL) { 570168793Sthompsa bzero(&lladdr, ETHER_ADDR_LEN); 571168793Sthompsa } else { 572168793Sthompsa bcopy(lp_ptr->lp_lladdr, 573168793Sthompsa lladdr, ETHER_ADDR_LEN); 574168793Sthompsa } 575168793Sthompsa lagg_lladdr(sc, lladdr); 576168793Sthompsa sc->sc_primary = lp_ptr; 577168793Sthompsa 578168793Sthompsa /* Update link layer address for each port */ 579168793Sthompsa SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries) 580168793Sthompsa lagg_port_lladdr(lp_ptr, lladdr); 581168793Sthompsa } 582168793Sthompsa 583169329Sthompsa /* Remove any pending lladdr changes from the queue */ 584169329Sthompsa if (lp->lp_detaching) { 585169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 586169329Sthompsa if (llq->llq_ifp == ifp) { 587169329Sthompsa SLIST_REMOVE(&sc->sc_llq_head, llq, lagg_llq, 588169329Sthompsa llq_entries); 589169329Sthompsa free(llq, M_DEVBUF); 590169329Sthompsa break; /* Only appears once */ 591169329Sthompsa } 592169329Sthompsa } 593169329Sthompsa } 594169329Sthompsa 595168793Sthompsa if (lp->lp_ifflags) 596168793Sthompsa if_printf(ifp, "%s: lp_ifflags unclean\n", __func__); 597168793Sthompsa 598168793Sthompsa free(lp, M_DEVBUF); 599168793Sthompsa 600168793Sthompsa /* Update lagg capabilities */ 601171661Sthompsa lagg_capabilities(sc); 602173895Sthompsa lagg_linkstate(sc); 603168793Sthompsa 604168793Sthompsa return (0); 605168793Sthompsa} 606168793Sthompsa 607168793Sthompsastatic int 608168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 609168793Sthompsa{ 610168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data; 611168793Sthompsa struct lagg_softc *sc; 612168793Sthompsa struct lagg_port *lp = NULL; 613168793Sthompsa int error = 0; 614168793Sthompsa 615168793Sthompsa /* Should be checked by the caller */ 616168793Sthompsa if (ifp->if_type != IFT_IEEE8023ADLAG || 617170599Sthompsa (lp = ifp->if_lagg) == NULL || (sc = lp->lp_softc) == NULL) 618168793Sthompsa goto fallback; 619168793Sthompsa 620168793Sthompsa switch (cmd) { 621168793Sthompsa case SIOCGLAGGPORT: 622168793Sthompsa if (rp->rp_portname[0] == '\0' || 623168793Sthompsa ifunit(rp->rp_portname) != ifp) { 624168793Sthompsa error = EINVAL; 625168793Sthompsa break; 626168793Sthompsa } 627168793Sthompsa 628171603Sthompsa LAGG_RLOCK(sc); 629171603Sthompsa if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) { 630168793Sthompsa error = ENOENT; 631171603Sthompsa LAGG_RUNLOCK(sc); 632168793Sthompsa break; 633168793Sthompsa } 634168793Sthompsa 635168793Sthompsa lagg_port2req(lp, rp); 636169569Sthompsa LAGG_RUNLOCK(sc); 637168793Sthompsa break; 638171661Sthompsa 639171661Sthompsa case SIOCSIFCAP: 640171661Sthompsa if (lp->lp_ioctl == NULL) { 641171661Sthompsa error = EINVAL; 642171661Sthompsa break; 643171661Sthompsa } 644171661Sthompsa error = (*lp->lp_ioctl)(ifp, cmd, data); 645171661Sthompsa if (error) 646171661Sthompsa break; 647171661Sthompsa 648171661Sthompsa /* Update lagg interface capabilities */ 649171661Sthompsa LAGG_WLOCK(sc); 650171661Sthompsa lagg_capabilities(sc); 651171661Sthompsa LAGG_WUNLOCK(sc); 652171661Sthompsa break; 653171661Sthompsa 654171661Sthompsa case SIOCSIFMTU: 655171661Sthompsa /* Do not allow the MTU to be changed once joined */ 656171661Sthompsa error = EINVAL; 657171661Sthompsa break; 658171661Sthompsa 659168793Sthompsa default: 660168793Sthompsa goto fallback; 661168793Sthompsa } 662168793Sthompsa 663168793Sthompsa return (error); 664168793Sthompsa 665168793Sthompsafallback: 666169340Sthompsa if (lp->lp_ioctl != NULL) 667168793Sthompsa return ((*lp->lp_ioctl)(ifp, cmd, data)); 668168793Sthompsa 669168793Sthompsa return (EINVAL); 670168793Sthompsa} 671168793Sthompsa 672168793Sthompsastatic int 673168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m, 674168793Sthompsa struct sockaddr *dst, struct rtentry *rt0) 675168793Sthompsa{ 676168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 677168793Sthompsa struct ether_header *eh; 678168793Sthompsa short type = 0; 679168793Sthompsa 680168793Sthompsa switch (dst->sa_family) { 681168793Sthompsa case pseudo_AF_HDRCMPLT: 682168793Sthompsa case AF_UNSPEC: 683168793Sthompsa eh = (struct ether_header *)dst->sa_data; 684168793Sthompsa type = eh->ether_type; 685168793Sthompsa break; 686168793Sthompsa } 687168793Sthompsa 688168793Sthompsa /* 689168793Sthompsa * Only allow ethernet types required to initiate or maintain the link, 690168793Sthompsa * aggregated frames take a different path. 691168793Sthompsa */ 692168793Sthompsa switch (ntohs(type)) { 693168793Sthompsa case ETHERTYPE_PAE: /* EAPOL PAE/802.1x */ 694168793Sthompsa return ((*lp->lp_output)(ifp, m, dst, rt0)); 695168793Sthompsa } 696168793Sthompsa 697168793Sthompsa /* drop any other frames */ 698168793Sthompsa m_freem(m); 699168793Sthompsa return (EBUSY); 700168793Sthompsa} 701168793Sthompsa 702168793Sthompsastatic void 703168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) 704168793Sthompsa{ 705168793Sthompsa struct lagg_port *lp; 706168793Sthompsa struct lagg_softc *sc; 707168793Sthompsa 708168793Sthompsa if ((lp = ifp->if_lagg) == NULL) 709168793Sthompsa return; 710168793Sthompsa 711170599Sthompsa sc = lp->lp_softc; 712168793Sthompsa 713169569Sthompsa LAGG_WLOCK(sc); 714169328Sthompsa lp->lp_detaching = 1; 715168793Sthompsa lagg_port_destroy(lp, 1); 716169569Sthompsa LAGG_WUNLOCK(sc); 717168793Sthompsa} 718168793Sthompsa 719168793Sthompsastatic void 720168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp) 721168793Sthompsa{ 722170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 723172020Sthompsa 724168793Sthompsa strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname)); 725168793Sthompsa strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname)); 726168793Sthompsa rp->rp_prio = lp->lp_prio; 727168793Sthompsa rp->rp_flags = lp->lp_flags; 728171247Sthompsa if (sc->sc_portreq != NULL) 729171247Sthompsa (*sc->sc_portreq)(lp, (caddr_t)&rp->rp_psc); 730168793Sthompsa 731168793Sthompsa /* Add protocol specific flags */ 732168793Sthompsa switch (sc->sc_proto) { 733168793Sthompsa case LAGG_PROTO_FAILOVER: 734168793Sthompsa if (lp == sc->sc_primary) 735169204Sthompsa rp->rp_flags |= LAGG_PORT_MASTER; 736172020Sthompsa if (lp == lagg_link_active(sc, sc->sc_primary)) 737172020Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 738172020Sthompsa break; 739172020Sthompsa 740168793Sthompsa case LAGG_PROTO_ROUNDROBIN: 741168793Sthompsa case LAGG_PROTO_LOADBALANCE: 742168793Sthompsa case LAGG_PROTO_ETHERCHANNEL: 743168793Sthompsa if (LAGG_PORTACTIVE(lp)) 744168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 745168793Sthompsa break; 746168793Sthompsa 747168793Sthompsa case LAGG_PROTO_LACP: 748168793Sthompsa /* LACP has a different definition of active */ 749177274Sthompsa if (lacp_isactive(lp)) 750168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 751177274Sthompsa if (lacp_iscollecting(lp)) 752177274Sthompsa rp->rp_flags |= LAGG_PORT_COLLECTING; 753177274Sthompsa if (lacp_isdistributing(lp)) 754177274Sthompsa rp->rp_flags |= LAGG_PORT_DISTRIBUTING; 755168793Sthompsa break; 756168793Sthompsa } 757168793Sthompsa 758168793Sthompsa} 759168793Sthompsa 760168793Sthompsastatic void 761168793Sthompsalagg_init(void *xsc) 762168793Sthompsa{ 763168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)xsc; 764168793Sthompsa struct lagg_port *lp; 765168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 766168793Sthompsa 767168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 768168793Sthompsa return; 769168793Sthompsa 770169569Sthompsa LAGG_WLOCK(sc); 771168793Sthompsa 772168793Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 773168793Sthompsa /* Update the port lladdrs */ 774168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 775168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(ifp)); 776168793Sthompsa 777168793Sthompsa if (sc->sc_init != NULL) 778168793Sthompsa (*sc->sc_init)(sc); 779168793Sthompsa 780169569Sthompsa LAGG_WUNLOCK(sc); 781168793Sthompsa} 782168793Sthompsa 783168793Sthompsastatic void 784168793Sthompsalagg_stop(struct lagg_softc *sc) 785168793Sthompsa{ 786168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 787168793Sthompsa 788169569Sthompsa LAGG_WLOCK_ASSERT(sc); 789168793Sthompsa 790168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 791168793Sthompsa return; 792168793Sthompsa 793168793Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 794168793Sthompsa 795168793Sthompsa if (sc->sc_stop != NULL) 796168793Sthompsa (*sc->sc_stop)(sc); 797168793Sthompsa} 798168793Sthompsa 799168793Sthompsastatic int 800168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 801168793Sthompsa{ 802168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 803168793Sthompsa struct lagg_reqall *ra = (struct lagg_reqall *)data; 804168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf; 805168793Sthompsa struct ifreq *ifr = (struct ifreq *)data; 806168793Sthompsa struct lagg_port *lp; 807168793Sthompsa struct ifnet *tpif; 808168793Sthompsa struct thread *td = curthread; 809171603Sthompsa char *buf, *outbuf; 810171603Sthompsa int count, buflen, len, error = 0; 811168793Sthompsa 812168793Sthompsa bzero(&rpbuf, sizeof(rpbuf)); 813168793Sthompsa 814168793Sthompsa switch (cmd) { 815168793Sthompsa case SIOCGLAGG: 816171603Sthompsa LAGG_RLOCK(sc); 817171603Sthompsa count = 0; 818171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 819171603Sthompsa count++; 820171603Sthompsa buflen = count * sizeof(struct lagg_reqport); 821171603Sthompsa LAGG_RUNLOCK(sc); 822171603Sthompsa 823171603Sthompsa outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); 824171603Sthompsa 825171603Sthompsa LAGG_RLOCK(sc); 826168793Sthompsa ra->ra_proto = sc->sc_proto; 827171247Sthompsa if (sc->sc_req != NULL) 828171247Sthompsa (*sc->sc_req)(sc, (caddr_t)&ra->ra_psc); 829171603Sthompsa 830171603Sthompsa count = 0; 831171603Sthompsa buf = outbuf; 832171603Sthompsa len = min(ra->ra_size, buflen); 833171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 834171603Sthompsa if (len < sizeof(rpbuf)) 835171603Sthompsa break; 836171603Sthompsa 837168793Sthompsa lagg_port2req(lp, &rpbuf); 838171603Sthompsa memcpy(buf, &rpbuf, sizeof(rpbuf)); 839171603Sthompsa count++; 840171603Sthompsa buf += sizeof(rpbuf); 841171603Sthompsa len -= sizeof(rpbuf); 842168793Sthompsa } 843171603Sthompsa LAGG_RUNLOCK(sc); 844171603Sthompsa ra->ra_ports = count; 845171603Sthompsa ra->ra_size = count * sizeof(rpbuf); 846171603Sthompsa error = copyout(outbuf, ra->ra_port, ra->ra_size); 847171603Sthompsa free(outbuf, M_TEMP); 848168793Sthompsa break; 849168793Sthompsa case SIOCSLAGG: 850168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 851168793Sthompsa if (error) 852168793Sthompsa break; 853168793Sthompsa if (ra->ra_proto >= LAGG_PROTO_MAX) { 854168793Sthompsa error = EPROTONOSUPPORT; 855168793Sthompsa break; 856168793Sthompsa } 857168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) { 858171603Sthompsa LAGG_WLOCK(sc); 859168793Sthompsa error = sc->sc_detach(sc); 860168793Sthompsa /* Reset protocol and pointers */ 861168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 862168793Sthompsa sc->sc_detach = NULL; 863168793Sthompsa sc->sc_start = NULL; 864168793Sthompsa sc->sc_input = NULL; 865168793Sthompsa sc->sc_port_create = NULL; 866168793Sthompsa sc->sc_port_destroy = NULL; 867168793Sthompsa sc->sc_linkstate = NULL; 868168793Sthompsa sc->sc_init = NULL; 869168793Sthompsa sc->sc_stop = NULL; 870168793Sthompsa sc->sc_lladdr = NULL; 871171247Sthompsa sc->sc_req = NULL; 872171247Sthompsa sc->sc_portreq = NULL; 873171603Sthompsa LAGG_WUNLOCK(sc); 874168793Sthompsa } 875168793Sthompsa if (error != 0) 876168793Sthompsa break; 877171603Sthompsa for (int i = 0; i < (sizeof(lagg_protos) / 878168793Sthompsa sizeof(lagg_protos[0])); i++) { 879168793Sthompsa if (lagg_protos[i].ti_proto == ra->ra_proto) { 880168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 881168793Sthompsa printf("%s: using proto %u\n", 882168793Sthompsa sc->sc_ifname, 883168793Sthompsa lagg_protos[i].ti_proto); 884171603Sthompsa LAGG_WLOCK(sc); 885168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 886168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) 887168793Sthompsa error = lagg_protos[i].ti_attach(sc); 888171603Sthompsa LAGG_WUNLOCK(sc); 889171603Sthompsa return (error); 890168793Sthompsa } 891168793Sthompsa } 892168793Sthompsa error = EPROTONOSUPPORT; 893168793Sthompsa break; 894168793Sthompsa case SIOCGLAGGPORT: 895168793Sthompsa if (rp->rp_portname[0] == '\0' || 896168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 897168793Sthompsa error = EINVAL; 898168793Sthompsa break; 899168793Sthompsa } 900168793Sthompsa 901171603Sthompsa LAGG_RLOCK(sc); 902168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 903170599Sthompsa lp->lp_softc != sc) { 904168793Sthompsa error = ENOENT; 905171603Sthompsa LAGG_RUNLOCK(sc); 906168793Sthompsa break; 907168793Sthompsa } 908168793Sthompsa 909168793Sthompsa lagg_port2req(lp, rp); 910171603Sthompsa LAGG_RUNLOCK(sc); 911168793Sthompsa break; 912168793Sthompsa case SIOCSLAGGPORT: 913168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 914168793Sthompsa if (error) 915168793Sthompsa break; 916168793Sthompsa if (rp->rp_portname[0] == '\0' || 917168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 918168793Sthompsa error = EINVAL; 919168793Sthompsa break; 920168793Sthompsa } 921171603Sthompsa LAGG_WLOCK(sc); 922168793Sthompsa error = lagg_port_create(sc, tpif); 923171603Sthompsa LAGG_WUNLOCK(sc); 924168793Sthompsa break; 925168793Sthompsa case SIOCSLAGGDELPORT: 926168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 927168793Sthompsa if (error) 928168793Sthompsa break; 929168793Sthompsa if (rp->rp_portname[0] == '\0' || 930168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 931168793Sthompsa error = EINVAL; 932168793Sthompsa break; 933168793Sthompsa } 934168793Sthompsa 935171603Sthompsa LAGG_WLOCK(sc); 936168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 937170599Sthompsa lp->lp_softc != sc) { 938168793Sthompsa error = ENOENT; 939171603Sthompsa LAGG_WUNLOCK(sc); 940168793Sthompsa break; 941168793Sthompsa } 942168793Sthompsa 943168793Sthompsa error = lagg_port_destroy(lp, 1); 944171603Sthompsa LAGG_WUNLOCK(sc); 945168793Sthompsa break; 946168793Sthompsa case SIOCSIFFLAGS: 947168793Sthompsa /* Set flags on ports too */ 948171603Sthompsa LAGG_WLOCK(sc); 949168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 950168793Sthompsa lagg_setflags(lp, 1); 951168793Sthompsa } 952171603Sthompsa LAGG_WUNLOCK(sc); 953168793Sthompsa 954168793Sthompsa if (!(ifp->if_flags & IFF_UP) && 955168793Sthompsa (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 956168793Sthompsa /* 957168793Sthompsa * If interface is marked down and it is running, 958168793Sthompsa * then stop and disable it. 959168793Sthompsa */ 960171603Sthompsa LAGG_WLOCK(sc); 961168793Sthompsa lagg_stop(sc); 962171603Sthompsa LAGG_WUNLOCK(sc); 963168793Sthompsa } else if ((ifp->if_flags & IFF_UP) && 964168793Sthompsa !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 965168793Sthompsa /* 966168793Sthompsa * If interface is marked up and it is stopped, then 967168793Sthompsa * start it. 968168793Sthompsa */ 969168793Sthompsa (*ifp->if_init)(sc); 970168793Sthompsa } 971168793Sthompsa break; 972168793Sthompsa case SIOCADDMULTI: 973168793Sthompsa case SIOCDELMULTI: 974171603Sthompsa LAGG_WLOCK(sc); 975168793Sthompsa error = lagg_ether_setmulti(sc); 976171603Sthompsa LAGG_WUNLOCK(sc); 977168793Sthompsa break; 978168793Sthompsa case SIOCSIFMEDIA: 979168793Sthompsa case SIOCGIFMEDIA: 980168793Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 981168793Sthompsa break; 982171661Sthompsa 983171661Sthompsa case SIOCSIFCAP: 984171661Sthompsa case SIOCSIFMTU: 985171661Sthompsa /* Do not allow the MTU or caps to be directly changed */ 986171661Sthompsa error = EINVAL; 987171661Sthompsa break; 988171661Sthompsa 989168793Sthompsa default: 990168793Sthompsa error = ether_ioctl(ifp, cmd, data); 991168793Sthompsa break; 992168793Sthompsa } 993168793Sthompsa return (error); 994168793Sthompsa} 995168793Sthompsa 996168793Sthompsastatic int 997168793Sthompsalagg_ether_setmulti(struct lagg_softc *sc) 998168793Sthompsa{ 999169327Sthompsa struct lagg_port *lp; 1000168793Sthompsa 1001169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1002168793Sthompsa 1003169327Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1004169340Sthompsa /* First, remove any existing filter entries. */ 1005169340Sthompsa lagg_ether_cmdmulti(lp, 0); 1006169340Sthompsa /* copy all addresses from the lagg interface to the port */ 1007169327Sthompsa lagg_ether_cmdmulti(lp, 1); 1008169340Sthompsa } 1009168793Sthompsa return (0); 1010168793Sthompsa} 1011168793Sthompsa 1012168793Sthompsastatic int 1013168793Sthompsalagg_ether_cmdmulti(struct lagg_port *lp, int set) 1014168793Sthompsa{ 1015170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1016169327Sthompsa struct ifnet *ifp = lp->lp_ifp; 1017170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1018169327Sthompsa struct lagg_mc *mc; 1019169327Sthompsa struct ifmultiaddr *ifma, *rifma = NULL; 1020169327Sthompsa struct sockaddr_dl sdl; 1021169327Sthompsa int error; 1022168793Sthompsa 1023169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1024168793Sthompsa 1025168793Sthompsa bzero((char *)&sdl, sizeof(sdl)); 1026168793Sthompsa sdl.sdl_len = sizeof(sdl); 1027168793Sthompsa sdl.sdl_family = AF_LINK; 1028168793Sthompsa sdl.sdl_type = IFT_ETHER; 1029168793Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 1030168793Sthompsa sdl.sdl_index = ifp->if_index; 1031168793Sthompsa 1032169327Sthompsa if (set) { 1033170599Sthompsa TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) { 1034169327Sthompsa if (ifma->ifma_addr->sa_family != AF_LINK) 1035169327Sthompsa continue; 1036169327Sthompsa bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 1037169327Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 1038168793Sthompsa 1039168793Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 1040169327Sthompsa if (error) 1041169327Sthompsa return (error); 1042169327Sthompsa mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); 1043169327Sthompsa if (mc == NULL) 1044169327Sthompsa return (ENOMEM); 1045169327Sthompsa mc->mc_ifma = rifma; 1046169327Sthompsa SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); 1047168793Sthompsa } 1048169327Sthompsa } else { 1049169327Sthompsa while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { 1050169327Sthompsa SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); 1051169327Sthompsa if_delmulti_ifma(mc->mc_ifma); 1052169327Sthompsa free(mc, M_DEVBUF); 1053169327Sthompsa } 1054168793Sthompsa } 1055168793Sthompsa return (0); 1056168793Sthompsa} 1057168793Sthompsa 1058168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */ 1059168793Sthompsastatic int 1060168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status, 1061168793Sthompsa int (*func)(struct ifnet *, int)) 1062168793Sthompsa{ 1063170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1064170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1065168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 1066168793Sthompsa int error; 1067168793Sthompsa 1068169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1069168793Sthompsa 1070170599Sthompsa status = status ? (scifp->if_flags & flag) : 0; 1071168793Sthompsa /* Now "status" contains the flag value or 0 */ 1072168793Sthompsa 1073168793Sthompsa /* 1074168793Sthompsa * See if recorded ports status is different from what 1075168793Sthompsa * we want it to be. If it is, flip it. We record ports 1076168793Sthompsa * status in lp_ifflags so that we won't clear ports flag 1077168793Sthompsa * we haven't set. In fact, we don't clear or set ports 1078168793Sthompsa * flags directly, but get or release references to them. 1079168793Sthompsa * That's why we can be sure that recorded flags still are 1080168793Sthompsa * in accord with actual ports flags. 1081168793Sthompsa */ 1082168793Sthompsa if (status != (lp->lp_ifflags & flag)) { 1083168793Sthompsa error = (*func)(ifp, status); 1084168793Sthompsa if (error) 1085168793Sthompsa return (error); 1086168793Sthompsa lp->lp_ifflags &= ~flag; 1087168793Sthompsa lp->lp_ifflags |= status; 1088168793Sthompsa } 1089168793Sthompsa return (0); 1090168793Sthompsa} 1091168793Sthompsa 1092168793Sthompsa/* 1093168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port 1094168793Sthompsa * if "status" is true, update ports flags respective to the lagg 1095168793Sthompsa * if "status" is false, forcedly clear the flags set on port. 1096168793Sthompsa */ 1097168793Sthompsastatic int 1098168793Sthompsalagg_setflags(struct lagg_port *lp, int status) 1099168793Sthompsa{ 1100168793Sthompsa int error, i; 1101170599Sthompsa 1102168793Sthompsa for (i = 0; lagg_pflags[i].flag; i++) { 1103168793Sthompsa error = lagg_setflag(lp, lagg_pflags[i].flag, 1104168793Sthompsa status, lagg_pflags[i].func); 1105168793Sthompsa if (error) 1106168793Sthompsa return (error); 1107168793Sthompsa } 1108168793Sthompsa return (0); 1109168793Sthompsa} 1110168793Sthompsa 1111168793Sthompsastatic void 1112168793Sthompsalagg_start(struct ifnet *ifp) 1113168793Sthompsa{ 1114168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1115168793Sthompsa struct mbuf *m; 1116168793Sthompsa int error = 0; 1117168793Sthompsa 1118169569Sthompsa LAGG_RLOCK(sc); 1119183160Sthompsa /* We need a Tx algorithm and at least one port */ 1120183160Sthompsa if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) { 1121183160Sthompsa IF_DRAIN(&ifp->if_snd); 1122183160Sthompsa LAGG_RUNLOCK(sc); 1123183160Sthompsa return; 1124183160Sthompsa } 1125183160Sthompsa 1126168793Sthompsa for (;; error = 0) { 1127168793Sthompsa IFQ_DEQUEUE(&ifp->if_snd, m); 1128168793Sthompsa if (m == NULL) 1129168793Sthompsa break; 1130168793Sthompsa 1131172825Sthompsa ETHER_BPF_MTAP(ifp, m); 1132168793Sthompsa 1133183160Sthompsa error = (*sc->sc_start)(sc, m); 1134168793Sthompsa if (error == 0) 1135168793Sthompsa ifp->if_opackets++; 1136172554Sthompsa else 1137168793Sthompsa ifp->if_oerrors++; 1138168793Sthompsa } 1139169569Sthompsa LAGG_RUNLOCK(sc); 1140168793Sthompsa} 1141168793Sthompsa 1142168793Sthompsastatic struct mbuf * 1143168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m) 1144168793Sthompsa{ 1145168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1146170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1147170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1148168793Sthompsa 1149170599Sthompsa if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 1150169227Sthompsa (lp->lp_flags & LAGG_PORT_DISABLED) || 1151168793Sthompsa sc->sc_proto == LAGG_PROTO_NONE) { 1152168793Sthompsa m_freem(m); 1153168793Sthompsa return (NULL); 1154168793Sthompsa } 1155168793Sthompsa 1156169569Sthompsa LAGG_RLOCK(sc); 1157172825Sthompsa ETHER_BPF_MTAP(scifp, m); 1158168793Sthompsa 1159168793Sthompsa m = (*sc->sc_input)(sc, lp, m); 1160168793Sthompsa 1161168793Sthompsa if (m != NULL) { 1162170599Sthompsa scifp->if_ipackets++; 1163170599Sthompsa scifp->if_ibytes += m->m_pkthdr.len; 1164174278Sthompsa 1165174278Sthompsa if (scifp->if_flags & IFF_MONITOR) { 1166174278Sthompsa m_freem(m); 1167174278Sthompsa m = NULL; 1168174278Sthompsa } 1169168793Sthompsa } 1170168793Sthompsa 1171169569Sthompsa LAGG_RUNLOCK(sc); 1172168793Sthompsa return (m); 1173168793Sthompsa} 1174168793Sthompsa 1175168793Sthompsastatic int 1176168793Sthompsalagg_media_change(struct ifnet *ifp) 1177168793Sthompsa{ 1178168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1179168793Sthompsa 1180168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1181168793Sthompsa printf("%s\n", __func__); 1182168793Sthompsa 1183168793Sthompsa /* Ignore */ 1184168793Sthompsa return (0); 1185168793Sthompsa} 1186168793Sthompsa 1187168793Sthompsastatic void 1188168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1189168793Sthompsa{ 1190168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1191168793Sthompsa struct lagg_port *lp; 1192168793Sthompsa 1193168793Sthompsa imr->ifm_status = IFM_AVALID; 1194168793Sthompsa imr->ifm_active = IFM_ETHER | IFM_AUTO; 1195168793Sthompsa 1196169569Sthompsa LAGG_RLOCK(sc); 1197169340Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1198169340Sthompsa if (LAGG_PORTACTIVE(lp)) 1199169340Sthompsa imr->ifm_status |= IFM_ACTIVE; 1200169340Sthompsa } 1201169569Sthompsa LAGG_RUNLOCK(sc); 1202168793Sthompsa} 1203168793Sthompsa 1204168793Sthompsastatic void 1205173895Sthompsalagg_linkstate(struct lagg_softc *sc) 1206173895Sthompsa{ 1207173895Sthompsa struct lagg_port *lp; 1208173895Sthompsa int new_link = LINK_STATE_DOWN; 1209173895Sthompsa 1210173895Sthompsa /* Our link is considered up if at least one of our ports is active */ 1211173895Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1212173895Sthompsa if (lp->lp_link_state == LINK_STATE_UP) { 1213173895Sthompsa new_link = LINK_STATE_UP; 1214173895Sthompsa break; 1215173895Sthompsa } 1216173895Sthompsa } 1217173895Sthompsa if_link_state_change(sc->sc_ifp, new_link); 1218173895Sthompsa} 1219173895Sthompsa 1220173895Sthompsastatic void 1221168793Sthompsalagg_port_state(struct ifnet *ifp, int state) 1222168793Sthompsa{ 1223168793Sthompsa struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; 1224168793Sthompsa struct lagg_softc *sc = NULL; 1225168793Sthompsa 1226168793Sthompsa if (lp != NULL) 1227170599Sthompsa sc = lp->lp_softc; 1228168793Sthompsa if (sc == NULL) 1229168793Sthompsa return; 1230168793Sthompsa 1231169569Sthompsa LAGG_WLOCK(sc); 1232173895Sthompsa lagg_linkstate(sc); 1233168793Sthompsa if (sc->sc_linkstate != NULL) 1234168793Sthompsa (*sc->sc_linkstate)(lp); 1235169569Sthompsa LAGG_WUNLOCK(sc); 1236168793Sthompsa} 1237168793Sthompsa 1238168793Sthompsastruct lagg_port * 1239168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) 1240168793Sthompsa{ 1241168793Sthompsa struct lagg_port *lp_next, *rval = NULL; 1242168793Sthompsa // int new_link = LINK_STATE_DOWN; 1243168793Sthompsa 1244169688Sthompsa LAGG_RLOCK_ASSERT(sc); 1245168793Sthompsa /* 1246168793Sthompsa * Search a port which reports an active link state. 1247168793Sthompsa */ 1248168793Sthompsa 1249168793Sthompsa if (lp == NULL) 1250168793Sthompsa goto search; 1251168793Sthompsa if (LAGG_PORTACTIVE(lp)) { 1252168793Sthompsa rval = lp; 1253168793Sthompsa goto found; 1254168793Sthompsa } 1255168793Sthompsa if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL && 1256168793Sthompsa LAGG_PORTACTIVE(lp_next)) { 1257168793Sthompsa rval = lp_next; 1258168793Sthompsa goto found; 1259168793Sthompsa } 1260168793Sthompsa 1261168793Sthompsasearch: 1262168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1263168793Sthompsa if (LAGG_PORTACTIVE(lp_next)) { 1264168793Sthompsa rval = lp_next; 1265168793Sthompsa goto found; 1266168793Sthompsa } 1267168793Sthompsa } 1268168793Sthompsa 1269168793Sthompsafound: 1270168793Sthompsa if (rval != NULL) { 1271168793Sthompsa /* 1272168793Sthompsa * The IEEE 802.1D standard assumes that a lagg with 1273168793Sthompsa * multiple ports is always full duplex. This is valid 1274168793Sthompsa * for load sharing laggs and if at least two links 1275168793Sthompsa * are active. Unfortunately, checking the latter would 1276168793Sthompsa * be too expensive at this point. 1277168793Sthompsa XXX 1278168793Sthompsa if ((sc->sc_capabilities & IFCAP_LAGG_FULLDUPLEX) && 1279168793Sthompsa (sc->sc_count > 1)) 1280168793Sthompsa new_link = LINK_STATE_FULL_DUPLEX; 1281168793Sthompsa else 1282168793Sthompsa new_link = rval->lp_link_state; 1283168793Sthompsa */ 1284168793Sthompsa } 1285168793Sthompsa 1286168793Sthompsa return (rval); 1287168793Sthompsa} 1288168793Sthompsa 1289168793Sthompsastatic const void * 1290168793Sthompsalagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf) 1291168793Sthompsa{ 1292168793Sthompsa if (m->m_pkthdr.len < (off + len)) { 1293168793Sthompsa return (NULL); 1294168793Sthompsa } else if (m->m_len < (off + len)) { 1295168793Sthompsa m_copydata(m, off, len, buf); 1296168793Sthompsa return (buf); 1297168793Sthompsa } 1298168793Sthompsa return (mtod(m, char *) + off); 1299168793Sthompsa} 1300168793Sthompsa 1301168793Sthompsauint32_t 1302168793Sthompsalagg_hashmbuf(struct mbuf *m, uint32_t key) 1303168793Sthompsa{ 1304168793Sthompsa uint16_t etype; 1305169583Sthompsa uint32_t p = 0; 1306168793Sthompsa int off; 1307168793Sthompsa struct ether_header *eh; 1308168793Sthompsa struct ether_vlan_header vlanbuf; 1309168793Sthompsa const struct ether_vlan_header *vlan; 1310168793Sthompsa#ifdef INET 1311168793Sthompsa const struct ip *ip; 1312168793Sthompsa struct ip ipbuf; 1313168793Sthompsa#endif 1314168793Sthompsa#ifdef INET6 1315168793Sthompsa const struct ip6_hdr *ip6; 1316168793Sthompsa struct ip6_hdr ip6buf; 1317169583Sthompsa uint32_t flow; 1318168793Sthompsa#endif 1319168793Sthompsa 1320168793Sthompsa off = sizeof(*eh); 1321168793Sthompsa if (m->m_len < off) 1322168793Sthompsa goto out; 1323168793Sthompsa eh = mtod(m, struct ether_header *); 1324168793Sthompsa etype = ntohs(eh->ether_type); 1325168793Sthompsa p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key); 1326168793Sthompsa p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 1327168793Sthompsa 1328168793Sthompsa /* Special handling for encapsulating VLAN frames */ 1329168793Sthompsa if (m->m_flags & M_VLANTAG) { 1330168793Sthompsa p = hash32_buf(&m->m_pkthdr.ether_vtag, 1331168793Sthompsa sizeof(m->m_pkthdr.ether_vtag), p); 1332168793Sthompsa } else if (etype == ETHERTYPE_VLAN) { 1333168793Sthompsa vlan = lagg_gethdr(m, off, sizeof(*vlan), &vlanbuf); 1334170599Sthompsa if (vlan == NULL) 1335168793Sthompsa goto out; 1336168793Sthompsa 1337168793Sthompsa p = hash32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 1338168793Sthompsa etype = ntohs(vlan->evl_proto); 1339168793Sthompsa off += sizeof(*vlan) - sizeof(*eh); 1340168793Sthompsa } 1341168793Sthompsa 1342168793Sthompsa switch (etype) { 1343168793Sthompsa#ifdef INET 1344168793Sthompsa case ETHERTYPE_IP: 1345168793Sthompsa ip = lagg_gethdr(m, off, sizeof(*ip), &ipbuf); 1346168793Sthompsa if (ip == NULL) 1347168793Sthompsa goto out; 1348168793Sthompsa 1349168793Sthompsa p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p); 1350168793Sthompsa p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 1351168793Sthompsa break; 1352168793Sthompsa#endif 1353168793Sthompsa#ifdef INET6 1354168793Sthompsa case ETHERTYPE_IPV6: 1355168793Sthompsa ip6 = lagg_gethdr(m, off, sizeof(*ip6), &ip6buf); 1356168793Sthompsa if (ip6 == NULL) 1357168793Sthompsa goto out; 1358168793Sthompsa 1359168793Sthompsa p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 1360168793Sthompsa p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 1361169570Sthompsa flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 1362169570Sthompsa p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */ 1363168793Sthompsa break; 1364168793Sthompsa#endif 1365168793Sthompsa } 1366168793Sthompsaout: 1367168793Sthompsa return (p); 1368168793Sthompsa} 1369168793Sthompsa 1370168793Sthompsaint 1371168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m) 1372168793Sthompsa{ 1373168793Sthompsa 1374185164Skmacy return (ifp->if_transmit)(ifp, m); 1375168793Sthompsa} 1376168793Sthompsa 1377168793Sthompsa/* 1378168793Sthompsa * Simple round robin aggregation 1379168793Sthompsa */ 1380168793Sthompsa 1381168793Sthompsastatic int 1382168793Sthompsalagg_rr_attach(struct lagg_softc *sc) 1383168793Sthompsa{ 1384168793Sthompsa sc->sc_detach = lagg_rr_detach; 1385168793Sthompsa sc->sc_start = lagg_rr_start; 1386168793Sthompsa sc->sc_input = lagg_rr_input; 1387168793Sthompsa sc->sc_port_create = NULL; 1388168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1389172554Sthompsa sc->sc_seq = 0; 1390168793Sthompsa 1391168793Sthompsa return (0); 1392168793Sthompsa} 1393168793Sthompsa 1394168793Sthompsastatic int 1395168793Sthompsalagg_rr_detach(struct lagg_softc *sc) 1396168793Sthompsa{ 1397168793Sthompsa return (0); 1398168793Sthompsa} 1399168793Sthompsa 1400168793Sthompsastatic int 1401168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m) 1402168793Sthompsa{ 1403172554Sthompsa struct lagg_port *lp; 1404172554Sthompsa uint32_t p; 1405168793Sthompsa 1406172554Sthompsa p = atomic_fetchadd_32(&sc->sc_seq, 1); 1407172554Sthompsa p %= sc->sc_count; 1408172554Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 1409172554Sthompsa while (p--) 1410172554Sthompsa lp = SLIST_NEXT(lp, lp_entries); 1411172554Sthompsa 1412172554Sthompsa /* 1413172554Sthompsa * Check the port's link state. This will return the next active 1414172554Sthompsa * port if the link is down or the port is NULL. 1415172554Sthompsa */ 1416172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1417172554Sthompsa m_freem(m); 1418168793Sthompsa return (ENOENT); 1419172554Sthompsa } 1420168793Sthompsa 1421168793Sthompsa /* Send mbuf */ 1422172554Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1423168793Sthompsa} 1424168793Sthompsa 1425168793Sthompsastatic struct mbuf * 1426168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1427168793Sthompsa{ 1428168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1429168793Sthompsa 1430168793Sthompsa /* Just pass in the packet to our lagg device */ 1431168793Sthompsa m->m_pkthdr.rcvif = ifp; 1432168793Sthompsa 1433168793Sthompsa return (m); 1434168793Sthompsa} 1435168793Sthompsa 1436168793Sthompsa/* 1437168793Sthompsa * Active failover 1438168793Sthompsa */ 1439168793Sthompsa 1440168793Sthompsastatic int 1441168793Sthompsalagg_fail_attach(struct lagg_softc *sc) 1442168793Sthompsa{ 1443168793Sthompsa sc->sc_detach = lagg_fail_detach; 1444168793Sthompsa sc->sc_start = lagg_fail_start; 1445168793Sthompsa sc->sc_input = lagg_fail_input; 1446168793Sthompsa sc->sc_port_create = NULL; 1447168793Sthompsa sc->sc_port_destroy = NULL; 1448168793Sthompsa 1449168793Sthompsa return (0); 1450168793Sthompsa} 1451168793Sthompsa 1452168793Sthompsastatic int 1453168793Sthompsalagg_fail_detach(struct lagg_softc *sc) 1454168793Sthompsa{ 1455168793Sthompsa return (0); 1456168793Sthompsa} 1457168793Sthompsa 1458168793Sthompsastatic int 1459168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m) 1460168793Sthompsa{ 1461168793Sthompsa struct lagg_port *lp; 1462168793Sthompsa 1463168793Sthompsa /* Use the master port if active or the next available port */ 1464172554Sthompsa if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) { 1465172554Sthompsa m_freem(m); 1466168793Sthompsa return (ENOENT); 1467172554Sthompsa } 1468168793Sthompsa 1469168793Sthompsa /* Send mbuf */ 1470168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1471168793Sthompsa} 1472168793Sthompsa 1473168793Sthompsastatic struct mbuf * 1474168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1475168793Sthompsa{ 1476168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1477168793Sthompsa struct lagg_port *tmp_tp; 1478168793Sthompsa 1479168793Sthompsa if (lp == sc->sc_primary) { 1480168793Sthompsa m->m_pkthdr.rcvif = ifp; 1481168793Sthompsa return (m); 1482168793Sthompsa } 1483168793Sthompsa 1484174742Sthompsa if (!LAGG_PORTACTIVE(sc->sc_primary)) { 1485174742Sthompsa tmp_tp = lagg_link_active(sc, sc->sc_primary); 1486168793Sthompsa /* 1487168793Sthompsa * If tmp_tp is null, we've recieved a packet when all 1488168793Sthompsa * our links are down. Weird, but process it anyways. 1489168793Sthompsa */ 1490168793Sthompsa if ((tmp_tp == NULL || tmp_tp == lp)) { 1491168793Sthompsa m->m_pkthdr.rcvif = ifp; 1492168793Sthompsa return (m); 1493168793Sthompsa } 1494168793Sthompsa } 1495168793Sthompsa 1496168793Sthompsa m_freem(m); 1497168793Sthompsa return (NULL); 1498168793Sthompsa} 1499168793Sthompsa 1500168793Sthompsa/* 1501168793Sthompsa * Loadbalancing 1502168793Sthompsa */ 1503168793Sthompsa 1504168793Sthompsastatic int 1505168793Sthompsalagg_lb_attach(struct lagg_softc *sc) 1506168793Sthompsa{ 1507168793Sthompsa struct lagg_port *lp; 1508168793Sthompsa struct lagg_lb *lb; 1509168793Sthompsa 1510168793Sthompsa if ((lb = (struct lagg_lb *)malloc(sizeof(struct lagg_lb), 1511168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 1512168793Sthompsa return (ENOMEM); 1513168793Sthompsa 1514168793Sthompsa sc->sc_detach = lagg_lb_detach; 1515168793Sthompsa sc->sc_start = lagg_lb_start; 1516168793Sthompsa sc->sc_input = lagg_lb_input; 1517168793Sthompsa sc->sc_port_create = lagg_lb_port_create; 1518168793Sthompsa sc->sc_port_destroy = lagg_lb_port_destroy; 1519168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1520168793Sthompsa 1521168793Sthompsa lb->lb_key = arc4random(); 1522168793Sthompsa sc->sc_psc = (caddr_t)lb; 1523168793Sthompsa 1524168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1525168793Sthompsa lagg_lb_port_create(lp); 1526168793Sthompsa 1527168793Sthompsa return (0); 1528168793Sthompsa} 1529168793Sthompsa 1530168793Sthompsastatic int 1531168793Sthompsalagg_lb_detach(struct lagg_softc *sc) 1532168793Sthompsa{ 1533168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1534168793Sthompsa if (lb != NULL) 1535168793Sthompsa free(lb, M_DEVBUF); 1536168793Sthompsa return (0); 1537168793Sthompsa} 1538168793Sthompsa 1539168793Sthompsastatic int 1540168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp) 1541168793Sthompsa{ 1542168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1543168793Sthompsa struct lagg_port *lp_next; 1544168793Sthompsa int i = 0; 1545168793Sthompsa 1546168793Sthompsa bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 1547168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1548168793Sthompsa if (lp_next == lp) 1549168793Sthompsa continue; 1550168793Sthompsa if (i >= LAGG_MAX_PORTS) 1551168793Sthompsa return (EINVAL); 1552168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1553168793Sthompsa printf("%s: port %s at index %d\n", 1554168793Sthompsa sc->sc_ifname, lp_next->lp_ifname, i); 1555168793Sthompsa lb->lb_ports[i++] = lp_next; 1556168793Sthompsa } 1557168793Sthompsa 1558168793Sthompsa return (0); 1559168793Sthompsa} 1560168793Sthompsa 1561168793Sthompsastatic int 1562168793Sthompsalagg_lb_port_create(struct lagg_port *lp) 1563168793Sthompsa{ 1564170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1565168793Sthompsa return (lagg_lb_porttable(sc, NULL)); 1566168793Sthompsa} 1567168793Sthompsa 1568168793Sthompsastatic void 1569168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp) 1570168793Sthompsa{ 1571170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1572168793Sthompsa lagg_lb_porttable(sc, lp); 1573168793Sthompsa} 1574168793Sthompsa 1575168793Sthompsastatic int 1576168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m) 1577168793Sthompsa{ 1578168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1579168793Sthompsa struct lagg_port *lp = NULL; 1580168793Sthompsa uint32_t p = 0; 1581168793Sthompsa 1582168793Sthompsa p = lagg_hashmbuf(m, lb->lb_key); 1583180249Sthompsa p %= sc->sc_count; 1584180249Sthompsa lp = lb->lb_ports[p]; 1585168793Sthompsa 1586168793Sthompsa /* 1587168793Sthompsa * Check the port's link state. This will return the next active 1588168793Sthompsa * port if the link is down or the port is NULL. 1589168793Sthompsa */ 1590172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1591172554Sthompsa m_freem(m); 1592168793Sthompsa return (ENOENT); 1593172554Sthompsa } 1594168793Sthompsa 1595168793Sthompsa /* Send mbuf */ 1596168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1597168793Sthompsa} 1598168793Sthompsa 1599168793Sthompsastatic struct mbuf * 1600168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1601168793Sthompsa{ 1602168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1603168793Sthompsa 1604168793Sthompsa /* Just pass in the packet to our lagg device */ 1605168793Sthompsa m->m_pkthdr.rcvif = ifp; 1606168793Sthompsa 1607168793Sthompsa return (m); 1608168793Sthompsa} 1609168793Sthompsa 1610168793Sthompsa/* 1611168793Sthompsa * 802.3ad LACP 1612168793Sthompsa */ 1613168793Sthompsa 1614168793Sthompsastatic int 1615168793Sthompsalagg_lacp_attach(struct lagg_softc *sc) 1616168793Sthompsa{ 1617168793Sthompsa struct lagg_port *lp; 1618168793Sthompsa int error; 1619168793Sthompsa 1620168793Sthompsa sc->sc_detach = lagg_lacp_detach; 1621168793Sthompsa sc->sc_port_create = lacp_port_create; 1622168793Sthompsa sc->sc_port_destroy = lacp_port_destroy; 1623168793Sthompsa sc->sc_linkstate = lacp_linkstate; 1624168793Sthompsa sc->sc_start = lagg_lacp_start; 1625168793Sthompsa sc->sc_input = lagg_lacp_input; 1626168793Sthompsa sc->sc_init = lacp_init; 1627168793Sthompsa sc->sc_stop = lacp_stop; 1628168793Sthompsa sc->sc_lladdr = lagg_lacp_lladdr; 1629171247Sthompsa sc->sc_req = lacp_req; 1630171247Sthompsa sc->sc_portreq = lacp_portreq; 1631168793Sthompsa 1632168793Sthompsa error = lacp_attach(sc); 1633168793Sthompsa if (error) 1634168793Sthompsa return (error); 1635168793Sthompsa 1636168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1637168793Sthompsa lacp_port_create(lp); 1638168793Sthompsa 1639168793Sthompsa return (error); 1640168793Sthompsa} 1641168793Sthompsa 1642168793Sthompsastatic int 1643168793Sthompsalagg_lacp_detach(struct lagg_softc *sc) 1644168793Sthompsa{ 1645168793Sthompsa struct lagg_port *lp; 1646168793Sthompsa int error; 1647168793Sthompsa 1648168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1649168793Sthompsa lacp_port_destroy(lp); 1650168793Sthompsa 1651168793Sthompsa /* unlocking is safe here */ 1652169569Sthompsa LAGG_WUNLOCK(sc); 1653168793Sthompsa error = lacp_detach(sc); 1654169569Sthompsa LAGG_WLOCK(sc); 1655168793Sthompsa 1656168793Sthompsa return (error); 1657168793Sthompsa} 1658168793Sthompsa 1659168793Sthompsastatic void 1660168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc) 1661168793Sthompsa{ 1662168793Sthompsa struct lagg_port *lp; 1663168793Sthompsa 1664168793Sthompsa /* purge all the lacp ports */ 1665168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1666168793Sthompsa lacp_port_destroy(lp); 1667168793Sthompsa 1668168793Sthompsa /* add them back in */ 1669168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1670168793Sthompsa lacp_port_create(lp); 1671168793Sthompsa} 1672168793Sthompsa 1673168793Sthompsastatic int 1674168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m) 1675168793Sthompsa{ 1676168793Sthompsa struct lagg_port *lp; 1677168793Sthompsa 1678168793Sthompsa lp = lacp_select_tx_port(sc, m); 1679172554Sthompsa if (lp == NULL) { 1680172554Sthompsa m_freem(m); 1681168793Sthompsa return (EBUSY); 1682172554Sthompsa } 1683168793Sthompsa 1684168793Sthompsa /* Send mbuf */ 1685168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1686168793Sthompsa} 1687168793Sthompsa 1688168793Sthompsastatic struct mbuf * 1689168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1690168793Sthompsa{ 1691168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1692168793Sthompsa struct ether_header *eh; 1693168793Sthompsa u_short etype; 1694168793Sthompsa 1695168793Sthompsa eh = mtod(m, struct ether_header *); 1696168793Sthompsa etype = ntohs(eh->ether_type); 1697168793Sthompsa 1698168793Sthompsa /* Tap off LACP control messages */ 1699168793Sthompsa if (etype == ETHERTYPE_SLOW) { 1700175005Sthompsa m = lacp_input(lp, m); 1701175005Sthompsa if (m == NULL) 1702175005Sthompsa return (NULL); 1703168793Sthompsa } 1704168793Sthompsa 1705168793Sthompsa /* 1706168793Sthompsa * If the port is not collecting or not in the active aggregator then 1707168793Sthompsa * free and return. 1708168793Sthompsa */ 1709177274Sthompsa if (lacp_iscollecting(lp) == 0 || lacp_isactive(lp) == 0) { 1710168793Sthompsa m_freem(m); 1711168793Sthompsa return (NULL); 1712168793Sthompsa } 1713168793Sthompsa 1714168793Sthompsa m->m_pkthdr.rcvif = ifp; 1715168793Sthompsa return (m); 1716168793Sthompsa} 1717