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: stable/10/sys/net/if_lagg.c 362368 2020-06-19 07:07:10Z rpokala $"); 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> 40255038Sadrian#include <sys/rmlock.h> 41169329Sthompsa#include <sys/taskqueue.h> 42203548Seri#include <sys/eventhandler.h> 43168793Sthompsa 44168793Sthompsa#include <net/ethernet.h> 45168793Sthompsa#include <net/if.h> 46168793Sthompsa#include <net/if_clone.h> 47168793Sthompsa#include <net/if_arp.h> 48168793Sthompsa#include <net/if_dl.h> 49168793Sthompsa#include <net/if_llc.h> 50168793Sthompsa#include <net/if_media.h> 51168793Sthompsa#include <net/if_types.h> 52168793Sthompsa#include <net/if_var.h> 53168793Sthompsa#include <net/bpf.h> 54287723Shrs#include <net/vnet.h> 55168793Sthompsa 56221130Sbz#if defined(INET) || defined(INET6) 57221130Sbz#include <netinet/in.h> 58265412Srmacklem#include <netinet/ip.h> 59221130Sbz#endif 60168793Sthompsa#ifdef INET 61168793Sthompsa#include <netinet/in_systm.h> 62168793Sthompsa#include <netinet/if_ether.h> 63168793Sthompsa#endif 64168793Sthompsa 65168793Sthompsa#ifdef INET6 66168793Sthompsa#include <netinet/ip6.h> 67252511Shrs#include <netinet6/in6_var.h> 68252511Shrs#include <netinet6/in6_ifattach.h> 69168793Sthompsa#endif 70168793Sthompsa 71168793Sthompsa#include <net/if_vlan_var.h> 72168793Sthompsa#include <net/if_lagg.h> 73168793Sthompsa#include <net/ieee8023ad_lacp.h> 74168793Sthompsa 75168793Sthompsa/* Special flags we should propagate to the lagg ports. */ 76168793Sthompsastatic struct { 77168793Sthompsa int flag; 78168793Sthompsa int (*func)(struct ifnet *, int); 79168793Sthompsa} lagg_pflags[] = { 80168793Sthompsa {IFF_PROMISC, ifpromisc}, 81168793Sthompsa {IFF_ALLMULTI, if_allmulti}, 82168793Sthompsa {0, NULL} 83168793Sthompsa}; 84168793Sthompsa 85287723ShrsVNET_DEFINE(SLIST_HEAD(__trhead, lagg_softc), lagg_list); /* list of laggs */ 86287723Shrs#define V_lagg_list VNET(lagg_list) 87287723Shrsstatic VNET_DEFINE(struct mtx, lagg_list_mtx); 88287723Shrs#define V_lagg_list_mtx VNET(lagg_list_mtx) 89287723Shrs#define LAGG_LIST_LOCK_INIT(x) mtx_init(&V_lagg_list_mtx, \ 90287723Shrs "if_lagg list", NULL, MTX_DEF) 91287723Shrs#define LAGG_LIST_LOCK_DESTROY(x) mtx_destroy(&V_lagg_list_mtx) 92287723Shrs#define LAGG_LIST_LOCK(x) mtx_lock(&V_lagg_list_mtx) 93287723Shrs#define LAGG_LIST_UNLOCK(x) mtx_unlock(&V_lagg_list_mtx) 94168793Sthompsaeventhandler_tag lagg_detach_cookie = NULL; 95168793Sthompsa 96168793Sthompsastatic int lagg_clone_create(struct if_clone *, int, caddr_t); 97168793Sthompsastatic void lagg_clone_destroy(struct ifnet *); 98287723Shrsstatic VNET_DEFINE(struct if_clone *, lagg_cloner); 99287723Shrs#define V_lagg_cloner VNET(lagg_cloner) 100241610Sglebiusstatic const char laggname[] = "lagg"; 101241610Sglebius 102168793Sthompsastatic void lagg_lladdr(struct lagg_softc *, uint8_t *); 103171661Sthompsastatic void lagg_capabilities(struct lagg_softc *); 104168793Sthompsastatic void lagg_port_lladdr(struct lagg_port *, uint8_t *); 105169329Sthompsastatic void lagg_port_setlladdr(void *, int); 106168793Sthompsastatic int lagg_port_create(struct lagg_softc *, struct ifnet *); 107168793Sthompsastatic int lagg_port_destroy(struct lagg_port *, int); 108168793Sthompsastatic struct mbuf *lagg_input(struct ifnet *, struct mbuf *); 109173895Sthompsastatic void lagg_linkstate(struct lagg_softc *); 110168793Sthompsastatic void lagg_port_state(struct ifnet *, int); 111168793Sthompsastatic int lagg_port_ioctl(struct ifnet *, u_long, caddr_t); 112168793Sthompsastatic int lagg_port_output(struct ifnet *, struct mbuf *, 113249925Sglebius const struct sockaddr *, struct route *); 114168793Sthompsastatic void lagg_port_ifdetach(void *arg __unused, struct ifnet *); 115201803Strasz#ifdef LAGG_PORT_STACKING 116168793Sthompsastatic int lagg_port_checkstacking(struct lagg_softc *); 117201803Strasz#endif 118168793Sthompsastatic void lagg_port2req(struct lagg_port *, struct lagg_reqport *); 119168793Sthompsastatic void lagg_init(void *); 120168793Sthompsastatic void lagg_stop(struct lagg_softc *); 121168793Sthompsastatic int lagg_ioctl(struct ifnet *, u_long, caddr_t); 122168793Sthompsastatic int lagg_ether_setmulti(struct lagg_softc *); 123168793Sthompsastatic int lagg_ether_cmdmulti(struct lagg_port *, int); 124168793Sthompsastatic int lagg_setflag(struct lagg_port *, int, int, 125168793Sthompsa int (*func)(struct ifnet *, int)); 126168793Sthompsastatic int lagg_setflags(struct lagg_port *, int status); 127240742Sglebiusstatic int lagg_transmit(struct ifnet *, struct mbuf *); 128240742Sglebiusstatic void lagg_qflush(struct ifnet *); 129168793Sthompsastatic int lagg_media_change(struct ifnet *); 130168793Sthompsastatic void lagg_media_status(struct ifnet *, struct ifmediareq *); 131168793Sthompsastatic struct lagg_port *lagg_link_active(struct lagg_softc *, 132168793Sthompsa struct lagg_port *); 133168793Sthompsastatic const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *); 134168793Sthompsa 135168793Sthompsa/* Simple round robin */ 136287723Shrsstatic void lagg_rr_attach(struct lagg_softc *); 137168793Sthompsastatic int lagg_rr_detach(struct lagg_softc *); 138168793Sthompsastatic int lagg_rr_start(struct lagg_softc *, struct mbuf *); 139168793Sthompsastatic struct mbuf *lagg_rr_input(struct lagg_softc *, struct lagg_port *, 140168793Sthompsa struct mbuf *); 141168793Sthompsa 142168793Sthompsa/* Active failover */ 143287723Shrsstatic void lagg_fail_attach(struct lagg_softc *); 144168793Sthompsastatic int lagg_fail_detach(struct lagg_softc *); 145168793Sthompsastatic int lagg_fail_start(struct lagg_softc *, struct mbuf *); 146168793Sthompsastatic struct mbuf *lagg_fail_input(struct lagg_softc *, struct lagg_port *, 147168793Sthompsa struct mbuf *); 148168793Sthompsa 149168793Sthompsa/* Loadbalancing */ 150287723Shrsstatic void lagg_lb_attach(struct lagg_softc *); 151168793Sthompsastatic int lagg_lb_detach(struct lagg_softc *); 152168793Sthompsastatic int lagg_lb_port_create(struct lagg_port *); 153168793Sthompsastatic void lagg_lb_port_destroy(struct lagg_port *); 154168793Sthompsastatic int lagg_lb_start(struct lagg_softc *, struct mbuf *); 155168793Sthompsastatic struct mbuf *lagg_lb_input(struct lagg_softc *, struct lagg_port *, 156168793Sthompsa struct mbuf *); 157168793Sthompsastatic int lagg_lb_porttable(struct lagg_softc *, struct lagg_port *); 158168793Sthompsa 159168793Sthompsa/* 802.3ad LACP */ 160287723Shrsstatic void lagg_lacp_attach(struct lagg_softc *); 161168793Sthompsastatic int lagg_lacp_detach(struct lagg_softc *); 162168793Sthompsastatic int lagg_lacp_start(struct lagg_softc *, struct mbuf *); 163168793Sthompsastatic struct mbuf *lagg_lacp_input(struct lagg_softc *, struct lagg_port *, 164168793Sthompsa struct mbuf *); 165168793Sthompsastatic void lagg_lacp_lladdr(struct lagg_softc *); 166168793Sthompsa 167249506Sglebiusstatic void lagg_callout(void *); 168249506Sglebius 169168793Sthompsa/* lagg protocol table */ 170287723Shrsstatic const struct lagg_proto { 171287723Shrs lagg_proto ti_proto; 172287723Shrs void (*ti_attach)(struct lagg_softc *); 173168793Sthompsa} lagg_protos[] = { 174168793Sthompsa { LAGG_PROTO_ROUNDROBIN, lagg_rr_attach }, 175168793Sthompsa { LAGG_PROTO_FAILOVER, lagg_fail_attach }, 176168793Sthompsa { LAGG_PROTO_LOADBALANCE, lagg_lb_attach }, 177168793Sthompsa { LAGG_PROTO_ETHERCHANNEL, lagg_lb_attach }, 178168793Sthompsa { LAGG_PROTO_LACP, lagg_lacp_attach }, 179168793Sthompsa { LAGG_PROTO_NONE, NULL } 180168793Sthompsa}; 181168793Sthompsa 182212100SemasteSYSCTL_DECL(_net_link); 183253314SadrianSYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0, 184227309Sed "Link Aggregation"); 185212100Semaste 186287723Shrs/* Allow input on any failover links */ 187287723Shrsstatic VNET_DEFINE(int, lagg_failover_rx_all); 188287723Shrs#define V_lagg_failover_rx_all VNET(lagg_failover_rx_all) 189287723ShrsSYSCTL_INT(_net_link_lagg, OID_AUTO, failover_rx_all, CTLFLAG_RW | CTLFLAG_VNET, 190287723Shrs &VNET_NAME(lagg_failover_rx_all), 0, 191212100Semaste "Accept input from any interface in a failover lagg"); 192287723Shrs 193287723Shrs/* Default value for using M_FLOWID */ 194287723Shrsstatic VNET_DEFINE(int, def_use_flowid) = 1; 195287723Shrs#define V_def_use_flowid VNET(def_use_flowid) 196287723ShrsSYSCTL_INT(_net_link_lagg, OID_AUTO, default_use_flowid, CTLFLAG_RWTUN, 197287723Shrs &VNET_NAME(def_use_flowid), 0, 198232080Sthompsa "Default setting for using flow id for load sharing"); 199287723Shrs 200287723Shrs/* Default value for using M_FLOWID */ 201287723Shrsstatic VNET_DEFINE(int, def_flowid_shift) = 16; 202287723Shrs#define V_def_flowid_shift VNET(def_flowid_shift) 203287723ShrsSYSCTL_INT(_net_link_lagg, OID_AUTO, default_flowid_shift, CTLFLAG_RWTUN, 204287723Shrs &VNET_NAME(def_flowid_shift), 0, 205260179Sscottl "Default setting for flowid shift for load sharing"); 206212100Semaste 207287723Shrsstatic void 208287723Shrsvnet_lagg_init(const void *unused __unused) 209287723Shrs{ 210287723Shrs 211287723Shrs LAGG_LIST_LOCK_INIT(); 212287723Shrs SLIST_INIT(&V_lagg_list); 213287723Shrs V_lagg_cloner = if_clone_simple(laggname, lagg_clone_create, 214287723Shrs lagg_clone_destroy, 0); 215287723Shrs} 216287723ShrsVNET_SYSINIT(vnet_lagg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 217287723Shrs vnet_lagg_init, NULL); 218287723Shrs 219287723Shrsstatic void 220287723Shrsvnet_lagg_uninit(const void *unused __unused) 221287723Shrs{ 222287723Shrs 223287723Shrs if_clone_detach(V_lagg_cloner); 224287723Shrs LAGG_LIST_LOCK_DESTROY(); 225287723Shrs} 226287723ShrsVNET_SYSUNINIT(vnet_lagg_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 227287723Shrs vnet_lagg_uninit, NULL); 228287723Shrs 229168793Sthompsastatic int 230168793Sthompsalagg_modevent(module_t mod, int type, void *data) 231168793Sthompsa{ 232168793Sthompsa 233168793Sthompsa switch (type) { 234168793Sthompsa case MOD_LOAD: 235168793Sthompsa lagg_input_p = lagg_input; 236168793Sthompsa lagg_linkstate_p = lagg_port_state; 237168793Sthompsa lagg_detach_cookie = EVENTHANDLER_REGISTER( 238168793Sthompsa ifnet_departure_event, lagg_port_ifdetach, NULL, 239168793Sthompsa EVENTHANDLER_PRI_ANY); 240168793Sthompsa break; 241168793Sthompsa case MOD_UNLOAD: 242168793Sthompsa EVENTHANDLER_DEREGISTER(ifnet_departure_event, 243168793Sthompsa lagg_detach_cookie); 244168793Sthompsa lagg_input_p = NULL; 245168793Sthompsa lagg_linkstate_p = NULL; 246168793Sthompsa break; 247168793Sthompsa default: 248168793Sthompsa return (EOPNOTSUPP); 249168793Sthompsa } 250168793Sthompsa return (0); 251168793Sthompsa} 252168793Sthompsa 253168793Sthompsastatic moduledata_t lagg_mod = { 254168793Sthompsa "if_lagg", 255168793Sthompsa lagg_modevent, 256241394Skevlo 0 257168793Sthompsa}; 258168793Sthompsa 259168793SthompsaDECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 260224571SpluknetMODULE_VERSION(if_lagg, 1); 261168793Sthompsa 262203548Seri/* 263203548Seri * This routine is run via an vlan 264203548Seri * config EVENT 265203548Seri */ 266203548Seristatic void 267203548Serilagg_register_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) 268203548Seri{ 269203548Seri struct lagg_softc *sc = ifp->if_softc; 270203548Seri struct lagg_port *lp; 271255038Sadrian struct rm_priotracker tracker; 272203548Seri 273203548Seri if (ifp->if_softc != arg) /* Not our event */ 274203548Seri return; 275203548Seri 276255038Sadrian LAGG_RLOCK(sc, &tracker); 277203548Seri if (!SLIST_EMPTY(&sc->sc_ports)) { 278203548Seri SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 279203548Seri EVENTHANDLER_INVOKE(vlan_config, lp->lp_ifp, vtag); 280203548Seri } 281255038Sadrian LAGG_RUNLOCK(sc, &tracker); 282203548Seri} 283203548Seri 284203548Seri/* 285203548Seri * This routine is run via an vlan 286203548Seri * unconfig EVENT 287203548Seri */ 288203548Seristatic void 289203548Serilagg_unregister_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) 290203548Seri{ 291203548Seri struct lagg_softc *sc = ifp->if_softc; 292203548Seri struct lagg_port *lp; 293255038Sadrian struct rm_priotracker tracker; 294203548Seri 295203548Seri if (ifp->if_softc != arg) /* Not our event */ 296203548Seri return; 297203548Seri 298255038Sadrian LAGG_RLOCK(sc, &tracker); 299203548Seri if (!SLIST_EMPTY(&sc->sc_ports)) { 300203548Seri SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 301203548Seri EVENTHANDLER_INVOKE(vlan_unconfig, lp->lp_ifp, vtag); 302203548Seri } 303255038Sadrian LAGG_RUNLOCK(sc, &tracker); 304203548Seri} 305203548Seri 306168793Sthompsastatic int 307168793Sthompsalagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) 308168793Sthompsa{ 309168793Sthompsa struct lagg_softc *sc; 310168793Sthompsa struct ifnet *ifp; 311168793Sthompsa static const u_char eaddr[6]; /* 00:00:00:00:00:00 */ 312287723Shrs int i; 313168793Sthompsa 314168793Sthompsa sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 315168793Sthompsa ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 316168793Sthompsa if (ifp == NULL) { 317168793Sthompsa free(sc, M_DEVBUF); 318168793Sthompsa return (ENOSPC); 319168793Sthompsa } 320168793Sthompsa 321249506Sglebius sc->sc_ipackets = counter_u64_alloc(M_WAITOK); 322249506Sglebius sc->sc_opackets = counter_u64_alloc(M_WAITOK); 323249506Sglebius sc->sc_ibytes = counter_u64_alloc(M_WAITOK); 324249506Sglebius sc->sc_obytes = counter_u64_alloc(M_WAITOK); 325249506Sglebius 326287723Shrs if (V_def_use_flowid) 327287723Shrs sc->sc_opts |= LAGG_OPT_USE_FLOWID; 328287723Shrs sc->flowid_shift = V_def_flowid_shift; 329287723Shrs 330232629Sthompsa /* Hash all layers by default */ 331232629Sthompsa sc->sc_flags = LAGG_F_HASHL2|LAGG_F_HASHL3|LAGG_F_HASHL4; 332232008Sthompsa 333168793Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 334168793Sthompsa for (i = 0; lagg_protos[i].ti_proto != LAGG_PROTO_NONE; i++) { 335168793Sthompsa if (lagg_protos[i].ti_proto == LAGG_PROTO_DEFAULT) { 336168793Sthompsa sc->sc_proto = lagg_protos[i].ti_proto; 337287723Shrs lagg_protos[i].ti_attach(sc); 338168793Sthompsa break; 339168793Sthompsa } 340168793Sthompsa } 341168793Sthompsa LAGG_LOCK_INIT(sc); 342255038Sadrian LAGG_CALLOUT_LOCK_INIT(sc); 343168793Sthompsa SLIST_INIT(&sc->sc_ports); 344169329Sthompsa TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc); 345168793Sthompsa 346255038Sadrian /* 347255038Sadrian * This uses the callout lock rather than the rmlock; one can't 348255038Sadrian * hold said rmlock during SWI. 349255038Sadrian */ 350255038Sadrian callout_init_mtx(&sc->sc_callout, &sc->sc_call_mtx, 0); 351255038Sadrian 352168793Sthompsa /* Initialise pseudo media types */ 353168793Sthompsa ifmedia_init(&sc->sc_media, 0, lagg_media_change, 354168793Sthompsa lagg_media_status); 355168793Sthompsa ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 356168793Sthompsa ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 357168793Sthompsa 358241610Sglebius if_initname(ifp, laggname, unit); 359168793Sthompsa ifp->if_softc = sc; 360240742Sglebius ifp->if_transmit = lagg_transmit; 361240742Sglebius ifp->if_qflush = lagg_qflush; 362168793Sthompsa ifp->if_init = lagg_init; 363168793Sthompsa ifp->if_ioctl = lagg_ioctl; 364168793Sthompsa ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 365256218Sglebius ifp->if_capenable = ifp->if_capabilities = IFCAP_HWSTATS; 366168793Sthompsa 367168793Sthompsa /* 368227459Sbrooks * Attach as an ordinary ethernet device, children will be attached 369168793Sthompsa * as special device IFT_IEEE8023ADLAG. 370168793Sthompsa */ 371168793Sthompsa ether_ifattach(ifp, eaddr); 372168793Sthompsa 373203548Seri sc->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, 374203548Seri lagg_register_vlan, sc, EVENTHANDLER_PRI_FIRST); 375203548Seri sc->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, 376203548Seri lagg_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST); 377203548Seri 378168793Sthompsa /* Insert into the global list of laggs */ 379287723Shrs LAGG_LIST_LOCK(); 380287723Shrs SLIST_INSERT_HEAD(&V_lagg_list, sc, sc_entries); 381287723Shrs LAGG_LIST_UNLOCK(); 382168793Sthompsa 383249506Sglebius callout_reset(&sc->sc_callout, hz, lagg_callout, sc); 384249506Sglebius 385168793Sthompsa return (0); 386168793Sthompsa} 387168793Sthompsa 388168793Sthompsastatic void 389168793Sthompsalagg_clone_destroy(struct ifnet *ifp) 390168793Sthompsa{ 391168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 392168793Sthompsa struct lagg_port *lp; 393168793Sthompsa 394169569Sthompsa LAGG_WLOCK(sc); 395168793Sthompsa 396168793Sthompsa lagg_stop(sc); 397168793Sthompsa ifp->if_flags &= ~IFF_UP; 398168793Sthompsa 399203548Seri EVENTHANDLER_DEREGISTER(vlan_config, sc->vlan_attach); 400203548Seri EVENTHANDLER_DEREGISTER(vlan_unconfig, sc->vlan_detach); 401203548Seri 402168793Sthompsa /* Shutdown and remove lagg ports */ 403168793Sthompsa while ((lp = SLIST_FIRST(&sc->sc_ports)) != NULL) 404168793Sthompsa lagg_port_destroy(lp, 1); 405168793Sthompsa /* Unhook the aggregation protocol */ 406219275Seri if (sc->sc_detach != NULL) 407219275Seri (*sc->sc_detach)(sc); 408287723Shrs else 409287723Shrs LAGG_WUNLOCK(sc); 410168793Sthompsa 411168793Sthompsa ifmedia_removeall(&sc->sc_media); 412168793Sthompsa ether_ifdetach(ifp); 413227459Sbrooks if_free(ifp); 414168793Sthompsa 415255038Sadrian /* This grabs sc_callout_mtx, serialising it correctly */ 416249506Sglebius callout_drain(&sc->sc_callout); 417255038Sadrian 418255038Sadrian /* At this point it's drained; we can free this */ 419249506Sglebius counter_u64_free(sc->sc_ipackets); 420249506Sglebius counter_u64_free(sc->sc_opackets); 421249506Sglebius counter_u64_free(sc->sc_ibytes); 422249506Sglebius counter_u64_free(sc->sc_obytes); 423249506Sglebius 424287723Shrs LAGG_LIST_LOCK(); 425287723Shrs SLIST_REMOVE(&V_lagg_list, sc, lagg_softc, sc_entries); 426287723Shrs LAGG_LIST_UNLOCK(); 427168793Sthompsa 428169329Sthompsa taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task); 429168793Sthompsa LAGG_LOCK_DESTROY(sc); 430255038Sadrian LAGG_CALLOUT_LOCK_DESTROY(sc); 431168793Sthompsa free(sc, M_DEVBUF); 432168793Sthompsa} 433168793Sthompsa 434168793Sthompsastatic void 435168793Sthompsalagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr) 436168793Sthompsa{ 437168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 438287723Shrs struct lagg_port lp; 439168793Sthompsa 440168793Sthompsa if (memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0) 441168793Sthompsa return; 442168793Sthompsa 443287723Shrs LAGG_WLOCK_ASSERT(sc); 444287723Shrs /* 445287723Shrs * Set the link layer address on the lagg interface. 446287723Shrs * sc_lladdr() notifies the MAC change to 447287723Shrs * the aggregation protocol. iflladdr_event handler which 448287723Shrs * may trigger gratuitous ARPs for INET will be handled in 449287723Shrs * a taskqueue. 450287723Shrs */ 451168793Sthompsa bcopy(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN); 452168793Sthompsa if (sc->sc_lladdr != NULL) 453168793Sthompsa (*sc->sc_lladdr)(sc); 454287723Shrs 455287723Shrs bzero(&lp, sizeof(lp)); 456287723Shrs lp.lp_ifp = sc->sc_ifp; 457287723Shrs lp.lp_softc = sc; 458287723Shrs 459287723Shrs lagg_port_lladdr(&lp, lladdr); 460168793Sthompsa} 461168793Sthompsa 462171661Sthompsastatic void 463168793Sthompsalagg_capabilities(struct lagg_softc *sc) 464168793Sthompsa{ 465168793Sthompsa struct lagg_port *lp; 466171661Sthompsa int cap = ~0, ena = ~0; 467186195Sthompsa u_long hwa = ~0UL; 468274043Shselasky struct ifnet_hw_tsomax hw_tsomax; 469168793Sthompsa 470169569Sthompsa LAGG_WLOCK_ASSERT(sc); 471168793Sthompsa 472274043Shselasky memset(&hw_tsomax, 0, sizeof(hw_tsomax)); 473274043Shselasky 474168793Sthompsa /* Get capabilities from the lagg ports */ 475171661Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 476171661Sthompsa cap &= lp->lp_ifp->if_capabilities; 477171661Sthompsa ena &= lp->lp_ifp->if_capenable; 478186195Sthompsa hwa &= lp->lp_ifp->if_hwassist; 479274043Shselasky if_hw_tsomax_common(lp->lp_ifp, &hw_tsomax); 480171661Sthompsa } 481171661Sthompsa cap = (cap == ~0 ? 0 : cap); 482171661Sthompsa ena = (ena == ~0 ? 0 : ena); 483186195Sthompsa hwa = (hwa == ~0 ? 0 : hwa); 484168793Sthompsa 485171661Sthompsa if (sc->sc_ifp->if_capabilities != cap || 486186195Sthompsa sc->sc_ifp->if_capenable != ena || 487265412Srmacklem sc->sc_ifp->if_hwassist != hwa || 488274043Shselasky if_hw_tsomax_update(sc->sc_ifp, &hw_tsomax) != 0) { 489171661Sthompsa sc->sc_ifp->if_capabilities = cap; 490171661Sthompsa sc->sc_ifp->if_capenable = ena; 491186195Sthompsa sc->sc_ifp->if_hwassist = hwa; 492171661Sthompsa getmicrotime(&sc->sc_ifp->if_lastchange); 493171661Sthompsa 494171661Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 495171661Sthompsa if_printf(sc->sc_ifp, 496171661Sthompsa "capabilities 0x%08x enabled 0x%08x\n", cap, ena); 497168793Sthompsa } 498168793Sthompsa} 499168793Sthompsa 500168793Sthompsastatic void 501168793Sthompsalagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr) 502168793Sthompsa{ 503170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 504168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 505169329Sthompsa struct lagg_llq *llq; 506169329Sthompsa int pending = 0; 507287723Shrs int primary; 508168793Sthompsa 509169569Sthompsa LAGG_WLOCK_ASSERT(sc); 510169329Sthompsa 511287723Shrs primary = (sc->sc_primary->lp_ifp == ifp) ? 1 : 0; 512287723Shrs if (primary == 0 && (lp->lp_detaching || 513287723Shrs memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0)) 514168793Sthompsa return; 515168793Sthompsa 516169329Sthompsa /* Check to make sure its not already queued to be changed */ 517169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 518169329Sthompsa if (llq->llq_ifp == ifp) { 519169329Sthompsa pending = 1; 520169329Sthompsa break; 521169329Sthompsa } 522169329Sthompsa } 523168793Sthompsa 524169329Sthompsa if (!pending) { 525169329Sthompsa llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT); 526169329Sthompsa if (llq == NULL) /* XXX what to do */ 527169329Sthompsa return; 528169329Sthompsa } 529169329Sthompsa 530169329Sthompsa /* Update the lladdr even if pending, it may have changed */ 531169329Sthompsa llq->llq_ifp = ifp; 532287723Shrs llq->llq_primary = primary; 533169329Sthompsa bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN); 534169329Sthompsa 535169329Sthompsa if (!pending) 536169329Sthompsa SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries); 537169329Sthompsa 538169329Sthompsa taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task); 539168793Sthompsa} 540168793Sthompsa 541169329Sthompsa/* 542169329Sthompsa * Set the interface MAC address from a taskqueue to avoid a LOR. 543169329Sthompsa */ 544169329Sthompsastatic void 545169329Sthompsalagg_port_setlladdr(void *arg, int pending) 546169329Sthompsa{ 547169329Sthompsa struct lagg_softc *sc = (struct lagg_softc *)arg; 548169329Sthompsa struct lagg_llq *llq, *head; 549169329Sthompsa struct ifnet *ifp; 550169329Sthompsa int error; 551169329Sthompsa 552169329Sthompsa /* Grab a local reference of the queue and remove it from the softc */ 553169569Sthompsa LAGG_WLOCK(sc); 554169329Sthompsa head = SLIST_FIRST(&sc->sc_llq_head); 555169329Sthompsa SLIST_FIRST(&sc->sc_llq_head) = NULL; 556169569Sthompsa LAGG_WUNLOCK(sc); 557169329Sthompsa 558169329Sthompsa /* 559169329Sthompsa * Traverse the queue and set the lladdr on each ifp. It is safe to do 560169329Sthompsa * unlocked as we have the only reference to it. 561169329Sthompsa */ 562169329Sthompsa for (llq = head; llq != NULL; llq = head) { 563169329Sthompsa ifp = llq->llq_ifp; 564169329Sthompsa 565251490Strociny CURVNET_SET(ifp->if_vnet); 566287723Shrs if (llq->llq_primary == 0) { 567287723Shrs /* 568287723Shrs * Set the link layer address on the laggport interface. 569287723Shrs * if_setlladdr() triggers gratuitous ARPs for INET. 570287723Shrs */ 571287723Shrs error = if_setlladdr(ifp, llq->llq_lladdr, 572287723Shrs ETHER_ADDR_LEN); 573287723Shrs if (error) 574287723Shrs printf("%s: setlladdr failed on %s\n", __func__, 575287723Shrs ifp->if_xname); 576287723Shrs } else 577287723Shrs EVENTHANDLER_INVOKE(iflladdr_event, ifp); 578251490Strociny CURVNET_RESTORE(); 579169329Sthompsa head = SLIST_NEXT(llq, llq_entries); 580169329Sthompsa free(llq, M_DEVBUF); 581169329Sthompsa } 582169329Sthompsa} 583169329Sthompsa 584168793Sthompsastatic int 585168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) 586168793Sthompsa{ 587168793Sthompsa struct lagg_softc *sc_ptr; 588272680Sae struct lagg_port *lp, *tlp; 589168793Sthompsa int error = 0; 590168793Sthompsa 591169569Sthompsa LAGG_WLOCK_ASSERT(sc); 592168793Sthompsa 593168793Sthompsa /* Limit the maximal number of lagg ports */ 594168793Sthompsa if (sc->sc_count >= LAGG_MAX_PORTS) 595168793Sthompsa return (ENOSPC); 596168793Sthompsa 597168793Sthompsa /* Check if port has already been associated to a lagg */ 598236178Srea if (ifp->if_lagg != NULL) { 599236178Srea /* Port is already in the current lagg? */ 600236178Srea lp = (struct lagg_port *)ifp->if_lagg; 601236178Srea if (lp->lp_softc == sc) 602236178Srea return (EEXIST); 603168793Sthompsa return (EBUSY); 604236178Srea } 605168793Sthompsa 606168793Sthompsa /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 607168793Sthompsa if (ifp->if_type != IFT_ETHER) 608168793Sthompsa return (EPROTONOSUPPORT); 609168793Sthompsa 610171661Sthompsa /* Allow the first Ethernet member to define the MTU */ 611171661Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) 612171661Sthompsa sc->sc_ifp->if_mtu = ifp->if_mtu; 613171661Sthompsa else if (sc->sc_ifp->if_mtu != ifp->if_mtu) { 614171661Sthompsa if_printf(sc->sc_ifp, "invalid MTU for %s\n", 615171661Sthompsa ifp->if_xname); 616171661Sthompsa return (EINVAL); 617171661Sthompsa } 618171661Sthompsa 619168793Sthompsa if ((lp = malloc(sizeof(struct lagg_port), 620168793Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) 621168793Sthompsa return (ENOMEM); 622168793Sthompsa 623168793Sthompsa /* Check if port is a stacked lagg */ 624287723Shrs LAGG_LIST_LOCK(); 625287723Shrs SLIST_FOREACH(sc_ptr, &V_lagg_list, sc_entries) { 626168793Sthompsa if (ifp == sc_ptr->sc_ifp) { 627287723Shrs LAGG_LIST_UNLOCK(); 628168793Sthompsa free(lp, M_DEVBUF); 629168793Sthompsa return (EINVAL); 630201803Strasz /* XXX disable stacking for the moment, its untested */ 631201803Strasz#ifdef LAGG_PORT_STACKING 632168793Sthompsa lp->lp_flags |= LAGG_PORT_STACK; 633168793Sthompsa if (lagg_port_checkstacking(sc_ptr) >= 634168793Sthompsa LAGG_MAX_STACKING) { 635287723Shrs LAGG_LIST_UNLOCK(); 636168793Sthompsa free(lp, M_DEVBUF); 637168793Sthompsa return (E2BIG); 638168793Sthompsa } 639201803Strasz#endif 640168793Sthompsa } 641168793Sthompsa } 642287723Shrs LAGG_LIST_UNLOCK(); 643168793Sthompsa 644168793Sthompsa /* Change the interface type */ 645168793Sthompsa lp->lp_iftype = ifp->if_type; 646168793Sthompsa ifp->if_type = IFT_IEEE8023ADLAG; 647168793Sthompsa ifp->if_lagg = lp; 648168793Sthompsa lp->lp_ioctl = ifp->if_ioctl; 649168793Sthompsa ifp->if_ioctl = lagg_port_ioctl; 650168793Sthompsa lp->lp_output = ifp->if_output; 651168793Sthompsa ifp->if_output = lagg_port_output; 652168793Sthompsa 653168793Sthompsa lp->lp_ifp = ifp; 654170599Sthompsa lp->lp_softc = sc; 655168793Sthompsa 656168793Sthompsa /* Save port link layer address */ 657168793Sthompsa bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN); 658168793Sthompsa 659168793Sthompsa if (SLIST_EMPTY(&sc->sc_ports)) { 660168793Sthompsa sc->sc_primary = lp; 661168793Sthompsa lagg_lladdr(sc, IF_LLADDR(ifp)); 662168793Sthompsa } else { 663168793Sthompsa /* Update link layer address for this port */ 664168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp)); 665168793Sthompsa } 666168793Sthompsa 667277707Sae /* 668277707Sae * Insert into the list of ports. 669277707Sae * Keep ports sorted by if_index. It is handy, when configuration 670277707Sae * is predictable and `ifconfig laggN create ...` command 671277707Sae * will lead to the same result each time. 672277707Sae */ 673272680Sae SLIST_FOREACH(tlp, &sc->sc_ports, lp_entries) { 674272680Sae if (tlp->lp_ifp->if_index < ifp->if_index && ( 675272680Sae SLIST_NEXT(tlp, lp_entries) == NULL || 676277707Sae SLIST_NEXT(tlp, lp_entries)->lp_ifp->if_index > 677272680Sae ifp->if_index)) 678272680Sae break; 679272680Sae } 680272680Sae if (tlp != NULL) 681272680Sae SLIST_INSERT_AFTER(tlp, lp, lp_entries); 682272680Sae else 683272680Sae SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries); 684168793Sthompsa sc->sc_count++; 685168793Sthompsa 686168793Sthompsa /* Update lagg capabilities */ 687171661Sthompsa lagg_capabilities(sc); 688173895Sthompsa lagg_linkstate(sc); 689168793Sthompsa 690168793Sthompsa /* Add multicast addresses and interface flags to this port */ 691168793Sthompsa lagg_ether_cmdmulti(lp, 1); 692168793Sthompsa lagg_setflags(lp, 1); 693168793Sthompsa 694168793Sthompsa if (sc->sc_port_create != NULL) 695168793Sthompsa error = (*sc->sc_port_create)(lp); 696168793Sthompsa if (error) { 697170599Sthompsa /* remove the port again, without calling sc_port_destroy */ 698168793Sthompsa lagg_port_destroy(lp, 0); 699168793Sthompsa return (error); 700168793Sthompsa } 701168793Sthompsa 702168793Sthompsa return (error); 703168793Sthompsa} 704168793Sthompsa 705201803Strasz#ifdef LAGG_PORT_STACKING 706168793Sthompsastatic int 707168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc) 708168793Sthompsa{ 709168793Sthompsa struct lagg_softc *sc_ptr; 710168793Sthompsa struct lagg_port *lp; 711168793Sthompsa int m = 0; 712168793Sthompsa 713169569Sthompsa LAGG_WLOCK_ASSERT(sc); 714168793Sthompsa 715168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 716168793Sthompsa if (lp->lp_flags & LAGG_PORT_STACK) { 717168793Sthompsa sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc; 718168793Sthompsa m = MAX(m, lagg_port_checkstacking(sc_ptr)); 719168793Sthompsa } 720168793Sthompsa } 721168793Sthompsa 722168793Sthompsa return (m + 1); 723168793Sthompsa} 724201803Strasz#endif 725168793Sthompsa 726168793Sthompsastatic int 727168793Sthompsalagg_port_destroy(struct lagg_port *lp, int runpd) 728168793Sthompsa{ 729170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 730168793Sthompsa struct lagg_port *lp_ptr; 731169329Sthompsa struct lagg_llq *llq; 732168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 733168793Sthompsa 734169569Sthompsa LAGG_WLOCK_ASSERT(sc); 735168793Sthompsa 736168793Sthompsa if (runpd && sc->sc_port_destroy != NULL) 737168793Sthompsa (*sc->sc_port_destroy)(lp); 738168793Sthompsa 739169328Sthompsa /* 740169328Sthompsa * Remove multicast addresses and interface flags from this port and 741169328Sthompsa * reset the MAC address, skip if the interface is being detached. 742169328Sthompsa */ 743169328Sthompsa if (!lp->lp_detaching) { 744169328Sthompsa lagg_ether_cmdmulti(lp, 0); 745169328Sthompsa lagg_setflags(lp, 0); 746169328Sthompsa lagg_port_lladdr(lp, lp->lp_lladdr); 747169328Sthompsa } 748168793Sthompsa 749168793Sthompsa /* Restore interface */ 750168793Sthompsa ifp->if_type = lp->lp_iftype; 751168793Sthompsa ifp->if_ioctl = lp->lp_ioctl; 752168793Sthompsa ifp->if_output = lp->lp_output; 753168793Sthompsa ifp->if_lagg = NULL; 754168793Sthompsa 755168793Sthompsa /* Finally, remove the port from the lagg */ 756168793Sthompsa SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries); 757168793Sthompsa sc->sc_count--; 758168793Sthompsa 759168793Sthompsa /* Update the primary interface */ 760168793Sthompsa if (lp == sc->sc_primary) { 761168793Sthompsa uint8_t lladdr[ETHER_ADDR_LEN]; 762168793Sthompsa 763168793Sthompsa if ((lp_ptr = SLIST_FIRST(&sc->sc_ports)) == NULL) { 764168793Sthompsa bzero(&lladdr, ETHER_ADDR_LEN); 765168793Sthompsa } else { 766168793Sthompsa bcopy(lp_ptr->lp_lladdr, 767168793Sthompsa lladdr, ETHER_ADDR_LEN); 768168793Sthompsa } 769168793Sthompsa lagg_lladdr(sc, lladdr); 770168793Sthompsa sc->sc_primary = lp_ptr; 771168793Sthompsa 772168793Sthompsa /* Update link layer address for each port */ 773168793Sthompsa SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries) 774168793Sthompsa lagg_port_lladdr(lp_ptr, lladdr); 775168793Sthompsa } 776168793Sthompsa 777169329Sthompsa /* Remove any pending lladdr changes from the queue */ 778169329Sthompsa if (lp->lp_detaching) { 779169329Sthompsa SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) { 780169329Sthompsa if (llq->llq_ifp == ifp) { 781169329Sthompsa SLIST_REMOVE(&sc->sc_llq_head, llq, lagg_llq, 782169329Sthompsa llq_entries); 783169329Sthompsa free(llq, M_DEVBUF); 784169329Sthompsa break; /* Only appears once */ 785169329Sthompsa } 786169329Sthompsa } 787169329Sthompsa } 788169329Sthompsa 789168793Sthompsa if (lp->lp_ifflags) 790168793Sthompsa if_printf(ifp, "%s: lp_ifflags unclean\n", __func__); 791168793Sthompsa 792168793Sthompsa free(lp, M_DEVBUF); 793168793Sthompsa 794168793Sthompsa /* Update lagg capabilities */ 795171661Sthompsa lagg_capabilities(sc); 796173895Sthompsa lagg_linkstate(sc); 797168793Sthompsa 798168793Sthompsa return (0); 799168793Sthompsa} 800168793Sthompsa 801168793Sthompsastatic int 802168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 803168793Sthompsa{ 804168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data; 805168793Sthompsa struct lagg_softc *sc; 806168793Sthompsa struct lagg_port *lp = NULL; 807168793Sthompsa int error = 0; 808255038Sadrian struct rm_priotracker tracker; 809168793Sthompsa 810168793Sthompsa /* Should be checked by the caller */ 811168793Sthompsa if (ifp->if_type != IFT_IEEE8023ADLAG || 812170599Sthompsa (lp = ifp->if_lagg) == NULL || (sc = lp->lp_softc) == NULL) 813168793Sthompsa goto fallback; 814168793Sthompsa 815168793Sthompsa switch (cmd) { 816168793Sthompsa case SIOCGLAGGPORT: 817168793Sthompsa if (rp->rp_portname[0] == '\0' || 818168793Sthompsa ifunit(rp->rp_portname) != ifp) { 819168793Sthompsa error = EINVAL; 820168793Sthompsa break; 821168793Sthompsa } 822168793Sthompsa 823255038Sadrian LAGG_RLOCK(sc, &tracker); 824171603Sthompsa if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) { 825168793Sthompsa error = ENOENT; 826255038Sadrian LAGG_RUNLOCK(sc, &tracker); 827168793Sthompsa break; 828168793Sthompsa } 829168793Sthompsa 830168793Sthompsa lagg_port2req(lp, rp); 831255038Sadrian LAGG_RUNLOCK(sc, &tracker); 832168793Sthompsa break; 833171661Sthompsa 834171661Sthompsa case SIOCSIFCAP: 835171661Sthompsa if (lp->lp_ioctl == NULL) { 836171661Sthompsa error = EINVAL; 837171661Sthompsa break; 838171661Sthompsa } 839171661Sthompsa error = (*lp->lp_ioctl)(ifp, cmd, data); 840171661Sthompsa if (error) 841171661Sthompsa break; 842171661Sthompsa 843171661Sthompsa /* Update lagg interface capabilities */ 844171661Sthompsa LAGG_WLOCK(sc); 845171661Sthompsa lagg_capabilities(sc); 846171661Sthompsa LAGG_WUNLOCK(sc); 847171661Sthompsa break; 848171661Sthompsa 849171661Sthompsa case SIOCSIFMTU: 850171661Sthompsa /* Do not allow the MTU to be changed once joined */ 851171661Sthompsa error = EINVAL; 852171661Sthompsa break; 853171661Sthompsa 854168793Sthompsa default: 855168793Sthompsa goto fallback; 856168793Sthompsa } 857168793Sthompsa 858168793Sthompsa return (error); 859168793Sthompsa 860168793Sthompsafallback: 861313112Sasomers if (lp != NULL && lp->lp_ioctl != NULL) 862168793Sthompsa return ((*lp->lp_ioctl)(ifp, cmd, data)); 863168793Sthompsa 864168793Sthompsa return (EINVAL); 865168793Sthompsa} 866168793Sthompsa 867234936Semaste/* 868234936Semaste * For direct output to child ports. 869234936Semaste */ 870168793Sthompsastatic int 871168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m, 872249925Sglebius const struct sockaddr *dst, struct route *ro) 873168793Sthompsa{ 874168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 875168793Sthompsa 876168793Sthompsa switch (dst->sa_family) { 877168793Sthompsa case pseudo_AF_HDRCMPLT: 878168793Sthompsa case AF_UNSPEC: 879191148Skmacy return ((*lp->lp_output)(ifp, m, dst, ro)); 880168793Sthompsa } 881168793Sthompsa 882168793Sthompsa /* drop any other frames */ 883168793Sthompsa m_freem(m); 884245741Sglebius return (ENETDOWN); 885168793Sthompsa} 886168793Sthompsa 887168793Sthompsastatic void 888168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) 889168793Sthompsa{ 890168793Sthompsa struct lagg_port *lp; 891168793Sthompsa struct lagg_softc *sc; 892168793Sthompsa 893168793Sthompsa if ((lp = ifp->if_lagg) == NULL) 894168793Sthompsa return; 895237852Sthompsa /* If the ifnet is just being renamed, don't do anything. */ 896237852Sthompsa if (ifp->if_flags & IFF_RENAMING) 897237852Sthompsa return; 898168793Sthompsa 899170599Sthompsa sc = lp->lp_softc; 900168793Sthompsa 901169569Sthompsa LAGG_WLOCK(sc); 902169328Sthompsa lp->lp_detaching = 1; 903168793Sthompsa lagg_port_destroy(lp, 1); 904169569Sthompsa LAGG_WUNLOCK(sc); 905168793Sthompsa} 906168793Sthompsa 907168793Sthompsastatic void 908168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp) 909168793Sthompsa{ 910170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 911172020Sthompsa 912168793Sthompsa strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname)); 913168793Sthompsa strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname)); 914168793Sthompsa rp->rp_prio = lp->lp_prio; 915168793Sthompsa rp->rp_flags = lp->lp_flags; 916171247Sthompsa if (sc->sc_portreq != NULL) 917171247Sthompsa (*sc->sc_portreq)(lp, (caddr_t)&rp->rp_psc); 918168793Sthompsa 919168793Sthompsa /* Add protocol specific flags */ 920168793Sthompsa switch (sc->sc_proto) { 921168793Sthompsa case LAGG_PROTO_FAILOVER: 922168793Sthompsa if (lp == sc->sc_primary) 923169204Sthompsa rp->rp_flags |= LAGG_PORT_MASTER; 924172020Sthompsa if (lp == lagg_link_active(sc, sc->sc_primary)) 925172020Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 926172020Sthompsa break; 927172020Sthompsa 928168793Sthompsa case LAGG_PROTO_ROUNDROBIN: 929168793Sthompsa case LAGG_PROTO_LOADBALANCE: 930168793Sthompsa case LAGG_PROTO_ETHERCHANNEL: 931168793Sthompsa if (LAGG_PORTACTIVE(lp)) 932168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 933168793Sthompsa break; 934168793Sthompsa 935168793Sthompsa case LAGG_PROTO_LACP: 936168793Sthompsa /* LACP has a different definition of active */ 937177274Sthompsa if (lacp_isactive(lp)) 938168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 939177274Sthompsa if (lacp_iscollecting(lp)) 940177274Sthompsa rp->rp_flags |= LAGG_PORT_COLLECTING; 941177274Sthompsa if (lacp_isdistributing(lp)) 942177274Sthompsa rp->rp_flags |= LAGG_PORT_DISTRIBUTING; 943168793Sthompsa break; 944168793Sthompsa } 945168793Sthompsa 946168793Sthompsa} 947168793Sthompsa 948168793Sthompsastatic void 949168793Sthompsalagg_init(void *xsc) 950168793Sthompsa{ 951168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)xsc; 952168793Sthompsa struct lagg_port *lp; 953168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 954168793Sthompsa 955168793Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 956168793Sthompsa return; 957168793Sthompsa 958169569Sthompsa LAGG_WLOCK(sc); 959168793Sthompsa 960168793Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 961168793Sthompsa /* Update the port lladdrs */ 962168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 963168793Sthompsa lagg_port_lladdr(lp, IF_LLADDR(ifp)); 964168793Sthompsa 965168793Sthompsa if (sc->sc_init != NULL) 966168793Sthompsa (*sc->sc_init)(sc); 967168793Sthompsa 968169569Sthompsa LAGG_WUNLOCK(sc); 969168793Sthompsa} 970168793Sthompsa 971168793Sthompsastatic void 972168793Sthompsalagg_stop(struct lagg_softc *sc) 973168793Sthompsa{ 974168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 975168793Sthompsa 976169569Sthompsa LAGG_WLOCK_ASSERT(sc); 977168793Sthompsa 978168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 979168793Sthompsa return; 980168793Sthompsa 981168793Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 982168793Sthompsa 983168793Sthompsa if (sc->sc_stop != NULL) 984168793Sthompsa (*sc->sc_stop)(sc); 985168793Sthompsa} 986168793Sthompsa 987168793Sthompsastatic int 988168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 989168793Sthompsa{ 990168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 991168793Sthompsa struct lagg_reqall *ra = (struct lagg_reqall *)data; 992287723Shrs struct lagg_reqopts *ro = (struct lagg_reqopts *)data; 993168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf; 994232629Sthompsa struct lagg_reqflags *rf = (struct lagg_reqflags *)data; 995168793Sthompsa struct ifreq *ifr = (struct ifreq *)data; 996168793Sthompsa struct lagg_port *lp; 997287723Shrs const struct lagg_proto *proto = NULL; 998168793Sthompsa struct ifnet *tpif; 999168793Sthompsa struct thread *td = curthread; 1000171603Sthompsa char *buf, *outbuf; 1001171603Sthompsa int count, buflen, len, error = 0; 1002255038Sadrian struct rm_priotracker tracker; 1003168793Sthompsa 1004168793Sthompsa bzero(&rpbuf, sizeof(rpbuf)); 1005168793Sthompsa 1006168793Sthompsa switch (cmd) { 1007168793Sthompsa case SIOCGLAGG: 1008255038Sadrian LAGG_RLOCK(sc, &tracker); 1009171603Sthompsa count = 0; 1010171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1011171603Sthompsa count++; 1012171603Sthompsa buflen = count * sizeof(struct lagg_reqport); 1013255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1014171603Sthompsa 1015171603Sthompsa outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); 1016171603Sthompsa 1017255038Sadrian LAGG_RLOCK(sc, &tracker); 1018168793Sthompsa ra->ra_proto = sc->sc_proto; 1019171247Sthompsa if (sc->sc_req != NULL) 1020171247Sthompsa (*sc->sc_req)(sc, (caddr_t)&ra->ra_psc); 1021171603Sthompsa 1022171603Sthompsa count = 0; 1023171603Sthompsa buf = outbuf; 1024171603Sthompsa len = min(ra->ra_size, buflen); 1025171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1026171603Sthompsa if (len < sizeof(rpbuf)) 1027171603Sthompsa break; 1028171603Sthompsa 1029168793Sthompsa lagg_port2req(lp, &rpbuf); 1030171603Sthompsa memcpy(buf, &rpbuf, sizeof(rpbuf)); 1031171603Sthompsa count++; 1032171603Sthompsa buf += sizeof(rpbuf); 1033171603Sthompsa len -= sizeof(rpbuf); 1034168793Sthompsa } 1035255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1036171603Sthompsa ra->ra_ports = count; 1037171603Sthompsa ra->ra_size = count * sizeof(rpbuf); 1038171603Sthompsa error = copyout(outbuf, ra->ra_port, ra->ra_size); 1039171603Sthompsa free(outbuf, M_TEMP); 1040168793Sthompsa break; 1041168793Sthompsa case SIOCSLAGG: 1042168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1043168793Sthompsa if (error) 1044168793Sthompsa break; 1045287723Shrs for (proto = lagg_protos; proto->ti_proto != LAGG_PROTO_NONE; 1046287723Shrs proto++) { 1047287723Shrs if (proto->ti_proto == ra->ra_proto) { 1048287723Shrs if (sc->sc_ifflags & IFF_DEBUG) 1049287723Shrs printf("%s: using proto %u\n", 1050287723Shrs sc->sc_ifname, proto->ti_proto); 1051287723Shrs break; 1052287723Shrs } 1053287723Shrs } 1054296040Saraujo if (proto->ti_proto >= LAGG_PROTO_MAX) { 1055168793Sthompsa error = EPROTONOSUPPORT; 1056168793Sthompsa break; 1057168793Sthompsa } 1058287723Shrs /* Set to LAGG_PROTO_NONE during the attach. */ 1059234163Sthompsa LAGG_WLOCK(sc); 1060168793Sthompsa if (sc->sc_proto != LAGG_PROTO_NONE) { 1061294070Srpokala int (*sc_detach)(struct lagg_softc *sc); 1062294070Srpokala 1063294070Srpokala /* Reset protocol and pointers */ 1064234163Sthompsa sc->sc_proto = LAGG_PROTO_NONE; 1065294070Srpokala sc_detach = sc->sc_detach; 1066294070Srpokala sc->sc_detach = NULL; 1067294070Srpokala sc->sc_start = NULL; 1068294070Srpokala sc->sc_input = NULL; 1069294070Srpokala sc->sc_port_create = NULL; 1070294070Srpokala sc->sc_port_destroy = NULL; 1071294070Srpokala sc->sc_linkstate = NULL; 1072294070Srpokala sc->sc_init = NULL; 1073294070Srpokala sc->sc_stop = NULL; 1074294070Srpokala sc->sc_lladdr = NULL; 1075294070Srpokala sc->sc_req = NULL; 1076294070Srpokala sc->sc_portreq = NULL; 1077294070Srpokala 1078294070Srpokala if (sc_detach != NULL) 1079294070Srpokala sc_detach(sc); 1080287723Shrs else 1081287723Shrs LAGG_WUNLOCK(sc); 1082288072Shrs } else 1083288072Shrs LAGG_WUNLOCK(sc); 1084296040Saraujo if (proto->ti_proto != LAGG_PROTO_NONE) 1085296040Saraujo proto->ti_attach(sc); 1086287723Shrs LAGG_WLOCK(sc); 1087287723Shrs sc->sc_proto = proto->ti_proto; 1088287723Shrs LAGG_WUNLOCK(sc); 1089287723Shrs break; 1090287723Shrs case SIOCGLAGGOPTS: 1091287723Shrs ro->ro_opts = sc->sc_opts; 1092287723Shrs if (sc->sc_proto == LAGG_PROTO_LACP) { 1093287723Shrs struct lacp_softc *lsc; 1094287723Shrs 1095287723Shrs lsc = (struct lacp_softc *)sc->sc_psc; 1096287723Shrs if (lsc->lsc_debug.lsc_tx_test != 0) 1097287723Shrs ro->ro_opts |= LAGG_OPT_LACP_TXTEST; 1098287723Shrs if (lsc->lsc_debug.lsc_rx_test != 0) 1099287723Shrs ro->ro_opts |= LAGG_OPT_LACP_RXTEST; 1100287723Shrs if (lsc->lsc_strict_mode != 0) 1101287723Shrs ro->ro_opts |= LAGG_OPT_LACP_STRICT; 1102287808Shiren if (lsc->lsc_fast_timeout != 0) 1103362368Srpokala ro->ro_opts |= LAGG_OPT_LACP_FAST_TIMO; 1104287723Shrs 1105287723Shrs ro->ro_active = sc->sc_active; 1106287723Shrs } else { 1107287723Shrs ro->ro_active = 0; 1108287723Shrs SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1109287723Shrs ro->ro_active += LAGG_PORTACTIVE(lp); 1110287723Shrs } 1111287723Shrs ro->ro_flapping = sc->sc_flapping; 1112287723Shrs ro->ro_flowid_shift = sc->flowid_shift; 1113287723Shrs break; 1114287723Shrs case SIOCSLAGGOPTS: 1115287723Shrs error = priv_check(td, PRIV_NET_LAGG); 1116287723Shrs if (error) 1117234163Sthompsa break; 1118287723Shrs if (ro->ro_opts == 0) 1119287723Shrs break; 1120287723Shrs /* 1121287723Shrs * Set options. LACP options are stored in sc->sc_psc, 1122287723Shrs * not in sc_opts. 1123287723Shrs */ 1124287723Shrs int valid, lacp; 1125287723Shrs 1126287723Shrs switch (ro->ro_opts) { 1127287723Shrs case LAGG_OPT_USE_FLOWID: 1128287723Shrs case -LAGG_OPT_USE_FLOWID: 1129287723Shrs case LAGG_OPT_FLOWIDSHIFT: 1130287723Shrs valid = 1; 1131287723Shrs lacp = 0; 1132287723Shrs break; 1133287723Shrs case LAGG_OPT_LACP_TXTEST: 1134287723Shrs case -LAGG_OPT_LACP_TXTEST: 1135287723Shrs case LAGG_OPT_LACP_RXTEST: 1136287723Shrs case -LAGG_OPT_LACP_RXTEST: 1137287723Shrs case LAGG_OPT_LACP_STRICT: 1138287723Shrs case -LAGG_OPT_LACP_STRICT: 1139362368Srpokala case LAGG_OPT_LACP_FAST_TIMO: 1140362368Srpokala case -LAGG_OPT_LACP_FAST_TIMO: 1141287723Shrs valid = lacp = 1; 1142287723Shrs break; 1143287723Shrs default: 1144287723Shrs valid = lacp = 0; 1145287723Shrs break; 1146168793Sthompsa } 1147287723Shrs 1148287723Shrs LAGG_WLOCK(sc); 1149287723Shrs if (valid == 0 || 1150287723Shrs (lacp == 1 && sc->sc_proto != LAGG_PROTO_LACP)) { 1151287723Shrs /* Invalid combination of options specified. */ 1152287723Shrs error = EINVAL; 1153287723Shrs LAGG_WUNLOCK(sc); 1154287723Shrs break; /* Return from SIOCSLAGGOPTS. */ 1155287723Shrs } 1156287723Shrs /* 1157287723Shrs * Store new options into sc->sc_opts except for 1158287723Shrs * FLOWIDSHIFT and LACP options. 1159287723Shrs */ 1160287723Shrs if (lacp == 0) { 1161287723Shrs if (ro->ro_opts == LAGG_OPT_FLOWIDSHIFT) 1162287723Shrs sc->flowid_shift = ro->ro_flowid_shift; 1163287723Shrs else if (ro->ro_opts > 0) 1164287723Shrs sc->sc_opts |= ro->ro_opts; 1165287723Shrs else 1166287723Shrs sc->sc_opts &= ~ro->ro_opts; 1167287723Shrs } else { 1168287723Shrs struct lacp_softc *lsc; 1169287808Shiren struct lacp_port *lp; 1170287723Shrs 1171287723Shrs lsc = (struct lacp_softc *)sc->sc_psc; 1172287723Shrs 1173287723Shrs switch (ro->ro_opts) { 1174287723Shrs case LAGG_OPT_LACP_TXTEST: 1175287723Shrs lsc->lsc_debug.lsc_tx_test = 1; 1176287723Shrs break; 1177287723Shrs case -LAGG_OPT_LACP_TXTEST: 1178287723Shrs lsc->lsc_debug.lsc_tx_test = 0; 1179287723Shrs break; 1180287723Shrs case LAGG_OPT_LACP_RXTEST: 1181287723Shrs lsc->lsc_debug.lsc_rx_test = 1; 1182287723Shrs break; 1183287723Shrs case -LAGG_OPT_LACP_RXTEST: 1184287723Shrs lsc->lsc_debug.lsc_rx_test = 0; 1185287723Shrs break; 1186287723Shrs case LAGG_OPT_LACP_STRICT: 1187287723Shrs lsc->lsc_strict_mode = 1; 1188287723Shrs break; 1189287723Shrs case -LAGG_OPT_LACP_STRICT: 1190287723Shrs lsc->lsc_strict_mode = 0; 1191287723Shrs break; 1192362368Srpokala case LAGG_OPT_LACP_FAST_TIMO: 1193287808Shiren LACP_LOCK(lsc); 1194287808Shiren LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) 1195287808Shiren lp->lp_state |= LACP_STATE_TIMEOUT; 1196287808Shiren LACP_UNLOCK(lsc); 1197287808Shiren lsc->lsc_fast_timeout = 1; 1198287808Shiren break; 1199362368Srpokala case -LAGG_OPT_LACP_FAST_TIMO: 1200287808Shiren LACP_LOCK(lsc); 1201287808Shiren LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) 1202287808Shiren lp->lp_state &= ~LACP_STATE_TIMEOUT; 1203287808Shiren LACP_UNLOCK(lsc); 1204287808Shiren lsc->lsc_fast_timeout = 0; 1205287808Shiren break; 1206168793Sthompsa } 1207168793Sthompsa } 1208234163Sthompsa LAGG_WUNLOCK(sc); 1209168793Sthompsa break; 1210232629Sthompsa case SIOCGLAGGFLAGS: 1211232629Sthompsa rf->rf_flags = sc->sc_flags; 1212232629Sthompsa break; 1213232629Sthompsa case SIOCSLAGGHASH: 1214232629Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1215232629Sthompsa if (error) 1216232629Sthompsa break; 1217232629Sthompsa if ((rf->rf_flags & LAGG_F_HASHMASK) == 0) { 1218232629Sthompsa error = EINVAL; 1219232629Sthompsa break; 1220232629Sthompsa } 1221232629Sthompsa LAGG_WLOCK(sc); 1222232629Sthompsa sc->sc_flags &= ~LAGG_F_HASHMASK; 1223232629Sthompsa sc->sc_flags |= rf->rf_flags & LAGG_F_HASHMASK; 1224232629Sthompsa LAGG_WUNLOCK(sc); 1225232629Sthompsa break; 1226168793Sthompsa case SIOCGLAGGPORT: 1227168793Sthompsa if (rp->rp_portname[0] == '\0' || 1228168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 1229168793Sthompsa error = EINVAL; 1230168793Sthompsa break; 1231168793Sthompsa } 1232168793Sthompsa 1233255038Sadrian LAGG_RLOCK(sc, &tracker); 1234168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 1235170599Sthompsa lp->lp_softc != sc) { 1236168793Sthompsa error = ENOENT; 1237255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1238168793Sthompsa break; 1239168793Sthompsa } 1240168793Sthompsa 1241168793Sthompsa lagg_port2req(lp, rp); 1242255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1243168793Sthompsa break; 1244168793Sthompsa case SIOCSLAGGPORT: 1245168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1246168793Sthompsa if (error) 1247168793Sthompsa break; 1248168793Sthompsa if (rp->rp_portname[0] == '\0' || 1249168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 1250168793Sthompsa error = EINVAL; 1251168793Sthompsa break; 1252168793Sthompsa } 1253287723Shrs#ifdef INET6 1254287723Shrs /* 1255287723Shrs * A laggport interface should not have inet6 address 1256287723Shrs * because two interfaces with a valid link-local 1257287723Shrs * scope zone must not be merged in any form. This 1258287723Shrs * restriction is needed to prevent violation of 1259287723Shrs * link-local scope zone. Attempts to add a laggport 1260287723Shrs * interface which has inet6 addresses triggers 1261287723Shrs * removal of all inet6 addresses on the member 1262287723Shrs * interface. 1263287723Shrs */ 1264287723Shrs if (in6ifa_llaonifp(tpif)) { 1265287723Shrs in6_ifdetach(tpif); 1266287723Shrs if_printf(sc->sc_ifp, 1267287723Shrs "IPv6 addresses on %s have been removed " 1268287723Shrs "before adding it as a member to prevent " 1269287723Shrs "IPv6 address scope violation.\n", 1270287723Shrs tpif->if_xname); 1271287723Shrs } 1272287723Shrs#endif 1273171603Sthompsa LAGG_WLOCK(sc); 1274168793Sthompsa error = lagg_port_create(sc, tpif); 1275171603Sthompsa LAGG_WUNLOCK(sc); 1276168793Sthompsa break; 1277168793Sthompsa case SIOCSLAGGDELPORT: 1278168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1279168793Sthompsa if (error) 1280168793Sthompsa break; 1281168793Sthompsa if (rp->rp_portname[0] == '\0' || 1282168793Sthompsa (tpif = ifunit(rp->rp_portname)) == NULL) { 1283168793Sthompsa error = EINVAL; 1284168793Sthompsa break; 1285168793Sthompsa } 1286168793Sthompsa 1287171603Sthompsa LAGG_WLOCK(sc); 1288168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 1289170599Sthompsa lp->lp_softc != sc) { 1290168793Sthompsa error = ENOENT; 1291171603Sthompsa LAGG_WUNLOCK(sc); 1292168793Sthompsa break; 1293168793Sthompsa } 1294168793Sthompsa 1295168793Sthompsa error = lagg_port_destroy(lp, 1); 1296171603Sthompsa LAGG_WUNLOCK(sc); 1297168793Sthompsa break; 1298168793Sthompsa case SIOCSIFFLAGS: 1299168793Sthompsa /* Set flags on ports too */ 1300171603Sthompsa LAGG_WLOCK(sc); 1301168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1302168793Sthompsa lagg_setflags(lp, 1); 1303168793Sthompsa } 1304171603Sthompsa LAGG_WUNLOCK(sc); 1305168793Sthompsa 1306168793Sthompsa if (!(ifp->if_flags & IFF_UP) && 1307168793Sthompsa (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1308168793Sthompsa /* 1309168793Sthompsa * If interface is marked down and it is running, 1310168793Sthompsa * then stop and disable it. 1311168793Sthompsa */ 1312171603Sthompsa LAGG_WLOCK(sc); 1313168793Sthompsa lagg_stop(sc); 1314171603Sthompsa LAGG_WUNLOCK(sc); 1315168793Sthompsa } else if ((ifp->if_flags & IFF_UP) && 1316168793Sthompsa !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1317168793Sthompsa /* 1318168793Sthompsa * If interface is marked up and it is stopped, then 1319168793Sthompsa * start it. 1320168793Sthompsa */ 1321168793Sthompsa (*ifp->if_init)(sc); 1322168793Sthompsa } 1323168793Sthompsa break; 1324168793Sthompsa case SIOCADDMULTI: 1325168793Sthompsa case SIOCDELMULTI: 1326171603Sthompsa LAGG_WLOCK(sc); 1327168793Sthompsa error = lagg_ether_setmulti(sc); 1328171603Sthompsa LAGG_WUNLOCK(sc); 1329168793Sthompsa break; 1330168793Sthompsa case SIOCSIFMEDIA: 1331168793Sthompsa case SIOCGIFMEDIA: 1332168793Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 1333168793Sthompsa break; 1334171661Sthompsa 1335171661Sthompsa case SIOCSIFCAP: 1336171661Sthompsa case SIOCSIFMTU: 1337171661Sthompsa /* Do not allow the MTU or caps to be directly changed */ 1338171661Sthompsa error = EINVAL; 1339171661Sthompsa break; 1340171661Sthompsa 1341168793Sthompsa default: 1342168793Sthompsa error = ether_ioctl(ifp, cmd, data); 1343168793Sthompsa break; 1344168793Sthompsa } 1345168793Sthompsa return (error); 1346168793Sthompsa} 1347168793Sthompsa 1348168793Sthompsastatic int 1349168793Sthompsalagg_ether_setmulti(struct lagg_softc *sc) 1350168793Sthompsa{ 1351169327Sthompsa struct lagg_port *lp; 1352168793Sthompsa 1353169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1354168793Sthompsa 1355169327Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1356169340Sthompsa /* First, remove any existing filter entries. */ 1357169340Sthompsa lagg_ether_cmdmulti(lp, 0); 1358169340Sthompsa /* copy all addresses from the lagg interface to the port */ 1359169327Sthompsa lagg_ether_cmdmulti(lp, 1); 1360169340Sthompsa } 1361168793Sthompsa return (0); 1362168793Sthompsa} 1363168793Sthompsa 1364168793Sthompsastatic int 1365168793Sthompsalagg_ether_cmdmulti(struct lagg_port *lp, int set) 1366168793Sthompsa{ 1367170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1368169327Sthompsa struct ifnet *ifp = lp->lp_ifp; 1369170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1370169327Sthompsa struct lagg_mc *mc; 1371270136Smav struct ifmultiaddr *ifma; 1372169327Sthompsa int error; 1373168793Sthompsa 1374169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1375168793Sthompsa 1376169327Sthompsa if (set) { 1377270136Smav IF_ADDR_WLOCK(scifp); 1378170599Sthompsa TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) { 1379169327Sthompsa if (ifma->ifma_addr->sa_family != AF_LINK) 1380169327Sthompsa continue; 1381169327Sthompsa mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); 1382270136Smav if (mc == NULL) { 1383270136Smav IF_ADDR_WUNLOCK(scifp); 1384169327Sthompsa return (ENOMEM); 1385270136Smav } 1386270136Smav bcopy(ifma->ifma_addr, &mc->mc_addr, 1387270136Smav ifma->ifma_addr->sa_len); 1388270136Smav mc->mc_addr.sdl_index = ifp->if_index; 1389270136Smav mc->mc_ifma = NULL; 1390169327Sthompsa SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); 1391168793Sthompsa } 1392270136Smav IF_ADDR_WUNLOCK(scifp); 1393270136Smav SLIST_FOREACH (mc, &lp->lp_mc_head, mc_entries) { 1394270136Smav error = if_addmulti(ifp, 1395270136Smav (struct sockaddr *)&mc->mc_addr, &mc->mc_ifma); 1396270136Smav if (error) 1397270136Smav return (error); 1398270136Smav } 1399169327Sthompsa } else { 1400169327Sthompsa while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { 1401169327Sthompsa SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); 1402270136Smav if (mc->mc_ifma && !lp->lp_detaching) 1403270136Smav if_delmulti_ifma(mc->mc_ifma); 1404169327Sthompsa free(mc, M_DEVBUF); 1405169327Sthompsa } 1406168793Sthompsa } 1407168793Sthompsa return (0); 1408168793Sthompsa} 1409168793Sthompsa 1410168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */ 1411168793Sthompsastatic int 1412168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status, 1413168793Sthompsa int (*func)(struct ifnet *, int)) 1414168793Sthompsa{ 1415170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1416170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1417168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 1418168793Sthompsa int error; 1419168793Sthompsa 1420169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1421168793Sthompsa 1422170599Sthompsa status = status ? (scifp->if_flags & flag) : 0; 1423168793Sthompsa /* Now "status" contains the flag value or 0 */ 1424168793Sthompsa 1425168793Sthompsa /* 1426168793Sthompsa * See if recorded ports status is different from what 1427168793Sthompsa * we want it to be. If it is, flip it. We record ports 1428168793Sthompsa * status in lp_ifflags so that we won't clear ports flag 1429168793Sthompsa * we haven't set. In fact, we don't clear or set ports 1430168793Sthompsa * flags directly, but get or release references to them. 1431168793Sthompsa * That's why we can be sure that recorded flags still are 1432168793Sthompsa * in accord with actual ports flags. 1433168793Sthompsa */ 1434168793Sthompsa if (status != (lp->lp_ifflags & flag)) { 1435168793Sthompsa error = (*func)(ifp, status); 1436168793Sthompsa if (error) 1437168793Sthompsa return (error); 1438168793Sthompsa lp->lp_ifflags &= ~flag; 1439168793Sthompsa lp->lp_ifflags |= status; 1440168793Sthompsa } 1441168793Sthompsa return (0); 1442168793Sthompsa} 1443168793Sthompsa 1444168793Sthompsa/* 1445168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port 1446168793Sthompsa * if "status" is true, update ports flags respective to the lagg 1447168793Sthompsa * if "status" is false, forcedly clear the flags set on port. 1448168793Sthompsa */ 1449168793Sthompsastatic int 1450168793Sthompsalagg_setflags(struct lagg_port *lp, int status) 1451168793Sthompsa{ 1452168793Sthompsa int error, i; 1453170599Sthompsa 1454168793Sthompsa for (i = 0; lagg_pflags[i].flag; i++) { 1455168793Sthompsa error = lagg_setflag(lp, lagg_pflags[i].flag, 1456168793Sthompsa status, lagg_pflags[i].func); 1457168793Sthompsa if (error) 1458168793Sthompsa return (error); 1459168793Sthompsa } 1460168793Sthompsa return (0); 1461168793Sthompsa} 1462168793Sthompsa 1463240742Sglebiusstatic int 1464240742Sglebiuslagg_transmit(struct ifnet *ifp, struct mbuf *m) 1465168793Sthompsa{ 1466168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1467240742Sglebius int error, len, mcast; 1468255038Sadrian struct rm_priotracker tracker; 1469168793Sthompsa 1470240742Sglebius len = m->m_pkthdr.len; 1471240742Sglebius mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 1472240742Sglebius 1473255038Sadrian LAGG_RLOCK(sc, &tracker); 1474183160Sthompsa /* We need a Tx algorithm and at least one port */ 1475183160Sthompsa if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) { 1476255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1477240742Sglebius m_freem(m); 1478240742Sglebius ifp->if_oerrors++; 1479240742Sglebius return (ENXIO); 1480183160Sthompsa } 1481183160Sthompsa 1482240742Sglebius ETHER_BPF_MTAP(ifp, m); 1483168793Sthompsa 1484240742Sglebius error = (*sc->sc_start)(sc, m); 1485255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1486168793Sthompsa 1487240742Sglebius if (error == 0) { 1488249506Sglebius counter_u64_add(sc->sc_opackets, 1); 1489249506Sglebius counter_u64_add(sc->sc_obytes, len); 1490240742Sglebius ifp->if_omcasts += mcast; 1491240742Sglebius } else 1492240742Sglebius ifp->if_oerrors++; 1493240742Sglebius 1494240742Sglebius return (error); 1495168793Sthompsa} 1496168793Sthompsa 1497240742Sglebius/* 1498240742Sglebius * The ifp->if_qflush entry point for lagg(4) is no-op. 1499240742Sglebius */ 1500240742Sglebiusstatic void 1501240742Sglebiuslagg_qflush(struct ifnet *ifp __unused) 1502240742Sglebius{ 1503240742Sglebius} 1504240742Sglebius 1505168793Sthompsastatic struct mbuf * 1506168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m) 1507168793Sthompsa{ 1508168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1509170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1510170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1511255038Sadrian struct rm_priotracker tracker; 1512168793Sthompsa 1513255038Sadrian LAGG_RLOCK(sc, &tracker); 1514170599Sthompsa if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 1515169227Sthompsa (lp->lp_flags & LAGG_PORT_DISABLED) || 1516168793Sthompsa sc->sc_proto == LAGG_PROTO_NONE) { 1517255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1518168793Sthompsa m_freem(m); 1519168793Sthompsa return (NULL); 1520168793Sthompsa } 1521168793Sthompsa 1522172825Sthompsa ETHER_BPF_MTAP(scifp, m); 1523168793Sthompsa 1524287723Shrs m = (lp->lp_detaching == 0) ? (*sc->sc_input)(sc, lp, m) : NULL; 1525168793Sthompsa 1526168793Sthompsa if (m != NULL) { 1527249506Sglebius counter_u64_add(sc->sc_ipackets, 1); 1528249506Sglebius counter_u64_add(sc->sc_ibytes, m->m_pkthdr.len); 1529174278Sthompsa 1530174278Sthompsa if (scifp->if_flags & IFF_MONITOR) { 1531174278Sthompsa m_freem(m); 1532174278Sthompsa m = NULL; 1533174278Sthompsa } 1534168793Sthompsa } 1535168793Sthompsa 1536255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1537168793Sthompsa return (m); 1538168793Sthompsa} 1539168793Sthompsa 1540168793Sthompsastatic int 1541168793Sthompsalagg_media_change(struct ifnet *ifp) 1542168793Sthompsa{ 1543168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1544168793Sthompsa 1545168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1546168793Sthompsa printf("%s\n", __func__); 1547168793Sthompsa 1548168793Sthompsa /* Ignore */ 1549168793Sthompsa return (0); 1550168793Sthompsa} 1551168793Sthompsa 1552168793Sthompsastatic void 1553168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1554168793Sthompsa{ 1555168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1556168793Sthompsa struct lagg_port *lp; 1557255038Sadrian struct rm_priotracker tracker; 1558168793Sthompsa 1559168793Sthompsa imr->ifm_status = IFM_AVALID; 1560168793Sthompsa imr->ifm_active = IFM_ETHER | IFM_AUTO; 1561168793Sthompsa 1562255038Sadrian LAGG_RLOCK(sc, &tracker); 1563169340Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1564169340Sthompsa if (LAGG_PORTACTIVE(lp)) 1565169340Sthompsa imr->ifm_status |= IFM_ACTIVE; 1566169340Sthompsa } 1567255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1568168793Sthompsa} 1569168793Sthompsa 1570168793Sthompsastatic void 1571173895Sthompsalagg_linkstate(struct lagg_softc *sc) 1572173895Sthompsa{ 1573173895Sthompsa struct lagg_port *lp; 1574173895Sthompsa int new_link = LINK_STATE_DOWN; 1575186255Sthompsa uint64_t speed; 1576173895Sthompsa 1577173895Sthompsa /* Our link is considered up if at least one of our ports is active */ 1578173895Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1579173895Sthompsa if (lp->lp_link_state == LINK_STATE_UP) { 1580173895Sthompsa new_link = LINK_STATE_UP; 1581173895Sthompsa break; 1582173895Sthompsa } 1583173895Sthompsa } 1584173895Sthompsa if_link_state_change(sc->sc_ifp, new_link); 1585186254Sthompsa 1586186254Sthompsa /* Update if_baudrate to reflect the max possible speed */ 1587186254Sthompsa switch (sc->sc_proto) { 1588186254Sthompsa case LAGG_PROTO_FAILOVER: 1589186255Sthompsa sc->sc_ifp->if_baudrate = sc->sc_primary != NULL ? 1590186255Sthompsa sc->sc_primary->lp_ifp->if_baudrate : 0; 1591186254Sthompsa break; 1592186254Sthompsa case LAGG_PROTO_ROUNDROBIN: 1593186254Sthompsa case LAGG_PROTO_LOADBALANCE: 1594186254Sthompsa case LAGG_PROTO_ETHERCHANNEL: 1595186255Sthompsa speed = 0; 1596186254Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1597186254Sthompsa speed += lp->lp_ifp->if_baudrate; 1598186254Sthompsa sc->sc_ifp->if_baudrate = speed; 1599186254Sthompsa break; 1600186254Sthompsa case LAGG_PROTO_LACP: 1601186254Sthompsa /* LACP updates if_baudrate itself */ 1602186254Sthompsa break; 1603186254Sthompsa } 1604173895Sthompsa} 1605173895Sthompsa 1606173895Sthompsastatic void 1607168793Sthompsalagg_port_state(struct ifnet *ifp, int state) 1608168793Sthompsa{ 1609168793Sthompsa struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; 1610168793Sthompsa struct lagg_softc *sc = NULL; 1611168793Sthompsa 1612168793Sthompsa if (lp != NULL) 1613170599Sthompsa sc = lp->lp_softc; 1614168793Sthompsa if (sc == NULL) 1615168793Sthompsa return; 1616168793Sthompsa 1617169569Sthompsa LAGG_WLOCK(sc); 1618173895Sthompsa lagg_linkstate(sc); 1619168793Sthompsa if (sc->sc_linkstate != NULL) 1620168793Sthompsa (*sc->sc_linkstate)(lp); 1621169569Sthompsa LAGG_WUNLOCK(sc); 1622168793Sthompsa} 1623168793Sthompsa 1624168793Sthompsastruct lagg_port * 1625168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) 1626168793Sthompsa{ 1627168793Sthompsa struct lagg_port *lp_next, *rval = NULL; 1628168793Sthompsa // int new_link = LINK_STATE_DOWN; 1629168793Sthompsa 1630169688Sthompsa LAGG_RLOCK_ASSERT(sc); 1631168793Sthompsa /* 1632168793Sthompsa * Search a port which reports an active link state. 1633168793Sthompsa */ 1634168793Sthompsa 1635168793Sthompsa if (lp == NULL) 1636168793Sthompsa goto search; 1637168793Sthompsa if (LAGG_PORTACTIVE(lp)) { 1638168793Sthompsa rval = lp; 1639168793Sthompsa goto found; 1640168793Sthompsa } 1641168793Sthompsa if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL && 1642168793Sthompsa LAGG_PORTACTIVE(lp_next)) { 1643168793Sthompsa rval = lp_next; 1644168793Sthompsa goto found; 1645168793Sthompsa } 1646168793Sthompsa 1647168793Sthompsasearch: 1648168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1649168793Sthompsa if (LAGG_PORTACTIVE(lp_next)) { 1650168793Sthompsa rval = lp_next; 1651168793Sthompsa goto found; 1652168793Sthompsa } 1653168793Sthompsa } 1654168793Sthompsa 1655168793Sthompsafound: 1656168793Sthompsa if (rval != NULL) { 1657168793Sthompsa /* 1658168793Sthompsa * The IEEE 802.1D standard assumes that a lagg with 1659168793Sthompsa * multiple ports is always full duplex. This is valid 1660168793Sthompsa * for load sharing laggs and if at least two links 1661168793Sthompsa * are active. Unfortunately, checking the latter would 1662168793Sthompsa * be too expensive at this point. 1663168793Sthompsa XXX 1664168793Sthompsa if ((sc->sc_capabilities & IFCAP_LAGG_FULLDUPLEX) && 1665168793Sthompsa (sc->sc_count > 1)) 1666168793Sthompsa new_link = LINK_STATE_FULL_DUPLEX; 1667168793Sthompsa else 1668168793Sthompsa new_link = rval->lp_link_state; 1669168793Sthompsa */ 1670168793Sthompsa } 1671168793Sthompsa 1672168793Sthompsa return (rval); 1673168793Sthompsa} 1674168793Sthompsa 1675168793Sthompsastatic const void * 1676168793Sthompsalagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf) 1677168793Sthompsa{ 1678168793Sthompsa if (m->m_pkthdr.len < (off + len)) { 1679168793Sthompsa return (NULL); 1680168793Sthompsa } else if (m->m_len < (off + len)) { 1681168793Sthompsa m_copydata(m, off, len, buf); 1682168793Sthompsa return (buf); 1683168793Sthompsa } 1684168793Sthompsa return (mtod(m, char *) + off); 1685168793Sthompsa} 1686168793Sthompsa 1687168793Sthompsauint32_t 1688232629Sthompsalagg_hashmbuf(struct lagg_softc *sc, struct mbuf *m, uint32_t key) 1689168793Sthompsa{ 1690168793Sthompsa uint16_t etype; 1691232629Sthompsa uint32_t p = key; 1692168793Sthompsa int off; 1693168793Sthompsa struct ether_header *eh; 1694168793Sthompsa const struct ether_vlan_header *vlan; 1695168793Sthompsa#ifdef INET 1696168793Sthompsa const struct ip *ip; 1697232629Sthompsa const uint32_t *ports; 1698232629Sthompsa int iphlen; 1699168793Sthompsa#endif 1700168793Sthompsa#ifdef INET6 1701168793Sthompsa const struct ip6_hdr *ip6; 1702169583Sthompsa uint32_t flow; 1703168793Sthompsa#endif 1704232629Sthompsa union { 1705232629Sthompsa#ifdef INET 1706232629Sthompsa struct ip ip; 1707232629Sthompsa#endif 1708232629Sthompsa#ifdef INET6 1709232629Sthompsa struct ip6_hdr ip6; 1710232629Sthompsa#endif 1711232640Sthompsa struct ether_vlan_header vlan; 1712232629Sthompsa uint32_t port; 1713232629Sthompsa } buf; 1714168793Sthompsa 1715232629Sthompsa 1716168793Sthompsa off = sizeof(*eh); 1717168793Sthompsa if (m->m_len < off) 1718168793Sthompsa goto out; 1719168793Sthompsa eh = mtod(m, struct ether_header *); 1720168793Sthompsa etype = ntohs(eh->ether_type); 1721232629Sthompsa if (sc->sc_flags & LAGG_F_HASHL2) { 1722232629Sthompsa p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p); 1723232629Sthompsa p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 1724232629Sthompsa } 1725168793Sthompsa 1726168793Sthompsa /* Special handling for encapsulating VLAN frames */ 1727232629Sthompsa if ((m->m_flags & M_VLANTAG) && (sc->sc_flags & LAGG_F_HASHL2)) { 1728168793Sthompsa p = hash32_buf(&m->m_pkthdr.ether_vtag, 1729168793Sthompsa sizeof(m->m_pkthdr.ether_vtag), p); 1730168793Sthompsa } else if (etype == ETHERTYPE_VLAN) { 1731232640Sthompsa vlan = lagg_gethdr(m, off, sizeof(*vlan), &buf); 1732170599Sthompsa if (vlan == NULL) 1733168793Sthompsa goto out; 1734168793Sthompsa 1735232629Sthompsa if (sc->sc_flags & LAGG_F_HASHL2) 1736232629Sthompsa p = hash32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 1737168793Sthompsa etype = ntohs(vlan->evl_proto); 1738168793Sthompsa off += sizeof(*vlan) - sizeof(*eh); 1739168793Sthompsa } 1740168793Sthompsa 1741168793Sthompsa switch (etype) { 1742168793Sthompsa#ifdef INET 1743168793Sthompsa case ETHERTYPE_IP: 1744232629Sthompsa ip = lagg_gethdr(m, off, sizeof(*ip), &buf); 1745168793Sthompsa if (ip == NULL) 1746168793Sthompsa goto out; 1747168793Sthompsa 1748232629Sthompsa if (sc->sc_flags & LAGG_F_HASHL3) { 1749232629Sthompsa p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p); 1750232629Sthompsa p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 1751232629Sthompsa } 1752232629Sthompsa if (!(sc->sc_flags & LAGG_F_HASHL4)) 1753232629Sthompsa break; 1754232629Sthompsa switch (ip->ip_p) { 1755232629Sthompsa case IPPROTO_TCP: 1756232629Sthompsa case IPPROTO_UDP: 1757232629Sthompsa case IPPROTO_SCTP: 1758232629Sthompsa iphlen = ip->ip_hl << 2; 1759232629Sthompsa if (iphlen < sizeof(*ip)) 1760232629Sthompsa break; 1761232629Sthompsa off += iphlen; 1762232629Sthompsa ports = lagg_gethdr(m, off, sizeof(*ports), &buf); 1763232629Sthompsa if (ports == NULL) 1764232629Sthompsa break; 1765232629Sthompsa p = hash32_buf(ports, sizeof(*ports), p); 1766232629Sthompsa break; 1767232629Sthompsa } 1768168793Sthompsa break; 1769168793Sthompsa#endif 1770168793Sthompsa#ifdef INET6 1771168793Sthompsa case ETHERTYPE_IPV6: 1772232629Sthompsa if (!(sc->sc_flags & LAGG_F_HASHL3)) 1773232629Sthompsa break; 1774232629Sthompsa ip6 = lagg_gethdr(m, off, sizeof(*ip6), &buf); 1775168793Sthompsa if (ip6 == NULL) 1776168793Sthompsa goto out; 1777168793Sthompsa 1778168793Sthompsa p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 1779168793Sthompsa p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 1780169570Sthompsa flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 1781169570Sthompsa p = hash32_buf(&flow, sizeof(flow), p); /* IPv6 flow label */ 1782168793Sthompsa break; 1783168793Sthompsa#endif 1784168793Sthompsa } 1785168793Sthompsaout: 1786168793Sthompsa return (p); 1787168793Sthompsa} 1788168793Sthompsa 1789168793Sthompsaint 1790168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m) 1791168793Sthompsa{ 1792168793Sthompsa 1793185164Skmacy return (ifp->if_transmit)(ifp, m); 1794168793Sthompsa} 1795168793Sthompsa 1796168793Sthompsa/* 1797168793Sthompsa * Simple round robin aggregation 1798168793Sthompsa */ 1799287723Shrsstatic void 1800168793Sthompsalagg_rr_attach(struct lagg_softc *sc) 1801168793Sthompsa{ 1802168793Sthompsa sc->sc_detach = lagg_rr_detach; 1803168793Sthompsa sc->sc_start = lagg_rr_start; 1804168793Sthompsa sc->sc_input = lagg_rr_input; 1805287723Shrs sc->sc_detach = NULL; 1806168793Sthompsa sc->sc_port_create = NULL; 1807168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1808172554Sthompsa sc->sc_seq = 0; 1809168793Sthompsa} 1810168793Sthompsa 1811168793Sthompsastatic int 1812168793Sthompsalagg_rr_detach(struct lagg_softc *sc) 1813168793Sthompsa{ 1814168793Sthompsa return (0); 1815168793Sthompsa} 1816168793Sthompsa 1817168793Sthompsastatic int 1818168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m) 1819168793Sthompsa{ 1820172554Sthompsa struct lagg_port *lp; 1821172554Sthompsa uint32_t p; 1822168793Sthompsa 1823172554Sthompsa p = atomic_fetchadd_32(&sc->sc_seq, 1); 1824172554Sthompsa p %= sc->sc_count; 1825172554Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 1826172554Sthompsa while (p--) 1827172554Sthompsa lp = SLIST_NEXT(lp, lp_entries); 1828172554Sthompsa 1829172554Sthompsa /* 1830172554Sthompsa * Check the port's link state. This will return the next active 1831172554Sthompsa * port if the link is down or the port is NULL. 1832172554Sthompsa */ 1833172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1834172554Sthompsa m_freem(m); 1835251859Sdelphij return (ENETDOWN); 1836172554Sthompsa } 1837168793Sthompsa 1838168793Sthompsa /* Send mbuf */ 1839172554Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1840168793Sthompsa} 1841168793Sthompsa 1842168793Sthompsastatic struct mbuf * 1843168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1844168793Sthompsa{ 1845168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1846168793Sthompsa 1847168793Sthompsa /* Just pass in the packet to our lagg device */ 1848168793Sthompsa m->m_pkthdr.rcvif = ifp; 1849168793Sthompsa 1850168793Sthompsa return (m); 1851168793Sthompsa} 1852168793Sthompsa 1853168793Sthompsa/* 1854168793Sthompsa * Active failover 1855168793Sthompsa */ 1856287723Shrsstatic void 1857168793Sthompsalagg_fail_attach(struct lagg_softc *sc) 1858168793Sthompsa{ 1859168793Sthompsa sc->sc_detach = lagg_fail_detach; 1860168793Sthompsa sc->sc_start = lagg_fail_start; 1861168793Sthompsa sc->sc_input = lagg_fail_input; 1862168793Sthompsa sc->sc_port_create = NULL; 1863168793Sthompsa sc->sc_port_destroy = NULL; 1864287723Shrs sc->sc_detach = NULL; 1865168793Sthompsa} 1866168793Sthompsa 1867168793Sthompsastatic int 1868168793Sthompsalagg_fail_detach(struct lagg_softc *sc) 1869168793Sthompsa{ 1870168793Sthompsa return (0); 1871168793Sthompsa} 1872168793Sthompsa 1873168793Sthompsastatic int 1874168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m) 1875168793Sthompsa{ 1876168793Sthompsa struct lagg_port *lp; 1877168793Sthompsa 1878168793Sthompsa /* Use the master port if active or the next available port */ 1879172554Sthompsa if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) { 1880172554Sthompsa m_freem(m); 1881251859Sdelphij return (ENETDOWN); 1882172554Sthompsa } 1883168793Sthompsa 1884168793Sthompsa /* Send mbuf */ 1885168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1886168793Sthompsa} 1887168793Sthompsa 1888168793Sthompsastatic struct mbuf * 1889168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1890168793Sthompsa{ 1891168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1892168793Sthompsa struct lagg_port *tmp_tp; 1893168793Sthompsa 1894287723Shrs if (lp == sc->sc_primary || V_lagg_failover_rx_all) { 1895168793Sthompsa m->m_pkthdr.rcvif = ifp; 1896168793Sthompsa return (m); 1897168793Sthompsa } 1898168793Sthompsa 1899174742Sthompsa if (!LAGG_PORTACTIVE(sc->sc_primary)) { 1900174742Sthompsa tmp_tp = lagg_link_active(sc, sc->sc_primary); 1901168793Sthompsa /* 1902168793Sthompsa * If tmp_tp is null, we've recieved a packet when all 1903168793Sthompsa * our links are down. Weird, but process it anyways. 1904168793Sthompsa */ 1905168793Sthompsa if ((tmp_tp == NULL || tmp_tp == lp)) { 1906168793Sthompsa m->m_pkthdr.rcvif = ifp; 1907168793Sthompsa return (m); 1908168793Sthompsa } 1909168793Sthompsa } 1910168793Sthompsa 1911168793Sthompsa m_freem(m); 1912168793Sthompsa return (NULL); 1913168793Sthompsa} 1914168793Sthompsa 1915168793Sthompsa/* 1916168793Sthompsa * Loadbalancing 1917168793Sthompsa */ 1918287723Shrsstatic void 1919168793Sthompsalagg_lb_attach(struct lagg_softc *sc) 1920168793Sthompsa{ 1921168793Sthompsa struct lagg_port *lp; 1922168793Sthompsa struct lagg_lb *lb; 1923168793Sthompsa 1924287723Shrs lb = malloc(sizeof(struct lagg_lb), M_DEVBUF, M_WAITOK | M_ZERO); 1925168793Sthompsa 1926168793Sthompsa sc->sc_detach = lagg_lb_detach; 1927168793Sthompsa sc->sc_start = lagg_lb_start; 1928168793Sthompsa sc->sc_input = lagg_lb_input; 1929168793Sthompsa sc->sc_port_create = lagg_lb_port_create; 1930168793Sthompsa sc->sc_port_destroy = lagg_lb_port_destroy; 1931168793Sthompsa sc->sc_capabilities = IFCAP_LAGG_FULLDUPLEX; 1932168793Sthompsa 1933168793Sthompsa lb->lb_key = arc4random(); 1934168793Sthompsa sc->sc_psc = (caddr_t)lb; 1935168793Sthompsa 1936168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1937168793Sthompsa lagg_lb_port_create(lp); 1938168793Sthompsa} 1939168793Sthompsa 1940168793Sthompsastatic int 1941168793Sthompsalagg_lb_detach(struct lagg_softc *sc) 1942168793Sthompsa{ 1943168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1944287723Shrs LAGG_WUNLOCK(sc); 1945168793Sthompsa if (lb != NULL) 1946168793Sthompsa free(lb, M_DEVBUF); 1947168793Sthompsa return (0); 1948168793Sthompsa} 1949168793Sthompsa 1950168793Sthompsastatic int 1951168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp) 1952168793Sthompsa{ 1953168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1954168793Sthompsa struct lagg_port *lp_next; 1955168793Sthompsa int i = 0; 1956168793Sthompsa 1957168793Sthompsa bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 1958168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1959168793Sthompsa if (lp_next == lp) 1960168793Sthompsa continue; 1961168793Sthompsa if (i >= LAGG_MAX_PORTS) 1962168793Sthompsa return (EINVAL); 1963168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1964168793Sthompsa printf("%s: port %s at index %d\n", 1965168793Sthompsa sc->sc_ifname, lp_next->lp_ifname, i); 1966168793Sthompsa lb->lb_ports[i++] = lp_next; 1967168793Sthompsa } 1968168793Sthompsa 1969168793Sthompsa return (0); 1970168793Sthompsa} 1971168793Sthompsa 1972168793Sthompsastatic int 1973168793Sthompsalagg_lb_port_create(struct lagg_port *lp) 1974168793Sthompsa{ 1975170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1976168793Sthompsa return (lagg_lb_porttable(sc, NULL)); 1977168793Sthompsa} 1978168793Sthompsa 1979168793Sthompsastatic void 1980168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp) 1981168793Sthompsa{ 1982170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1983168793Sthompsa lagg_lb_porttable(sc, lp); 1984168793Sthompsa} 1985168793Sthompsa 1986168793Sthompsastatic int 1987168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m) 1988168793Sthompsa{ 1989168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 1990168793Sthompsa struct lagg_port *lp = NULL; 1991168793Sthompsa uint32_t p = 0; 1992168793Sthompsa 1993287723Shrs if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && 1994281955Shiren M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) 1995260179Sscottl p = m->m_pkthdr.flowid >> sc->flowid_shift; 1996191692Sthompsa else 1997232629Sthompsa p = lagg_hashmbuf(sc, m, lb->lb_key); 1998180249Sthompsa p %= sc->sc_count; 1999180249Sthompsa lp = lb->lb_ports[p]; 2000168793Sthompsa 2001168793Sthompsa /* 2002168793Sthompsa * Check the port's link state. This will return the next active 2003168793Sthompsa * port if the link is down or the port is NULL. 2004168793Sthompsa */ 2005172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 2006172554Sthompsa m_freem(m); 2007251859Sdelphij return (ENETDOWN); 2008172554Sthompsa } 2009168793Sthompsa 2010168793Sthompsa /* Send mbuf */ 2011168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 2012168793Sthompsa} 2013168793Sthompsa 2014168793Sthompsastatic struct mbuf * 2015168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 2016168793Sthompsa{ 2017168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 2018168793Sthompsa 2019168793Sthompsa /* Just pass in the packet to our lagg device */ 2020168793Sthompsa m->m_pkthdr.rcvif = ifp; 2021168793Sthompsa 2022168793Sthompsa return (m); 2023168793Sthompsa} 2024168793Sthompsa 2025168793Sthompsa/* 2026168793Sthompsa * 802.3ad LACP 2027168793Sthompsa */ 2028287723Shrsstatic void 2029168793Sthompsalagg_lacp_attach(struct lagg_softc *sc) 2030168793Sthompsa{ 2031168793Sthompsa struct lagg_port *lp; 2032168793Sthompsa 2033168793Sthompsa sc->sc_detach = lagg_lacp_detach; 2034168793Sthompsa sc->sc_port_create = lacp_port_create; 2035168793Sthompsa sc->sc_port_destroy = lacp_port_destroy; 2036168793Sthompsa sc->sc_linkstate = lacp_linkstate; 2037168793Sthompsa sc->sc_start = lagg_lacp_start; 2038168793Sthompsa sc->sc_input = lagg_lacp_input; 2039168793Sthompsa sc->sc_init = lacp_init; 2040168793Sthompsa sc->sc_stop = lacp_stop; 2041168793Sthompsa sc->sc_lladdr = lagg_lacp_lladdr; 2042171247Sthompsa sc->sc_req = lacp_req; 2043171247Sthompsa sc->sc_portreq = lacp_portreq; 2044168793Sthompsa 2045287723Shrs lacp_attach(sc); 2046168793Sthompsa 2047168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2048168793Sthompsa lacp_port_create(lp); 2049168793Sthompsa} 2050168793Sthompsa 2051168793Sthompsastatic int 2052168793Sthompsalagg_lacp_detach(struct lagg_softc *sc) 2053168793Sthompsa{ 2054168793Sthompsa struct lagg_port *lp; 2055287723Shrs void *psc; 2056168793Sthompsa 2057168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2058168793Sthompsa lacp_port_destroy(lp); 2059168793Sthompsa 2060287723Shrs psc = sc->sc_psc; 2061287723Shrs sc->sc_psc = NULL; 2062169569Sthompsa LAGG_WUNLOCK(sc); 2063168793Sthompsa 2064287723Shrs lacp_detach(psc); 2065287723Shrs 2066287723Shrs return (0); 2067168793Sthompsa} 2068168793Sthompsa 2069168793Sthompsastatic void 2070168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc) 2071168793Sthompsa{ 2072168793Sthompsa struct lagg_port *lp; 2073168793Sthompsa 2074168793Sthompsa /* purge all the lacp ports */ 2075168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2076168793Sthompsa lacp_port_destroy(lp); 2077168793Sthompsa 2078168793Sthompsa /* add them back in */ 2079168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2080168793Sthompsa lacp_port_create(lp); 2081168793Sthompsa} 2082168793Sthompsa 2083168793Sthompsastatic int 2084168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m) 2085168793Sthompsa{ 2086168793Sthompsa struct lagg_port *lp; 2087168793Sthompsa 2088168793Sthompsa lp = lacp_select_tx_port(sc, m); 2089172554Sthompsa if (lp == NULL) { 2090172554Sthompsa m_freem(m); 2091245741Sglebius return (ENETDOWN); 2092172554Sthompsa } 2093168793Sthompsa 2094168793Sthompsa /* Send mbuf */ 2095168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 2096168793Sthompsa} 2097168793Sthompsa 2098168793Sthompsastatic struct mbuf * 2099168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 2100168793Sthompsa{ 2101168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 2102168793Sthompsa struct ether_header *eh; 2103168793Sthompsa u_short etype; 2104168793Sthompsa 2105168793Sthompsa eh = mtod(m, struct ether_header *); 2106168793Sthompsa etype = ntohs(eh->ether_type); 2107168793Sthompsa 2108168793Sthompsa /* Tap off LACP control messages */ 2109221270Sthompsa if ((m->m_flags & M_VLANTAG) == 0 && etype == ETHERTYPE_SLOW) { 2110175005Sthompsa m = lacp_input(lp, m); 2111175005Sthompsa if (m == NULL) 2112175005Sthompsa return (NULL); 2113168793Sthompsa } 2114168793Sthompsa 2115168793Sthompsa /* 2116168793Sthompsa * If the port is not collecting or not in the active aggregator then 2117168793Sthompsa * free and return. 2118168793Sthompsa */ 2119177274Sthompsa if (lacp_iscollecting(lp) == 0 || lacp_isactive(lp) == 0) { 2120168793Sthompsa m_freem(m); 2121168793Sthompsa return (NULL); 2122168793Sthompsa } 2123168793Sthompsa 2124168793Sthompsa m->m_pkthdr.rcvif = ifp; 2125168793Sthompsa return (m); 2126168793Sthompsa} 2127249506Sglebius 2128249506Sglebiusstatic void 2129249506Sglebiuslagg_callout(void *arg) 2130249506Sglebius{ 2131249506Sglebius struct lagg_softc *sc = (struct lagg_softc *)arg; 2132249506Sglebius struct ifnet *ifp = sc->sc_ifp; 2133249506Sglebius 2134249506Sglebius ifp->if_ipackets = counter_u64_fetch(sc->sc_ipackets); 2135249506Sglebius ifp->if_opackets = counter_u64_fetch(sc->sc_opackets); 2136249506Sglebius ifp->if_ibytes = counter_u64_fetch(sc->sc_ibytes); 2137249506Sglebius ifp->if_obytes = counter_u64_fetch(sc->sc_obytes); 2138249506Sglebius 2139249506Sglebius callout_reset(&sc->sc_callout, hz, lagg_callout, sc); 2140249506Sglebius} 2141