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> 6294615Saraujo * Copyright (c) 2014, 2016 Marcelo Araujo <araujo@FreeBSD.org> 7168793Sthompsa * 8168793Sthompsa * Permission to use, copy, modify, and distribute this software for any 9168793Sthompsa * purpose with or without fee is hereby granted, provided that the above 10168793Sthompsa * copyright notice and this permission notice appear in all copies. 11168793Sthompsa * 12168793Sthompsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13168793Sthompsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14168793Sthompsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15168793Sthompsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16168793Sthompsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17168793Sthompsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18168793Sthompsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19168793Sthompsa */ 20168793Sthompsa 21168793Sthompsa#include <sys/cdefs.h> 22168793Sthompsa__FBSDID("$FreeBSD: stable/11/sys/net/if_lagg.c 362366 2020-06-19 06:35:57Z rpokala $"); 23168793Sthompsa 24168793Sthompsa#include "opt_inet.h" 25168793Sthompsa#include "opt_inet6.h" 26168793Sthompsa 27168793Sthompsa#include <sys/param.h> 28168793Sthompsa#include <sys/kernel.h> 29168793Sthompsa#include <sys/malloc.h> 30168793Sthompsa#include <sys/mbuf.h> 31168793Sthompsa#include <sys/queue.h> 32168793Sthompsa#include <sys/socket.h> 33168793Sthompsa#include <sys/sockio.h> 34168793Sthompsa#include <sys/sysctl.h> 35168793Sthompsa#include <sys/module.h> 36168793Sthompsa#include <sys/priv.h> 37168793Sthompsa#include <sys/systm.h> 38168793Sthompsa#include <sys/proc.h> 39169569Sthompsa#include <sys/lock.h> 40255038Sadrian#include <sys/rmlock.h> 41318329Smav#include <sys/sx.h> 42169329Sthompsa#include <sys/taskqueue.h> 43203548Seri#include <sys/eventhandler.h> 44168793Sthompsa 45168793Sthompsa#include <net/ethernet.h> 46168793Sthompsa#include <net/if.h> 47168793Sthompsa#include <net/if_clone.h> 48168793Sthompsa#include <net/if_arp.h> 49168793Sthompsa#include <net/if_dl.h> 50168793Sthompsa#include <net/if_media.h> 51168793Sthompsa#include <net/if_types.h> 52168793Sthompsa#include <net/if_var.h> 53168793Sthompsa#include <net/bpf.h> 54272386Shrs#include <net/vnet.h> 55168793Sthompsa 56221130Sbz#if defined(INET) || defined(INET6) 57221130Sbz#include <netinet/in.h> 58264498Srmacklem#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 85272386ShrsVNET_DEFINE(SLIST_HEAD(__trhead, lagg_softc), lagg_list); /* list of laggs */ 86272386Shrs#define V_lagg_list VNET(lagg_list) 87272386Shrsstatic VNET_DEFINE(struct mtx, lagg_list_mtx); 88272386Shrs#define V_lagg_list_mtx VNET(lagg_list_mtx) 89272386Shrs#define LAGG_LIST_LOCK_INIT(x) mtx_init(&V_lagg_list_mtx, \ 90272386Shrs "if_lagg list", NULL, MTX_DEF) 91272386Shrs#define LAGG_LIST_LOCK_DESTROY(x) mtx_destroy(&V_lagg_list_mtx) 92272386Shrs#define LAGG_LIST_LOCK(x) mtx_lock(&V_lagg_list_mtx) 93272386Shrs#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 *); 98272386Shrsstatic VNET_DEFINE(struct if_clone *, lagg_cloner); 99272386Shrs#define V_lagg_cloner VNET(lagg_cloner) 100241610Sglebiusstatic const char laggname[] = "lagg"; 101241610Sglebius 102171661Sthompsastatic void lagg_capabilities(struct lagg_softc *); 103168793Sthompsastatic int lagg_port_create(struct lagg_softc *, struct ifnet *); 104168793Sthompsastatic int lagg_port_destroy(struct lagg_port *, int); 105168793Sthompsastatic struct mbuf *lagg_input(struct ifnet *, struct mbuf *); 106173895Sthompsastatic void lagg_linkstate(struct lagg_softc *); 107292402Ssmhstatic void lagg_port_state(struct ifnet *, int); 108168793Sthompsastatic int lagg_port_ioctl(struct ifnet *, u_long, caddr_t); 109168793Sthompsastatic int lagg_port_output(struct ifnet *, struct mbuf *, 110249925Sglebius const struct sockaddr *, struct route *); 111168793Sthompsastatic void lagg_port_ifdetach(void *arg __unused, struct ifnet *); 112201803Strasz#ifdef LAGG_PORT_STACKING 113168793Sthompsastatic int lagg_port_checkstacking(struct lagg_softc *); 114201803Strasz#endif 115168793Sthompsastatic void lagg_port2req(struct lagg_port *, struct lagg_reqport *); 116168793Sthompsastatic void lagg_init(void *); 117168793Sthompsastatic void lagg_stop(struct lagg_softc *); 118168793Sthompsastatic int lagg_ioctl(struct ifnet *, u_long, caddr_t); 119318329Smavstatic int lagg_setmulti(struct lagg_port *); 120318329Smavstatic int lagg_clrmulti(struct lagg_port *); 121318329Smavstatic int lagg_setcaps(struct lagg_port *, int cap); 122168793Sthompsastatic int lagg_setflag(struct lagg_port *, int, int, 123168793Sthompsa int (*func)(struct ifnet *, int)); 124168793Sthompsastatic int lagg_setflags(struct lagg_port *, int status); 125272211Smelifarostatic uint64_t lagg_get_counter(struct ifnet *ifp, ift_counter cnt); 126240742Sglebiusstatic int lagg_transmit(struct ifnet *, struct mbuf *); 127240742Sglebiusstatic void lagg_qflush(struct ifnet *); 128168793Sthompsastatic int lagg_media_change(struct ifnet *); 129168793Sthompsastatic void lagg_media_status(struct ifnet *, struct ifmediareq *); 130168793Sthompsastatic struct lagg_port *lagg_link_active(struct lagg_softc *, 131168793Sthompsa struct lagg_port *); 132168793Sthompsa 133168793Sthompsa/* Simple round robin */ 134272161Sglebiusstatic void lagg_rr_attach(struct lagg_softc *); 135168793Sthompsastatic int lagg_rr_start(struct lagg_softc *, struct mbuf *); 136168793Sthompsastatic struct mbuf *lagg_rr_input(struct lagg_softc *, struct lagg_port *, 137168793Sthompsa struct mbuf *); 138168793Sthompsa 139168793Sthompsa/* Active failover */ 140168793Sthompsastatic int lagg_fail_start(struct lagg_softc *, struct mbuf *); 141168793Sthompsastatic struct mbuf *lagg_fail_input(struct lagg_softc *, struct lagg_port *, 142168793Sthompsa struct mbuf *); 143168793Sthompsa 144168793Sthompsa/* Loadbalancing */ 145272161Sglebiusstatic void lagg_lb_attach(struct lagg_softc *); 146272158Sglebiusstatic void lagg_lb_detach(struct lagg_softc *); 147168793Sthompsastatic int lagg_lb_port_create(struct lagg_port *); 148168793Sthompsastatic void lagg_lb_port_destroy(struct lagg_port *); 149168793Sthompsastatic int lagg_lb_start(struct lagg_softc *, struct mbuf *); 150168793Sthompsastatic struct mbuf *lagg_lb_input(struct lagg_softc *, struct lagg_port *, 151168793Sthompsa struct mbuf *); 152168793Sthompsastatic int lagg_lb_porttable(struct lagg_softc *, struct lagg_port *); 153168793Sthompsa 154271732Saraujo/* Broadcast */ 155271732Saraujostatic int lagg_bcast_start(struct lagg_softc *, struct mbuf *); 156271732Saraujostatic struct mbuf *lagg_bcast_input(struct lagg_softc *, struct lagg_port *, 157272175Sglebius struct mbuf *); 158271732Saraujo 159168793Sthompsa/* 802.3ad LACP */ 160272161Sglebiusstatic void lagg_lacp_attach(struct lagg_softc *); 161272158Sglebiusstatic void 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 167168793Sthompsa/* lagg protocol table */ 168272161Sglebiusstatic const struct lagg_proto { 169272170Sglebius lagg_proto pr_num; 170272170Sglebius void (*pr_attach)(struct lagg_softc *); 171272170Sglebius void (*pr_detach)(struct lagg_softc *); 172272178Sglebius int (*pr_start)(struct lagg_softc *, struct mbuf *); 173272178Sglebius struct mbuf * (*pr_input)(struct lagg_softc *, struct lagg_port *, 174272178Sglebius struct mbuf *); 175272178Sglebius int (*pr_addport)(struct lagg_port *); 176272178Sglebius void (*pr_delport)(struct lagg_port *); 177272178Sglebius void (*pr_linkstate)(struct lagg_port *); 178272178Sglebius void (*pr_init)(struct lagg_softc *); 179272178Sglebius void (*pr_stop)(struct lagg_softc *); 180272178Sglebius void (*pr_lladdr)(struct lagg_softc *); 181272178Sglebius void (*pr_request)(struct lagg_softc *, void *); 182272178Sglebius void (*pr_portreq)(struct lagg_port *, void *); 183168793Sthompsa} lagg_protos[] = { 184272170Sglebius { 185272170Sglebius .pr_num = LAGG_PROTO_NONE 186272170Sglebius }, 187272170Sglebius { 188272170Sglebius .pr_num = LAGG_PROTO_ROUNDROBIN, 189272170Sglebius .pr_attach = lagg_rr_attach, 190272178Sglebius .pr_start = lagg_rr_start, 191272178Sglebius .pr_input = lagg_rr_input, 192272170Sglebius }, 193272170Sglebius { 194272170Sglebius .pr_num = LAGG_PROTO_FAILOVER, 195272178Sglebius .pr_start = lagg_fail_start, 196272178Sglebius .pr_input = lagg_fail_input, 197272170Sglebius }, 198272170Sglebius { 199272170Sglebius .pr_num = LAGG_PROTO_LOADBALANCE, 200272170Sglebius .pr_attach = lagg_lb_attach, 201272170Sglebius .pr_detach = lagg_lb_detach, 202272178Sglebius .pr_start = lagg_lb_start, 203272178Sglebius .pr_input = lagg_lb_input, 204272178Sglebius .pr_addport = lagg_lb_port_create, 205272178Sglebius .pr_delport = lagg_lb_port_destroy, 206272170Sglebius }, 207272170Sglebius { 208272170Sglebius .pr_num = LAGG_PROTO_LACP, 209272170Sglebius .pr_attach = lagg_lacp_attach, 210272170Sglebius .pr_detach = lagg_lacp_detach, 211272178Sglebius .pr_start = lagg_lacp_start, 212272178Sglebius .pr_input = lagg_lacp_input, 213272178Sglebius .pr_addport = lacp_port_create, 214272178Sglebius .pr_delport = lacp_port_destroy, 215272178Sglebius .pr_linkstate = lacp_linkstate, 216272178Sglebius .pr_init = lacp_init, 217272178Sglebius .pr_stop = lacp_stop, 218272178Sglebius .pr_lladdr = lagg_lacp_lladdr, 219272178Sglebius .pr_request = lacp_req, 220272178Sglebius .pr_portreq = lacp_portreq, 221272170Sglebius }, 222272170Sglebius { 223272170Sglebius .pr_num = LAGG_PROTO_BROADCAST, 224272178Sglebius .pr_start = lagg_bcast_start, 225272178Sglebius .pr_input = lagg_bcast_input, 226272170Sglebius }, 227168793Sthompsa}; 228168793Sthompsa 229212100SemasteSYSCTL_DECL(_net_link); 230253314SadrianSYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0, 231227309Sed "Link Aggregation"); 232212100Semaste 233272386Shrs/* Allow input on any failover links */ 234272386Shrsstatic VNET_DEFINE(int, lagg_failover_rx_all); 235272386Shrs#define V_lagg_failover_rx_all VNET(lagg_failover_rx_all) 236272386ShrsSYSCTL_INT(_net_link_lagg, OID_AUTO, failover_rx_all, CTLFLAG_RW | CTLFLAG_VNET, 237272386Shrs &VNET_NAME(lagg_failover_rx_all), 0, 238212100Semaste "Accept input from any interface in a failover lagg"); 239272386Shrs 240275358Shselasky/* Default value for using flowid */ 241332318Ssmhstatic VNET_DEFINE(int, def_use_flowid) = 0; 242272386Shrs#define V_def_use_flowid VNET(def_use_flowid) 243267992ShselaskySYSCTL_INT(_net_link_lagg, OID_AUTO, default_use_flowid, CTLFLAG_RWTUN, 244272386Shrs &VNET_NAME(def_use_flowid), 0, 245232080Sthompsa "Default setting for using flow id for load sharing"); 246272386Shrs 247275358Shselasky/* Default value for flowid shift */ 248272386Shrsstatic VNET_DEFINE(int, def_flowid_shift) = 16; 249272386Shrs#define V_def_flowid_shift VNET(def_flowid_shift) 250267992ShselaskySYSCTL_INT(_net_link_lagg, OID_AUTO, default_flowid_shift, CTLFLAG_RWTUN, 251272386Shrs &VNET_NAME(def_flowid_shift), 0, 252260070Sscottl "Default setting for flowid shift for load sharing"); 253212100Semaste 254272386Shrsstatic void 255272386Shrsvnet_lagg_init(const void *unused __unused) 256272386Shrs{ 257272386Shrs 258272386Shrs LAGG_LIST_LOCK_INIT(); 259272386Shrs SLIST_INIT(&V_lagg_list); 260272386Shrs V_lagg_cloner = if_clone_simple(laggname, lagg_clone_create, 261272386Shrs lagg_clone_destroy, 0); 262272386Shrs} 263272386ShrsVNET_SYSINIT(vnet_lagg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 264272386Shrs vnet_lagg_init, NULL); 265272386Shrs 266272386Shrsstatic void 267272386Shrsvnet_lagg_uninit(const void *unused __unused) 268272386Shrs{ 269272386Shrs 270272386Shrs if_clone_detach(V_lagg_cloner); 271272386Shrs LAGG_LIST_LOCK_DESTROY(); 272272386Shrs} 273302054SbzVNET_SYSUNINIT(vnet_lagg_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, 274272386Shrs vnet_lagg_uninit, NULL); 275272386Shrs 276168793Sthompsastatic int 277168793Sthompsalagg_modevent(module_t mod, int type, void *data) 278168793Sthompsa{ 279168793Sthompsa 280168793Sthompsa switch (type) { 281168793Sthompsa case MOD_LOAD: 282168793Sthompsa lagg_input_p = lagg_input; 283168793Sthompsa lagg_linkstate_p = lagg_port_state; 284168793Sthompsa lagg_detach_cookie = EVENTHANDLER_REGISTER( 285168793Sthompsa ifnet_departure_event, lagg_port_ifdetach, NULL, 286168793Sthompsa EVENTHANDLER_PRI_ANY); 287168793Sthompsa break; 288168793Sthompsa case MOD_UNLOAD: 289168793Sthompsa EVENTHANDLER_DEREGISTER(ifnet_departure_event, 290168793Sthompsa lagg_detach_cookie); 291168793Sthompsa lagg_input_p = NULL; 292168793Sthompsa lagg_linkstate_p = NULL; 293168793Sthompsa break; 294168793Sthompsa default: 295168793Sthompsa return (EOPNOTSUPP); 296168793Sthompsa } 297168793Sthompsa return (0); 298168793Sthompsa} 299168793Sthompsa 300168793Sthompsastatic moduledata_t lagg_mod = { 301168793Sthompsa "if_lagg", 302168793Sthompsa lagg_modevent, 303241394Skevlo 0 304168793Sthompsa}; 305168793Sthompsa 306168793SthompsaDECLARE_MODULE(if_lagg, lagg_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 307224571SpluknetMODULE_VERSION(if_lagg, 1); 308168793Sthompsa 309272170Sglebiusstatic void 310272170Sglebiuslagg_proto_attach(struct lagg_softc *sc, lagg_proto pr) 311272170Sglebius{ 312272170Sglebius 313318329Smav LAGG_XLOCK_ASSERT(sc); 314272170Sglebius KASSERT(sc->sc_proto == LAGG_PROTO_NONE, ("%s: sc %p has proto", 315272170Sglebius __func__, sc)); 316272170Sglebius 317272170Sglebius if (sc->sc_ifflags & IFF_DEBUG) 318272170Sglebius if_printf(sc->sc_ifp, "using proto %u\n", pr); 319272170Sglebius 320272178Sglebius if (lagg_protos[pr].pr_attach != NULL) 321272178Sglebius lagg_protos[pr].pr_attach(sc); 322272170Sglebius sc->sc_proto = pr; 323272170Sglebius} 324272170Sglebius 325272170Sglebiusstatic void 326272170Sglebiuslagg_proto_detach(struct lagg_softc *sc) 327272170Sglebius{ 328272170Sglebius lagg_proto pr; 329272170Sglebius 330318329Smav LAGG_XLOCK_ASSERT(sc); 331272170Sglebius LAGG_WLOCK_ASSERT(sc); 332272170Sglebius pr = sc->sc_proto; 333272175Sglebius sc->sc_proto = LAGG_PROTO_NONE; 334272170Sglebius 335272170Sglebius if (lagg_protos[pr].pr_detach != NULL) 336272170Sglebius lagg_protos[pr].pr_detach(sc); 337272170Sglebius else 338272170Sglebius LAGG_WUNLOCK(sc); 339272170Sglebius} 340272170Sglebius 341272178Sglebiusstatic int 342272178Sglebiuslagg_proto_start(struct lagg_softc *sc, struct mbuf *m) 343272178Sglebius{ 344272178Sglebius 345272178Sglebius return (lagg_protos[sc->sc_proto].pr_start(sc, m)); 346272178Sglebius} 347272178Sglebius 348272178Sglebiusstatic struct mbuf * 349272178Sglebiuslagg_proto_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 350272178Sglebius{ 351272178Sglebius 352272178Sglebius return (lagg_protos[sc->sc_proto].pr_input(sc, lp, m)); 353272178Sglebius} 354272178Sglebius 355272178Sglebiusstatic int 356272178Sglebiuslagg_proto_addport(struct lagg_softc *sc, struct lagg_port *lp) 357272178Sglebius{ 358272178Sglebius 359272178Sglebius if (lagg_protos[sc->sc_proto].pr_addport == NULL) 360272178Sglebius return (0); 361272178Sglebius else 362272178Sglebius return (lagg_protos[sc->sc_proto].pr_addport(lp)); 363272178Sglebius} 364272178Sglebius 365272178Sglebiusstatic void 366272178Sglebiuslagg_proto_delport(struct lagg_softc *sc, struct lagg_port *lp) 367272178Sglebius{ 368272178Sglebius 369272178Sglebius if (lagg_protos[sc->sc_proto].pr_delport != NULL) 370272178Sglebius lagg_protos[sc->sc_proto].pr_delport(lp); 371272178Sglebius} 372272178Sglebius 373272178Sglebiusstatic void 374272178Sglebiuslagg_proto_linkstate(struct lagg_softc *sc, struct lagg_port *lp) 375272178Sglebius{ 376272178Sglebius 377272178Sglebius if (lagg_protos[sc->sc_proto].pr_linkstate != NULL) 378272178Sglebius lagg_protos[sc->sc_proto].pr_linkstate(lp); 379272178Sglebius} 380272178Sglebius 381272178Sglebiusstatic void 382272178Sglebiuslagg_proto_init(struct lagg_softc *sc) 383272178Sglebius{ 384272178Sglebius 385272178Sglebius if (lagg_protos[sc->sc_proto].pr_init != NULL) 386272178Sglebius lagg_protos[sc->sc_proto].pr_init(sc); 387272178Sglebius} 388272178Sglebius 389272178Sglebiusstatic void 390272178Sglebiuslagg_proto_stop(struct lagg_softc *sc) 391272178Sglebius{ 392272178Sglebius 393272178Sglebius if (lagg_protos[sc->sc_proto].pr_stop != NULL) 394272178Sglebius lagg_protos[sc->sc_proto].pr_stop(sc); 395272178Sglebius} 396272178Sglebius 397272178Sglebiusstatic void 398272178Sglebiuslagg_proto_lladdr(struct lagg_softc *sc) 399272178Sglebius{ 400272178Sglebius 401272178Sglebius if (lagg_protos[sc->sc_proto].pr_lladdr != NULL) 402272178Sglebius lagg_protos[sc->sc_proto].pr_lladdr(sc); 403272178Sglebius} 404272178Sglebius 405272178Sglebiusstatic void 406272178Sglebiuslagg_proto_request(struct lagg_softc *sc, void *v) 407272178Sglebius{ 408272178Sglebius 409272178Sglebius if (lagg_protos[sc->sc_proto].pr_request != NULL) 410272178Sglebius lagg_protos[sc->sc_proto].pr_request(sc, v); 411272178Sglebius} 412272178Sglebius 413272178Sglebiusstatic void 414272178Sglebiuslagg_proto_portreq(struct lagg_softc *sc, struct lagg_port *lp, void *v) 415272178Sglebius{ 416272178Sglebius 417272178Sglebius if (lagg_protos[sc->sc_proto].pr_portreq != NULL) 418272178Sglebius lagg_protos[sc->sc_proto].pr_portreq(lp, v); 419272178Sglebius} 420272178Sglebius 421203548Seri/* 422203548Seri * This routine is run via an vlan 423203548Seri * config EVENT 424203548Seri */ 425203548Seristatic void 426203548Serilagg_register_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) 427203548Seri{ 428272175Sglebius struct lagg_softc *sc = ifp->if_softc; 429272175Sglebius struct lagg_port *lp; 430203548Seri 431272175Sglebius if (ifp->if_softc != arg) /* Not our event */ 432272175Sglebius return; 433203548Seri 434318329Smav LAGG_SLOCK(sc); 435317699Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 436317699Smav EVENTHANDLER_INVOKE(vlan_config, lp->lp_ifp, vtag); 437318329Smav LAGG_SUNLOCK(sc); 438203548Seri} 439203548Seri 440203548Seri/* 441203548Seri * This routine is run via an vlan 442203548Seri * unconfig EVENT 443203548Seri */ 444203548Seristatic void 445203548Serilagg_unregister_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) 446203548Seri{ 447272175Sglebius struct lagg_softc *sc = ifp->if_softc; 448272175Sglebius struct lagg_port *lp; 449203548Seri 450272175Sglebius if (ifp->if_softc != arg) /* Not our event */ 451272175Sglebius return; 452203548Seri 453318329Smav LAGG_SLOCK(sc); 454317699Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 455317699Smav EVENTHANDLER_INVOKE(vlan_unconfig, lp->lp_ifp, vtag); 456318329Smav LAGG_SUNLOCK(sc); 457203548Seri} 458203548Seri 459168793Sthompsastatic int 460168793Sthompsalagg_clone_create(struct if_clone *ifc, int unit, caddr_t params) 461168793Sthompsa{ 462168793Sthompsa struct lagg_softc *sc; 463168793Sthompsa struct ifnet *ifp; 464168793Sthompsa static const u_char eaddr[6]; /* 00:00:00:00:00:00 */ 465168793Sthompsa 466168793Sthompsa sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 467168793Sthompsa ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 468168793Sthompsa if (ifp == NULL) { 469168793Sthompsa free(sc, M_DEVBUF); 470168793Sthompsa return (ENOSPC); 471168793Sthompsa } 472318329Smav LAGG_LOCK_INIT(sc); 473318329Smav LAGG_SX_INIT(sc); 474168793Sthompsa 475318329Smav LAGG_XLOCK(sc); 476272386Shrs if (V_def_use_flowid) 477272386Shrs sc->sc_opts |= LAGG_OPT_USE_FLOWID; 478272386Shrs sc->flowid_shift = V_def_flowid_shift; 479272386Shrs 480232629Sthompsa /* Hash all layers by default */ 481279891Shselasky sc->sc_flags = MBUF_HASHFLAG_L2|MBUF_HASHFLAG_L3|MBUF_HASHFLAG_L4; 482232008Sthompsa 483272170Sglebius lagg_proto_attach(sc, LAGG_PROTO_DEFAULT); 484272170Sglebius 485168793Sthompsa SLIST_INIT(&sc->sc_ports); 486168793Sthompsa 487168793Sthompsa /* Initialise pseudo media types */ 488168793Sthompsa ifmedia_init(&sc->sc_media, 0, lagg_media_change, 489168793Sthompsa lagg_media_status); 490168793Sthompsa ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); 491168793Sthompsa ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); 492168793Sthompsa 493241610Sglebius if_initname(ifp, laggname, unit); 494168793Sthompsa ifp->if_softc = sc; 495240742Sglebius ifp->if_transmit = lagg_transmit; 496240742Sglebius ifp->if_qflush = lagg_qflush; 497168793Sthompsa ifp->if_init = lagg_init; 498168793Sthompsa ifp->if_ioctl = lagg_ioctl; 499272211Smelifaro ifp->if_get_counter = lagg_get_counter; 500168793Sthompsa ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 501256218Sglebius ifp->if_capenable = ifp->if_capabilities = IFCAP_HWSTATS; 502168793Sthompsa 503168793Sthompsa /* 504227459Sbrooks * Attach as an ordinary ethernet device, children will be attached 505168793Sthompsa * as special device IFT_IEEE8023ADLAG. 506168793Sthompsa */ 507168793Sthompsa ether_ifattach(ifp, eaddr); 508168793Sthompsa 509203548Seri sc->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, 510203548Seri lagg_register_vlan, sc, EVENTHANDLER_PRI_FIRST); 511203548Seri sc->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, 512203548Seri lagg_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST); 513203548Seri 514168793Sthompsa /* Insert into the global list of laggs */ 515272386Shrs LAGG_LIST_LOCK(); 516272386Shrs SLIST_INSERT_HEAD(&V_lagg_list, sc, sc_entries); 517272386Shrs LAGG_LIST_UNLOCK(); 518318329Smav LAGG_XUNLOCK(sc); 519168793Sthompsa 520168793Sthompsa return (0); 521168793Sthompsa} 522168793Sthompsa 523168793Sthompsastatic void 524168793Sthompsalagg_clone_destroy(struct ifnet *ifp) 525168793Sthompsa{ 526168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 527168793Sthompsa struct lagg_port *lp; 528168793Sthompsa 529318329Smav LAGG_XLOCK(sc); 530318329Smav sc->sc_destroying = 1; 531168793Sthompsa lagg_stop(sc); 532168793Sthompsa ifp->if_flags &= ~IFF_UP; 533168793Sthompsa 534203548Seri EVENTHANDLER_DEREGISTER(vlan_config, sc->vlan_attach); 535203548Seri EVENTHANDLER_DEREGISTER(vlan_unconfig, sc->vlan_detach); 536203548Seri 537168793Sthompsa /* Shutdown and remove lagg ports */ 538318329Smav while ((lp = SLIST_FIRST(&sc->sc_ports)) != NULL) 539168793Sthompsa lagg_port_destroy(lp, 1); 540318329Smav 541168793Sthompsa /* Unhook the aggregation protocol */ 542318329Smav LAGG_WLOCK(sc); 543272170Sglebius lagg_proto_detach(sc); 544290239Smelifaro LAGG_UNLOCK_ASSERT(sc); 545318329Smav LAGG_XUNLOCK(sc); 546168793Sthompsa 547168793Sthompsa ifmedia_removeall(&sc->sc_media); 548168793Sthompsa ether_ifdetach(ifp); 549227459Sbrooks if_free(ifp); 550168793Sthompsa 551272386Shrs LAGG_LIST_LOCK(); 552272386Shrs SLIST_REMOVE(&V_lagg_list, sc, lagg_softc, sc_entries); 553272386Shrs LAGG_LIST_UNLOCK(); 554168793Sthompsa 555318329Smav LAGG_SX_DESTROY(sc); 556168793Sthompsa LAGG_LOCK_DESTROY(sc); 557168793Sthompsa free(sc, M_DEVBUF); 558168793Sthompsa} 559168793Sthompsa 560171661Sthompsastatic void 561168793Sthompsalagg_capabilities(struct lagg_softc *sc) 562168793Sthompsa{ 563168793Sthompsa struct lagg_port *lp; 564319696Smav int cap, ena, pena; 565319696Smav uint64_t hwa; 566271946Shselasky struct ifnet_hw_tsomax hw_tsomax; 567168793Sthompsa 568318329Smav LAGG_XLOCK_ASSERT(sc); 569168793Sthompsa 570319696Smav /* Get common enabled capabilities for the lagg ports */ 571319696Smav ena = ~0; 572319696Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 573319696Smav ena &= lp->lp_ifp->if_capenable; 574319696Smav ena = (ena == ~0 ? 0 : ena); 575319696Smav 576319696Smav /* 577319696Smav * Apply common enabled capabilities back to the lagg ports. 578319696Smav * May require several iterations if they are dependent. 579319696Smav */ 580319696Smav do { 581319696Smav pena = ena; 582319696Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 583319696Smav lagg_setcaps(lp, ena); 584319696Smav ena &= lp->lp_ifp->if_capenable; 585319696Smav } 586319696Smav } while (pena != ena); 587319696Smav 588319696Smav /* Get other capabilities from the lagg ports */ 589319696Smav cap = ~0; 590319696Smav hwa = ~(uint64_t)0; 591271946Shselasky memset(&hw_tsomax, 0, sizeof(hw_tsomax)); 592171661Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 593171661Sthompsa cap &= lp->lp_ifp->if_capabilities; 594186195Sthompsa hwa &= lp->lp_ifp->if_hwassist; 595271946Shselasky if_hw_tsomax_common(lp->lp_ifp, &hw_tsomax); 596171661Sthompsa } 597171661Sthompsa cap = (cap == ~0 ? 0 : cap); 598319696Smav hwa = (hwa == ~(uint64_t)0 ? 0 : hwa); 599168793Sthompsa 600171661Sthompsa if (sc->sc_ifp->if_capabilities != cap || 601186195Sthompsa sc->sc_ifp->if_capenable != ena || 602264469Srmacklem sc->sc_ifp->if_hwassist != hwa || 603271946Shselasky if_hw_tsomax_update(sc->sc_ifp, &hw_tsomax) != 0) { 604171661Sthompsa sc->sc_ifp->if_capabilities = cap; 605171661Sthompsa sc->sc_ifp->if_capenable = ena; 606186195Sthompsa sc->sc_ifp->if_hwassist = hwa; 607171661Sthompsa getmicrotime(&sc->sc_ifp->if_lastchange); 608171661Sthompsa 609171661Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 610171661Sthompsa if_printf(sc->sc_ifp, 611171661Sthompsa "capabilities 0x%08x enabled 0x%08x\n", cap, ena); 612168793Sthompsa } 613168793Sthompsa} 614168793Sthompsa 615168793Sthompsastatic int 616168793Sthompsalagg_port_create(struct lagg_softc *sc, struct ifnet *ifp) 617168793Sthompsa{ 618168793Sthompsa struct lagg_softc *sc_ptr; 619272176Sae struct lagg_port *lp, *tlp; 620342206Smav struct ifreq ifr; 621342206Smav int error, i, oldmtu; 622272211Smelifaro uint64_t *pval; 623168793Sthompsa 624318329Smav LAGG_XLOCK_ASSERT(sc); 625168793Sthompsa 626342206Smav if (sc->sc_ifp == ifp) { 627342206Smav if_printf(sc->sc_ifp, 628342206Smav "cannot add a lagg to itself as a port\n"); 629342206Smav return (EINVAL); 630342206Smav } 631342206Smav 632168793Sthompsa /* Limit the maximal number of lagg ports */ 633168793Sthompsa if (sc->sc_count >= LAGG_MAX_PORTS) 634168793Sthompsa return (ENOSPC); 635168793Sthompsa 636168793Sthompsa /* Check if port has already been associated to a lagg */ 637236178Srea if (ifp->if_lagg != NULL) { 638236178Srea /* Port is already in the current lagg? */ 639236178Srea lp = (struct lagg_port *)ifp->if_lagg; 640236178Srea if (lp->lp_softc == sc) 641236178Srea return (EEXIST); 642168793Sthompsa return (EBUSY); 643236178Srea } 644168793Sthompsa 645168793Sthompsa /* XXX Disallow non-ethernet interfaces (this should be any of 802) */ 646290239Smelifaro if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN) 647168793Sthompsa return (EPROTONOSUPPORT); 648168793Sthompsa 649171661Sthompsa /* Allow the first Ethernet member to define the MTU */ 650342206Smav oldmtu = -1; 651342206Smav if (SLIST_EMPTY(&sc->sc_ports)) { 652171661Sthompsa sc->sc_ifp->if_mtu = ifp->if_mtu; 653342206Smav } else if (sc->sc_ifp->if_mtu != ifp->if_mtu) { 654342206Smav if (ifp->if_ioctl == NULL) { 655342206Smav if_printf(sc->sc_ifp, "cannot change MTU for %s\n", 656342206Smav ifp->if_xname); 657342206Smav return (EINVAL); 658342206Smav } 659342206Smav oldmtu = ifp->if_mtu; 660342206Smav strlcpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); 661342206Smav ifr.ifr_mtu = sc->sc_ifp->if_mtu; 662342206Smav error = (*ifp->if_ioctl)(ifp, SIOCSIFMTU, (caddr_t)&ifr); 663342206Smav if (error != 0) { 664342206Smav if_printf(sc->sc_ifp, "invalid MTU for %s\n", 665342206Smav ifp->if_xname); 666342206Smav return (error); 667342206Smav } 668342206Smav ifr.ifr_mtu = oldmtu; 669171661Sthompsa } 670171661Sthompsa 671318329Smav lp = malloc(sizeof(struct lagg_port), M_DEVBUF, M_WAITOK|M_ZERO); 672318329Smav lp->lp_softc = sc; 673168793Sthompsa 674168793Sthompsa /* Check if port is a stacked lagg */ 675272386Shrs LAGG_LIST_LOCK(); 676272386Shrs SLIST_FOREACH(sc_ptr, &V_lagg_list, sc_entries) { 677168793Sthompsa if (ifp == sc_ptr->sc_ifp) { 678272386Shrs LAGG_LIST_UNLOCK(); 679168793Sthompsa free(lp, M_DEVBUF); 680342206Smav if (oldmtu != -1) 681342206Smav (*ifp->if_ioctl)(ifp, SIOCSIFMTU, 682342206Smav (caddr_t)&ifr); 683168793Sthompsa return (EINVAL); 684201803Strasz /* XXX disable stacking for the moment, its untested */ 685201803Strasz#ifdef LAGG_PORT_STACKING 686168793Sthompsa lp->lp_flags |= LAGG_PORT_STACK; 687168793Sthompsa if (lagg_port_checkstacking(sc_ptr) >= 688168793Sthompsa LAGG_MAX_STACKING) { 689272386Shrs LAGG_LIST_UNLOCK(); 690168793Sthompsa free(lp, M_DEVBUF); 691342206Smav if (oldmtu != -1) 692342206Smav (*ifp->if_ioctl)(ifp, SIOCSIFMTU, 693342206Smav (caddr_t)&ifr); 694168793Sthompsa return (E2BIG); 695168793Sthompsa } 696201803Strasz#endif 697168793Sthompsa } 698168793Sthompsa } 699272386Shrs LAGG_LIST_UNLOCK(); 700168793Sthompsa 701318329Smav if_ref(ifp); 702318329Smav lp->lp_ifp = ifp; 703318329Smav 704318329Smav bcopy(IF_LLADDR(ifp), lp->lp_lladdr, ETHER_ADDR_LEN); 705318329Smav lp->lp_ifcapenable = ifp->if_capenable; 706318329Smav if (SLIST_EMPTY(&sc->sc_ports)) { 707318329Smav LAGG_WLOCK(sc); 708318329Smav bcopy(IF_LLADDR(ifp), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 709318329Smav lagg_proto_lladdr(sc); 710318329Smav LAGG_WUNLOCK(sc); 711318329Smav EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); 712318329Smav } else { 713318329Smav if_setlladdr(ifp, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 714318329Smav } 715318329Smav lagg_setflags(lp, 1); 716318329Smav 717318329Smav LAGG_WLOCK(sc); 718318329Smav if (SLIST_EMPTY(&sc->sc_ports)) 719318329Smav sc->sc_primary = lp; 720318329Smav 721168793Sthompsa /* Change the interface type */ 722168793Sthompsa lp->lp_iftype = ifp->if_type; 723168793Sthompsa ifp->if_type = IFT_IEEE8023ADLAG; 724168793Sthompsa ifp->if_lagg = lp; 725168793Sthompsa lp->lp_ioctl = ifp->if_ioctl; 726168793Sthompsa ifp->if_ioctl = lagg_port_ioctl; 727168793Sthompsa lp->lp_output = ifp->if_output; 728168793Sthompsa ifp->if_output = lagg_port_output; 729168793Sthompsa 730318329Smav /* Read port counters */ 731318329Smav pval = lp->port_counters.val; 732318329Smav for (i = 0; i < IFCOUNTERS; i++, pval++) 733318329Smav *pval = ifp->if_get_counter(ifp, i); 734168793Sthompsa 735277295Sae /* 736277295Sae * Insert into the list of ports. 737277295Sae * Keep ports sorted by if_index. It is handy, when configuration 738277295Sae * is predictable and `ifconfig laggN create ...` command 739277295Sae * will lead to the same result each time. 740277295Sae */ 741272176Sae SLIST_FOREACH(tlp, &sc->sc_ports, lp_entries) { 742272176Sae if (tlp->lp_ifp->if_index < ifp->if_index && ( 743272176Sae SLIST_NEXT(tlp, lp_entries) == NULL || 744277295Sae SLIST_NEXT(tlp, lp_entries)->lp_ifp->if_index > 745272176Sae ifp->if_index)) 746272176Sae break; 747272176Sae } 748272176Sae if (tlp != NULL) 749272176Sae SLIST_INSERT_AFTER(tlp, lp, lp_entries); 750272176Sae else 751272176Sae SLIST_INSERT_HEAD(&sc->sc_ports, lp, lp_entries); 752168793Sthompsa sc->sc_count++; 753168793Sthompsa 754318329Smav lagg_setmulti(lp); 755168793Sthompsa 756272178Sglebius if ((error = lagg_proto_addport(sc, lp)) != 0) { 757272178Sglebius /* Remove the port, without calling pr_delport. */ 758168793Sthompsa lagg_port_destroy(lp, 0); 759342206Smav if (oldmtu != -1) 760342206Smav (*ifp->if_ioctl)(ifp, SIOCSIFMTU, (caddr_t)&ifr); 761318329Smav LAGG_UNLOCK_ASSERT(sc); 762168793Sthompsa return (error); 763168793Sthompsa } 764168793Sthompsa 765318329Smav LAGG_WUNLOCK(sc); 766318329Smav 767318329Smav /* Update lagg capabilities */ 768318329Smav lagg_capabilities(sc); 769318329Smav lagg_linkstate(sc); 770318329Smav 771272178Sglebius return (0); 772168793Sthompsa} 773168793Sthompsa 774201803Strasz#ifdef LAGG_PORT_STACKING 775168793Sthompsastatic int 776168793Sthompsalagg_port_checkstacking(struct lagg_softc *sc) 777168793Sthompsa{ 778168793Sthompsa struct lagg_softc *sc_ptr; 779168793Sthompsa struct lagg_port *lp; 780168793Sthompsa int m = 0; 781168793Sthompsa 782318329Smav LAGG_SXLOCK_ASSERT(sc); 783168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 784168793Sthompsa if (lp->lp_flags & LAGG_PORT_STACK) { 785168793Sthompsa sc_ptr = (struct lagg_softc *)lp->lp_ifp->if_softc; 786168793Sthompsa m = MAX(m, lagg_port_checkstacking(sc_ptr)); 787168793Sthompsa } 788168793Sthompsa } 789168793Sthompsa 790168793Sthompsa return (m + 1); 791168793Sthompsa} 792201803Strasz#endif 793168793Sthompsa 794168793Sthompsastatic int 795272178Sglebiuslagg_port_destroy(struct lagg_port *lp, int rundelport) 796168793Sthompsa{ 797170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 798288980Shrs struct lagg_port *lp_ptr, *lp0; 799168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 800272211Smelifaro uint64_t *pval, vdiff; 801272211Smelifaro int i; 802168793Sthompsa 803318329Smav LAGG_XLOCK_ASSERT(sc); 804168793Sthompsa 805318329Smav if (rundelport) { 806318329Smav LAGG_WLOCK(sc); 807272178Sglebius lagg_proto_delport(sc, lp); 808318329Smav } else 809318329Smav LAGG_WLOCK_ASSERT(sc); 810168793Sthompsa 811318329Smav if (lp->lp_detaching == 0) 812318329Smav lagg_clrmulti(lp); 813168793Sthompsa 814168793Sthompsa /* Restore interface */ 815168793Sthompsa ifp->if_type = lp->lp_iftype; 816168793Sthompsa ifp->if_ioctl = lp->lp_ioctl; 817168793Sthompsa ifp->if_output = lp->lp_output; 818168793Sthompsa ifp->if_lagg = NULL; 819168793Sthompsa 820272211Smelifaro /* Update detached port counters */ 821272211Smelifaro pval = lp->port_counters.val; 822272354Sglebius for (i = 0; i < IFCOUNTERS; i++, pval++) { 823272211Smelifaro vdiff = ifp->if_get_counter(ifp, i) - *pval; 824272244Sglebius sc->detached_counters.val[i] += vdiff; 825272211Smelifaro } 826272211Smelifaro 827168793Sthompsa /* Finally, remove the port from the lagg */ 828168793Sthompsa SLIST_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entries); 829168793Sthompsa sc->sc_count--; 830168793Sthompsa 831168793Sthompsa /* Update the primary interface */ 832168793Sthompsa if (lp == sc->sc_primary) { 833168793Sthompsa uint8_t lladdr[ETHER_ADDR_LEN]; 834168793Sthompsa 835318329Smav if ((lp0 = SLIST_FIRST(&sc->sc_ports)) == NULL) 836168793Sthompsa bzero(&lladdr, ETHER_ADDR_LEN); 837318329Smav else 838318329Smav bcopy(lp0->lp_lladdr, lladdr, ETHER_ADDR_LEN); 839290239Smelifaro sc->sc_primary = lp0; 840318329Smav if (sc->sc_destroying == 0) { 841318329Smav bcopy(lladdr, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 842318329Smav lagg_proto_lladdr(sc); 843318329Smav LAGG_WUNLOCK(sc); 844318329Smav EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); 845318329Smav } else 846318329Smav LAGG_WUNLOCK(sc); 847290239Smelifaro 848288980Shrs /* 849318329Smav * Update lladdr for each port (new primary needs update 850318329Smav * as well, to switch from old lladdr to its 'real' one) 851288980Shrs */ 852168793Sthompsa SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries) 853318329Smav if_setlladdr(lp_ptr->lp_ifp, lladdr, ETHER_ADDR_LEN); 854318329Smav } else 855318329Smav LAGG_WUNLOCK(sc); 856168793Sthompsa 857168793Sthompsa if (lp->lp_ifflags) 858168793Sthompsa if_printf(ifp, "%s: lp_ifflags unclean\n", __func__); 859168793Sthompsa 860318329Smav if (lp->lp_detaching == 0) { 861318329Smav lagg_setflags(lp, 0); 862318329Smav lagg_setcaps(lp, lp->lp_ifcapenable); 863318329Smav if_setlladdr(ifp, lp->lp_lladdr, ETHER_ADDR_LEN); 864318329Smav } 865318329Smav 866317698Smav if_rele(ifp); 867168793Sthompsa free(lp, M_DEVBUF); 868168793Sthompsa 869168793Sthompsa /* Update lagg capabilities */ 870171661Sthompsa lagg_capabilities(sc); 871173895Sthompsa lagg_linkstate(sc); 872168793Sthompsa 873168793Sthompsa return (0); 874168793Sthompsa} 875168793Sthompsa 876168793Sthompsastatic int 877168793Sthompsalagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 878168793Sthompsa{ 879168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data; 880168793Sthompsa struct lagg_softc *sc; 881168793Sthompsa struct lagg_port *lp = NULL; 882168793Sthompsa int error = 0; 883168793Sthompsa 884168793Sthompsa /* Should be checked by the caller */ 885168793Sthompsa if (ifp->if_type != IFT_IEEE8023ADLAG || 886170599Sthompsa (lp = ifp->if_lagg) == NULL || (sc = lp->lp_softc) == NULL) 887168793Sthompsa goto fallback; 888168793Sthompsa 889168793Sthompsa switch (cmd) { 890168793Sthompsa case SIOCGLAGGPORT: 891168793Sthompsa if (rp->rp_portname[0] == '\0' || 892168793Sthompsa ifunit(rp->rp_portname) != ifp) { 893168793Sthompsa error = EINVAL; 894168793Sthompsa break; 895168793Sthompsa } 896168793Sthompsa 897318329Smav LAGG_SLOCK(sc); 898171603Sthompsa if ((lp = ifp->if_lagg) == NULL || lp->lp_softc != sc) { 899168793Sthompsa error = ENOENT; 900318329Smav LAGG_SUNLOCK(sc); 901168793Sthompsa break; 902168793Sthompsa } 903168793Sthompsa 904168793Sthompsa lagg_port2req(lp, rp); 905318329Smav LAGG_SUNLOCK(sc); 906168793Sthompsa break; 907171661Sthompsa 908171661Sthompsa case SIOCSIFCAP: 909171661Sthompsa if (lp->lp_ioctl == NULL) { 910171661Sthompsa error = EINVAL; 911171661Sthompsa break; 912171661Sthompsa } 913171661Sthompsa error = (*lp->lp_ioctl)(ifp, cmd, data); 914171661Sthompsa if (error) 915171661Sthompsa break; 916171661Sthompsa 917171661Sthompsa /* Update lagg interface capabilities */ 918318329Smav LAGG_XLOCK(sc); 919171661Sthompsa lagg_capabilities(sc); 920318329Smav LAGG_XUNLOCK(sc); 921319697Smav VLAN_CAPABILITIES(sc->sc_ifp); 922171661Sthompsa break; 923171661Sthompsa 924171661Sthompsa case SIOCSIFMTU: 925171661Sthompsa /* Do not allow the MTU to be changed once joined */ 926171661Sthompsa error = EINVAL; 927171661Sthompsa break; 928171661Sthompsa 929168793Sthompsa default: 930168793Sthompsa goto fallback; 931168793Sthompsa } 932168793Sthompsa 933168793Sthompsa return (error); 934168793Sthompsa 935168793Sthompsafallback: 936313108Sasomers if (lp != NULL && lp->lp_ioctl != NULL) 937168793Sthompsa return ((*lp->lp_ioctl)(ifp, cmd, data)); 938168793Sthompsa 939168793Sthompsa return (EINVAL); 940168793Sthompsa} 941168793Sthompsa 942234936Semaste/* 943272211Smelifaro * Requests counter @cnt data. 944272211Smelifaro * 945272211Smelifaro * Counter value is calculated the following way: 946272211Smelifaro * 1) for each port, sum difference between current and "initial" measurements. 947272211Smelifaro * 2) add lagg logical interface counters. 948272211Smelifaro * 3) add data from detached_counters array. 949272211Smelifaro * 950272211Smelifaro * We also do the following things on ports attach/detach: 951272211Smelifaro * 1) On port attach we store all counters it has into port_counter array. 952272211Smelifaro * 2) On port detach we add the different between "initial" and 953272211Smelifaro * current counters data to detached_counters array. 954272211Smelifaro */ 955272211Smelifarostatic uint64_t 956272211Smelifarolagg_get_counter(struct ifnet *ifp, ift_counter cnt) 957272211Smelifaro{ 958272211Smelifaro struct lagg_softc *sc; 959272211Smelifaro struct lagg_port *lp; 960272211Smelifaro struct ifnet *lpifp; 961272211Smelifaro struct rm_priotracker tracker; 962272211Smelifaro uint64_t newval, oldval, vsum; 963272211Smelifaro 964272244Sglebius /* Revise this when we've got non-generic counters. */ 965272244Sglebius KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt)); 966272211Smelifaro 967272211Smelifaro sc = (struct lagg_softc *)ifp->if_softc; 968272211Smelifaro LAGG_RLOCK(sc, &tracker); 969272211Smelifaro 970272211Smelifaro vsum = 0; 971272211Smelifaro SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 972272211Smelifaro /* Saved attached value */ 973272244Sglebius oldval = lp->port_counters.val[cnt]; 974272211Smelifaro /* current value */ 975272211Smelifaro lpifp = lp->lp_ifp; 976272211Smelifaro newval = lpifp->if_get_counter(lpifp, cnt); 977272211Smelifaro /* Calculate diff and save new */ 978272211Smelifaro vsum += newval - oldval; 979272211Smelifaro } 980272211Smelifaro 981272211Smelifaro /* 982272211Smelifaro * Add counter data which might be added by upper 983272211Smelifaro * layer protocols operating on logical interface. 984272211Smelifaro */ 985272211Smelifaro vsum += if_get_counter_default(ifp, cnt); 986272211Smelifaro 987272211Smelifaro /* 988272211Smelifaro * Add counter data from detached ports counters 989272211Smelifaro */ 990272244Sglebius vsum += sc->detached_counters.val[cnt]; 991272211Smelifaro 992272211Smelifaro LAGG_RUNLOCK(sc, &tracker); 993272211Smelifaro 994272211Smelifaro return (vsum); 995272211Smelifaro} 996272211Smelifaro 997272211Smelifaro/* 998234936Semaste * For direct output to child ports. 999234936Semaste */ 1000168793Sthompsastatic int 1001168793Sthompsalagg_port_output(struct ifnet *ifp, struct mbuf *m, 1002249925Sglebius const struct sockaddr *dst, struct route *ro) 1003168793Sthompsa{ 1004168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1005168793Sthompsa 1006168793Sthompsa switch (dst->sa_family) { 1007168793Sthompsa case pseudo_AF_HDRCMPLT: 1008168793Sthompsa case AF_UNSPEC: 1009191148Skmacy return ((*lp->lp_output)(ifp, m, dst, ro)); 1010168793Sthompsa } 1011168793Sthompsa 1012168793Sthompsa /* drop any other frames */ 1013168793Sthompsa m_freem(m); 1014245741Sglebius return (ENETDOWN); 1015168793Sthompsa} 1016168793Sthompsa 1017168793Sthompsastatic void 1018168793Sthompsalagg_port_ifdetach(void *arg __unused, struct ifnet *ifp) 1019168793Sthompsa{ 1020168793Sthompsa struct lagg_port *lp; 1021168793Sthompsa struct lagg_softc *sc; 1022168793Sthompsa 1023168793Sthompsa if ((lp = ifp->if_lagg) == NULL) 1024168793Sthompsa return; 1025237852Sthompsa /* If the ifnet is just being renamed, don't do anything. */ 1026237852Sthompsa if (ifp->if_flags & IFF_RENAMING) 1027237852Sthompsa return; 1028168793Sthompsa 1029170599Sthompsa sc = lp->lp_softc; 1030168793Sthompsa 1031318329Smav LAGG_XLOCK(sc); 1032318329Smav lp->lp_detaching = 1; 1033168793Sthompsa lagg_port_destroy(lp, 1); 1034318329Smav LAGG_XUNLOCK(sc); 1035319697Smav VLAN_CAPABILITIES(sc->sc_ifp); 1036168793Sthompsa} 1037168793Sthompsa 1038168793Sthompsastatic void 1039168793Sthompsalagg_port2req(struct lagg_port *lp, struct lagg_reqport *rp) 1040168793Sthompsa{ 1041170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1042172020Sthompsa 1043168793Sthompsa strlcpy(rp->rp_ifname, sc->sc_ifname, sizeof(rp->rp_ifname)); 1044168793Sthompsa strlcpy(rp->rp_portname, lp->lp_ifp->if_xname, sizeof(rp->rp_portname)); 1045168793Sthompsa rp->rp_prio = lp->lp_prio; 1046168793Sthompsa rp->rp_flags = lp->lp_flags; 1047272178Sglebius lagg_proto_portreq(sc, lp, &rp->rp_psc); 1048168793Sthompsa 1049168793Sthompsa /* Add protocol specific flags */ 1050168793Sthompsa switch (sc->sc_proto) { 1051168793Sthompsa case LAGG_PROTO_FAILOVER: 1052168793Sthompsa if (lp == sc->sc_primary) 1053169204Sthompsa rp->rp_flags |= LAGG_PORT_MASTER; 1054172020Sthompsa if (lp == lagg_link_active(sc, sc->sc_primary)) 1055172020Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 1056172020Sthompsa break; 1057172020Sthompsa 1058168793Sthompsa case LAGG_PROTO_ROUNDROBIN: 1059168793Sthompsa case LAGG_PROTO_LOADBALANCE: 1060272175Sglebius case LAGG_PROTO_BROADCAST: 1061168793Sthompsa if (LAGG_PORTACTIVE(lp)) 1062168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 1063168793Sthompsa break; 1064168793Sthompsa 1065168793Sthompsa case LAGG_PROTO_LACP: 1066168793Sthompsa /* LACP has a different definition of active */ 1067177274Sthompsa if (lacp_isactive(lp)) 1068168793Sthompsa rp->rp_flags |= LAGG_PORT_ACTIVE; 1069177274Sthompsa if (lacp_iscollecting(lp)) 1070177274Sthompsa rp->rp_flags |= LAGG_PORT_COLLECTING; 1071177274Sthompsa if (lacp_isdistributing(lp)) 1072177274Sthompsa rp->rp_flags |= LAGG_PORT_DISTRIBUTING; 1073168793Sthompsa break; 1074168793Sthompsa } 1075168793Sthompsa 1076168793Sthompsa} 1077168793Sthompsa 1078168793Sthompsastatic void 1079168793Sthompsalagg_init(void *xsc) 1080168793Sthompsa{ 1081168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)xsc; 1082290239Smelifaro struct ifnet *ifp = sc->sc_ifp; 1083168793Sthompsa struct lagg_port *lp; 1084168793Sthompsa 1085318329Smav LAGG_XLOCK(sc); 1086318329Smav if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1087318329Smav LAGG_XUNLOCK(sc); 1088168793Sthompsa return; 1089318329Smav } 1090168793Sthompsa 1091168793Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 1092290239Smelifaro 1093290239Smelifaro /* 1094290239Smelifaro * Update the port lladdrs if needed. 1095290239Smelifaro * This might be if_setlladdr() notification 1096290239Smelifaro * that lladdr has been changed. 1097290239Smelifaro */ 1098318329Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1099318329Smav if (memcmp(IF_LLADDR(ifp), IF_LLADDR(lp->lp_ifp), 1100318329Smav ETHER_ADDR_LEN) != 0) 1101318329Smav if_setlladdr(lp->lp_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN); 1102318329Smav } 1103168793Sthompsa 1104272178Sglebius lagg_proto_init(sc); 1105168793Sthompsa 1106318329Smav LAGG_XUNLOCK(sc); 1107168793Sthompsa} 1108168793Sthompsa 1109168793Sthompsastatic void 1110168793Sthompsalagg_stop(struct lagg_softc *sc) 1111168793Sthompsa{ 1112168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1113168793Sthompsa 1114318329Smav LAGG_XLOCK_ASSERT(sc); 1115168793Sthompsa 1116168793Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 1117168793Sthompsa return; 1118168793Sthompsa 1119168793Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1120168793Sthompsa 1121272178Sglebius lagg_proto_stop(sc); 1122168793Sthompsa} 1123168793Sthompsa 1124168793Sthompsastatic int 1125168793Sthompsalagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 1126168793Sthompsa{ 1127168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1128168793Sthompsa struct lagg_reqall *ra = (struct lagg_reqall *)data; 1129272446Shrs struct lagg_reqopts *ro = (struct lagg_reqopts *)data; 1130168793Sthompsa struct lagg_reqport *rp = (struct lagg_reqport *)data, rpbuf; 1131232629Sthompsa struct lagg_reqflags *rf = (struct lagg_reqflags *)data; 1132168793Sthompsa struct ifreq *ifr = (struct ifreq *)data; 1133168793Sthompsa struct lagg_port *lp; 1134168793Sthompsa struct ifnet *tpif; 1135168793Sthompsa struct thread *td = curthread; 1136171603Sthompsa char *buf, *outbuf; 1137171603Sthompsa int count, buflen, len, error = 0; 1138168793Sthompsa 1139168793Sthompsa bzero(&rpbuf, sizeof(rpbuf)); 1140168793Sthompsa 1141168793Sthompsa switch (cmd) { 1142168793Sthompsa case SIOCGLAGG: 1143318329Smav LAGG_SLOCK(sc); 1144318329Smav buflen = sc->sc_count * sizeof(struct lagg_reqport); 1145171603Sthompsa outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); 1146168793Sthompsa ra->ra_proto = sc->sc_proto; 1147272178Sglebius lagg_proto_request(sc, &ra->ra_psc); 1148171603Sthompsa count = 0; 1149171603Sthompsa buf = outbuf; 1150171603Sthompsa len = min(ra->ra_size, buflen); 1151171603Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1152171603Sthompsa if (len < sizeof(rpbuf)) 1153171603Sthompsa break; 1154171603Sthompsa 1155168793Sthompsa lagg_port2req(lp, &rpbuf); 1156171603Sthompsa memcpy(buf, &rpbuf, sizeof(rpbuf)); 1157171603Sthompsa count++; 1158171603Sthompsa buf += sizeof(rpbuf); 1159171603Sthompsa len -= sizeof(rpbuf); 1160168793Sthompsa } 1161318329Smav LAGG_SUNLOCK(sc); 1162171603Sthompsa ra->ra_ports = count; 1163171603Sthompsa ra->ra_size = count * sizeof(rpbuf); 1164171603Sthompsa error = copyout(outbuf, ra->ra_port, ra->ra_size); 1165171603Sthompsa free(outbuf, M_TEMP); 1166168793Sthompsa break; 1167168793Sthompsa case SIOCSLAGG: 1168168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1169168793Sthompsa if (error) 1170168793Sthompsa break; 1171295796Saraujo if (ra->ra_proto >= LAGG_PROTO_MAX) { 1172272446Shrs error = EPROTONOSUPPORT; 1173272446Shrs break; 1174272446Shrs } 1175272386Shrs 1176318329Smav LAGG_XLOCK(sc); 1177272446Shrs LAGG_WLOCK(sc); 1178272446Shrs lagg_proto_detach(sc); 1179290239Smelifaro LAGG_UNLOCK_ASSERT(sc); 1180272446Shrs lagg_proto_attach(sc, ra->ra_proto); 1181318329Smav LAGG_XUNLOCK(sc); 1182272446Shrs break; 1183272446Shrs case SIOCGLAGGOPTS: 1184318329Smav LAGG_SLOCK(sc); 1185272446Shrs ro->ro_opts = sc->sc_opts; 1186272446Shrs if (sc->sc_proto == LAGG_PROTO_LACP) { 1187272446Shrs struct lacp_softc *lsc; 1188272446Shrs 1189272446Shrs lsc = (struct lacp_softc *)sc->sc_psc; 1190272446Shrs if (lsc->lsc_debug.lsc_tx_test != 0) 1191272446Shrs ro->ro_opts |= LAGG_OPT_LACP_TXTEST; 1192272446Shrs if (lsc->lsc_debug.lsc_rx_test != 0) 1193272446Shrs ro->ro_opts |= LAGG_OPT_LACP_RXTEST; 1194272446Shrs if (lsc->lsc_strict_mode != 0) 1195272446Shrs ro->ro_opts |= LAGG_OPT_LACP_STRICT; 1196286700Shiren if (lsc->lsc_fast_timeout != 0) 1197362366Srpokala ro->ro_opts |= LAGG_OPT_LACP_FAST_TIMO; 1198272446Shrs 1199272446Shrs ro->ro_active = sc->sc_active; 1200272446Shrs } else { 1201272446Shrs ro->ro_active = 0; 1202272446Shrs SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1203272446Shrs ro->ro_active += LAGG_PORTACTIVE(lp); 1204272446Shrs } 1205294615Saraujo ro->ro_bkt = sc->sc_bkt; 1206272446Shrs ro->ro_flapping = sc->sc_flapping; 1207272446Shrs ro->ro_flowid_shift = sc->flowid_shift; 1208318329Smav LAGG_SUNLOCK(sc); 1209272446Shrs break; 1210272446Shrs case SIOCSLAGGOPTS: 1211294615Saraujo if (sc->sc_proto == LAGG_PROTO_ROUNDROBIN) { 1212294615Saraujo if (ro->ro_bkt == 0) 1213294615Saraujo sc->sc_bkt = 1; // Minimum 1 packet per iface. 1214294615Saraujo else 1215294615Saraujo sc->sc_bkt = ro->ro_bkt; 1216294615Saraujo } 1217272446Shrs error = priv_check(td, PRIV_NET_LAGG); 1218272446Shrs if (error) 1219272446Shrs break; 1220272446Shrs if (ro->ro_opts == 0) 1221272446Shrs break; 1222272446Shrs /* 1223272446Shrs * Set options. LACP options are stored in sc->sc_psc, 1224272446Shrs * not in sc_opts. 1225272446Shrs */ 1226272446Shrs int valid, lacp; 1227272446Shrs 1228272446Shrs switch (ro->ro_opts) { 1229272446Shrs case LAGG_OPT_USE_FLOWID: 1230272446Shrs case -LAGG_OPT_USE_FLOWID: 1231272446Shrs case LAGG_OPT_FLOWIDSHIFT: 1232272446Shrs valid = 1; 1233272446Shrs lacp = 0; 1234272446Shrs break; 1235272446Shrs case LAGG_OPT_LACP_TXTEST: 1236272446Shrs case -LAGG_OPT_LACP_TXTEST: 1237272446Shrs case LAGG_OPT_LACP_RXTEST: 1238272446Shrs case -LAGG_OPT_LACP_RXTEST: 1239272446Shrs case LAGG_OPT_LACP_STRICT: 1240272446Shrs case -LAGG_OPT_LACP_STRICT: 1241362366Srpokala case LAGG_OPT_LACP_FAST_TIMO: 1242362366Srpokala case -LAGG_OPT_LACP_FAST_TIMO: 1243272446Shrs valid = lacp = 1; 1244272446Shrs break; 1245272446Shrs default: 1246272446Shrs valid = lacp = 0; 1247272446Shrs break; 1248272446Shrs } 1249272446Shrs 1250318329Smav LAGG_XLOCK(sc); 1251294615Saraujo 1252272446Shrs if (valid == 0 || 1253272446Shrs (lacp == 1 && sc->sc_proto != LAGG_PROTO_LACP)) { 1254272446Shrs /* Invalid combination of options specified. */ 1255272446Shrs error = EINVAL; 1256318329Smav LAGG_XUNLOCK(sc); 1257272446Shrs break; /* Return from SIOCSLAGGOPTS. */ 1258272446Shrs } 1259272446Shrs /* 1260272446Shrs * Store new options into sc->sc_opts except for 1261272446Shrs * FLOWIDSHIFT and LACP options. 1262272446Shrs */ 1263272446Shrs if (lacp == 0) { 1264272446Shrs if (ro->ro_opts == LAGG_OPT_FLOWIDSHIFT) 1265272446Shrs sc->flowid_shift = ro->ro_flowid_shift; 1266272446Shrs else if (ro->ro_opts > 0) 1267272446Shrs sc->sc_opts |= ro->ro_opts; 1268272446Shrs else 1269272446Shrs sc->sc_opts &= ~ro->ro_opts; 1270272446Shrs } else { 1271272446Shrs struct lacp_softc *lsc; 1272286700Shiren struct lacp_port *lp; 1273272446Shrs 1274272446Shrs lsc = (struct lacp_softc *)sc->sc_psc; 1275272446Shrs 1276272446Shrs switch (ro->ro_opts) { 1277272446Shrs case LAGG_OPT_LACP_TXTEST: 1278272446Shrs lsc->lsc_debug.lsc_tx_test = 1; 1279272386Shrs break; 1280272386Shrs case -LAGG_OPT_LACP_TXTEST: 1281272446Shrs lsc->lsc_debug.lsc_tx_test = 0; 1282272446Shrs break; 1283272386Shrs case LAGG_OPT_LACP_RXTEST: 1284272446Shrs lsc->lsc_debug.lsc_rx_test = 1; 1285272446Shrs break; 1286272386Shrs case -LAGG_OPT_LACP_RXTEST: 1287272446Shrs lsc->lsc_debug.lsc_rx_test = 0; 1288272446Shrs break; 1289272386Shrs case LAGG_OPT_LACP_STRICT: 1290272446Shrs lsc->lsc_strict_mode = 1; 1291272446Shrs break; 1292272386Shrs case -LAGG_OPT_LACP_STRICT: 1293272446Shrs lsc->lsc_strict_mode = 0; 1294272386Shrs break; 1295362366Srpokala case LAGG_OPT_LACP_FAST_TIMO: 1296286700Shiren LACP_LOCK(lsc); 1297286700Shiren LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) 1298286700Shiren lp->lp_state |= LACP_STATE_TIMEOUT; 1299286700Shiren LACP_UNLOCK(lsc); 1300286700Shiren lsc->lsc_fast_timeout = 1; 1301286700Shiren break; 1302362366Srpokala case -LAGG_OPT_LACP_FAST_TIMO: 1303286700Shiren LACP_LOCK(lsc); 1304286700Shiren LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) 1305286700Shiren lp->lp_state &= ~LACP_STATE_TIMEOUT; 1306286700Shiren LACP_UNLOCK(lsc); 1307286700Shiren lsc->lsc_fast_timeout = 0; 1308286700Shiren break; 1309272386Shrs } 1310272386Shrs } 1311318329Smav LAGG_XUNLOCK(sc); 1312168793Sthompsa break; 1313232629Sthompsa case SIOCGLAGGFLAGS: 1314279891Shselasky rf->rf_flags = 0; 1315318329Smav LAGG_SLOCK(sc); 1316279891Shselasky if (sc->sc_flags & MBUF_HASHFLAG_L2) 1317279891Shselasky rf->rf_flags |= LAGG_F_HASHL2; 1318279891Shselasky if (sc->sc_flags & MBUF_HASHFLAG_L3) 1319279891Shselasky rf->rf_flags |= LAGG_F_HASHL3; 1320279891Shselasky if (sc->sc_flags & MBUF_HASHFLAG_L4) 1321279891Shselasky rf->rf_flags |= LAGG_F_HASHL4; 1322318329Smav LAGG_SUNLOCK(sc); 1323232629Sthompsa break; 1324232629Sthompsa case SIOCSLAGGHASH: 1325232629Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1326232629Sthompsa if (error) 1327232629Sthompsa break; 1328232629Sthompsa if ((rf->rf_flags & LAGG_F_HASHMASK) == 0) { 1329232629Sthompsa error = EINVAL; 1330232629Sthompsa break; 1331232629Sthompsa } 1332318329Smav LAGG_XLOCK(sc); 1333279891Shselasky sc->sc_flags = 0; 1334279891Shselasky if (rf->rf_flags & LAGG_F_HASHL2) 1335279891Shselasky sc->sc_flags |= MBUF_HASHFLAG_L2; 1336279891Shselasky if (rf->rf_flags & LAGG_F_HASHL3) 1337279891Shselasky sc->sc_flags |= MBUF_HASHFLAG_L3; 1338279891Shselasky if (rf->rf_flags & LAGG_F_HASHL4) 1339279891Shselasky sc->sc_flags |= MBUF_HASHFLAG_L4; 1340318329Smav LAGG_XUNLOCK(sc); 1341232629Sthompsa break; 1342168793Sthompsa case SIOCGLAGGPORT: 1343168793Sthompsa if (rp->rp_portname[0] == '\0' || 1344317698Smav (tpif = ifunit_ref(rp->rp_portname)) == NULL) { 1345168793Sthompsa error = EINVAL; 1346168793Sthompsa break; 1347168793Sthompsa } 1348168793Sthompsa 1349318329Smav LAGG_SLOCK(sc); 1350168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 1351170599Sthompsa lp->lp_softc != sc) { 1352168793Sthompsa error = ENOENT; 1353318329Smav LAGG_SUNLOCK(sc); 1354317698Smav if_rele(tpif); 1355168793Sthompsa break; 1356168793Sthompsa } 1357168793Sthompsa 1358168793Sthompsa lagg_port2req(lp, rp); 1359318329Smav LAGG_SUNLOCK(sc); 1360317698Smav if_rele(tpif); 1361168793Sthompsa break; 1362168793Sthompsa case SIOCSLAGGPORT: 1363168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1364168793Sthompsa if (error) 1365168793Sthompsa break; 1366168793Sthompsa if (rp->rp_portname[0] == '\0' || 1367317698Smav (tpif = ifunit_ref(rp->rp_portname)) == NULL) { 1368168793Sthompsa error = EINVAL; 1369168793Sthompsa break; 1370168793Sthompsa } 1371273210Shrs#ifdef INET6 1372273210Shrs /* 1373273210Shrs * A laggport interface should not have inet6 address 1374273210Shrs * because two interfaces with a valid link-local 1375273210Shrs * scope zone must not be merged in any form. This 1376273210Shrs * restriction is needed to prevent violation of 1377273210Shrs * link-local scope zone. Attempts to add a laggport 1378273210Shrs * interface which has inet6 addresses triggers 1379273210Shrs * removal of all inet6 addresses on the member 1380273210Shrs * interface. 1381273210Shrs */ 1382273210Shrs if (in6ifa_llaonifp(tpif)) { 1383273210Shrs in6_ifdetach(tpif); 1384273210Shrs if_printf(sc->sc_ifp, 1385273210Shrs "IPv6 addresses on %s have been removed " 1386273210Shrs "before adding it as a member to prevent " 1387273210Shrs "IPv6 address scope violation.\n", 1388273210Shrs tpif->if_xname); 1389273210Shrs } 1390273210Shrs#endif 1391318329Smav LAGG_XLOCK(sc); 1392168793Sthompsa error = lagg_port_create(sc, tpif); 1393318329Smav LAGG_XUNLOCK(sc); 1394317698Smav if_rele(tpif); 1395319697Smav VLAN_CAPABILITIES(ifp); 1396168793Sthompsa break; 1397168793Sthompsa case SIOCSLAGGDELPORT: 1398168793Sthompsa error = priv_check(td, PRIV_NET_LAGG); 1399168793Sthompsa if (error) 1400168793Sthompsa break; 1401168793Sthompsa if (rp->rp_portname[0] == '\0' || 1402317698Smav (tpif = ifunit_ref(rp->rp_portname)) == NULL) { 1403168793Sthompsa error = EINVAL; 1404168793Sthompsa break; 1405168793Sthompsa } 1406168793Sthompsa 1407318329Smav LAGG_XLOCK(sc); 1408168793Sthompsa if ((lp = (struct lagg_port *)tpif->if_lagg) == NULL || 1409170599Sthompsa lp->lp_softc != sc) { 1410168793Sthompsa error = ENOENT; 1411318329Smav LAGG_XUNLOCK(sc); 1412317698Smav if_rele(tpif); 1413168793Sthompsa break; 1414168793Sthompsa } 1415168793Sthompsa 1416168793Sthompsa error = lagg_port_destroy(lp, 1); 1417318329Smav LAGG_XUNLOCK(sc); 1418317698Smav if_rele(tpif); 1419319697Smav VLAN_CAPABILITIES(ifp); 1420168793Sthompsa break; 1421168793Sthompsa case SIOCSIFFLAGS: 1422168793Sthompsa /* Set flags on ports too */ 1423318329Smav LAGG_XLOCK(sc); 1424168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1425168793Sthompsa lagg_setflags(lp, 1); 1426168793Sthompsa } 1427168793Sthompsa 1428168793Sthompsa if (!(ifp->if_flags & IFF_UP) && 1429168793Sthompsa (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1430168793Sthompsa /* 1431168793Sthompsa * If interface is marked down and it is running, 1432168793Sthompsa * then stop and disable it. 1433168793Sthompsa */ 1434168793Sthompsa lagg_stop(sc); 1435318329Smav LAGG_XUNLOCK(sc); 1436168793Sthompsa } else if ((ifp->if_flags & IFF_UP) && 1437168793Sthompsa !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1438168793Sthompsa /* 1439168793Sthompsa * If interface is marked up and it is stopped, then 1440168793Sthompsa * start it. 1441168793Sthompsa */ 1442318329Smav LAGG_XUNLOCK(sc); 1443168793Sthompsa (*ifp->if_init)(sc); 1444318329Smav } else 1445318329Smav LAGG_XUNLOCK(sc); 1446168793Sthompsa break; 1447168793Sthompsa case SIOCADDMULTI: 1448168793Sthompsa case SIOCDELMULTI: 1449171603Sthompsa LAGG_WLOCK(sc); 1450318329Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1451318329Smav lagg_clrmulti(lp); 1452318329Smav lagg_setmulti(lp); 1453318329Smav } 1454171603Sthompsa LAGG_WUNLOCK(sc); 1455318329Smav error = 0; 1456168793Sthompsa break; 1457168793Sthompsa case SIOCSIFMEDIA: 1458168793Sthompsa case SIOCGIFMEDIA: 1459168793Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 1460168793Sthompsa break; 1461171661Sthompsa 1462171661Sthompsa case SIOCSIFCAP: 1463318329Smav LAGG_XLOCK(sc); 1464318329Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1465318329Smav if (lp->lp_ioctl != NULL) 1466318329Smav (*lp->lp_ioctl)(lp->lp_ifp, cmd, data); 1467318329Smav } 1468318329Smav lagg_capabilities(sc); 1469318329Smav LAGG_XUNLOCK(sc); 1470319697Smav VLAN_CAPABILITIES(ifp); 1471318329Smav error = 0; 1472318329Smav break; 1473318329Smav 1474297610Srpokala case SIOCSIFMTU: 1475342206Smav LAGG_XLOCK(sc); 1476342206Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1477342206Smav if (lp->lp_ioctl != NULL) 1478342206Smav error = (*lp->lp_ioctl)(lp->lp_ifp, cmd, data); 1479342206Smav else 1480342206Smav error = EINVAL; 1481342206Smav if (error != 0) { 1482342206Smav if_printf(ifp, 1483342206Smav "failed to change MTU to %d on port %s, " 1484342206Smav "reverting all ports to original MTU (%d)\n", 1485342206Smav ifr->ifr_mtu, lp->lp_ifp->if_xname, ifp->if_mtu); 1486342206Smav break; 1487342206Smav } 1488342206Smav } 1489342206Smav if (error == 0) { 1490342206Smav ifp->if_mtu = ifr->ifr_mtu; 1491342206Smav } else { 1492342206Smav /* set every port back to the original MTU */ 1493342206Smav ifr->ifr_mtu = ifp->if_mtu; 1494342206Smav SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1495342206Smav if (lp->lp_ioctl != NULL) 1496342206Smav (*lp->lp_ioctl)(lp->lp_ifp, cmd, data); 1497342206Smav } 1498342206Smav } 1499342206Smav LAGG_XUNLOCK(sc); 1500171661Sthompsa break; 1501171661Sthompsa 1502168793Sthompsa default: 1503168793Sthompsa error = ether_ioctl(ifp, cmd, data); 1504168793Sthompsa break; 1505168793Sthompsa } 1506168793Sthompsa return (error); 1507168793Sthompsa} 1508168793Sthompsa 1509168793Sthompsastatic int 1510318329Smavlagg_setmulti(struct lagg_port *lp) 1511168793Sthompsa{ 1512318329Smav struct lagg_softc *sc = lp->lp_softc; 1513318329Smav struct ifnet *ifp = lp->lp_ifp; 1514318329Smav struct ifnet *scifp = sc->sc_ifp; 1515318329Smav struct lagg_mc *mc; 1516318329Smav struct ifmultiaddr *ifma; 1517318329Smav int error; 1518168793Sthompsa 1519169569Sthompsa LAGG_WLOCK_ASSERT(sc); 1520318329Smav IF_ADDR_WLOCK(scifp); 1521318329Smav TAILQ_FOREACH(ifma, &scifp->if_multiaddrs, ifma_link) { 1522318329Smav if (ifma->ifma_addr->sa_family != AF_LINK) 1523318329Smav continue; 1524318329Smav mc = malloc(sizeof(struct lagg_mc), M_DEVBUF, M_NOWAIT); 1525318329Smav if (mc == NULL) { 1526318329Smav IF_ADDR_WUNLOCK(scifp); 1527318329Smav return (ENOMEM); 1528318329Smav } 1529318329Smav bcopy(ifma->ifma_addr, &mc->mc_addr, 1530318329Smav ifma->ifma_addr->sa_len); 1531318329Smav mc->mc_addr.sdl_index = ifp->if_index; 1532318329Smav mc->mc_ifma = NULL; 1533318329Smav SLIST_INSERT_HEAD(&lp->lp_mc_head, mc, mc_entries); 1534169340Sthompsa } 1535318329Smav IF_ADDR_WUNLOCK(scifp); 1536318329Smav SLIST_FOREACH (mc, &lp->lp_mc_head, mc_entries) { 1537318329Smav error = if_addmulti(ifp, 1538318329Smav (struct sockaddr *)&mc->mc_addr, &mc->mc_ifma); 1539318329Smav if (error) 1540318329Smav return (error); 1541318329Smav } 1542168793Sthompsa return (0); 1543168793Sthompsa} 1544168793Sthompsa 1545168793Sthompsastatic int 1546318329Smavlagg_clrmulti(struct lagg_port *lp) 1547168793Sthompsa{ 1548169327Sthompsa struct lagg_mc *mc; 1549168793Sthompsa 1550318329Smav LAGG_WLOCK_ASSERT(lp->lp_softc); 1551318329Smav while ((mc = SLIST_FIRST(&lp->lp_mc_head)) != NULL) { 1552318329Smav SLIST_REMOVE(&lp->lp_mc_head, mc, lagg_mc, mc_entries); 1553318329Smav if (mc->mc_ifma && lp->lp_detaching == 0) 1554318329Smav if_delmulti_ifma(mc->mc_ifma); 1555318329Smav free(mc, M_DEVBUF); 1556168793Sthompsa } 1557168793Sthompsa return (0); 1558168793Sthompsa} 1559168793Sthompsa 1560318329Smavstatic int 1561318329Smavlagg_setcaps(struct lagg_port *lp, int cap) 1562318329Smav{ 1563318329Smav struct ifreq ifr; 1564318329Smav 1565318329Smav if (lp->lp_ifp->if_capenable == cap) 1566318329Smav return (0); 1567318329Smav if (lp->lp_ioctl == NULL) 1568318329Smav return (ENXIO); 1569318329Smav ifr.ifr_reqcap = cap; 1570318329Smav return ((*lp->lp_ioctl)(lp->lp_ifp, SIOCSIFCAP, (caddr_t)&ifr)); 1571318329Smav} 1572318329Smav 1573168793Sthompsa/* Handle a ref counted flag that should be set on the lagg port as well */ 1574168793Sthompsastatic int 1575168793Sthompsalagg_setflag(struct lagg_port *lp, int flag, int status, 1576272175Sglebius int (*func)(struct ifnet *, int)) 1577168793Sthompsa{ 1578170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1579170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1580168793Sthompsa struct ifnet *ifp = lp->lp_ifp; 1581168793Sthompsa int error; 1582168793Sthompsa 1583318329Smav LAGG_XLOCK_ASSERT(sc); 1584168793Sthompsa 1585170599Sthompsa status = status ? (scifp->if_flags & flag) : 0; 1586168793Sthompsa /* Now "status" contains the flag value or 0 */ 1587168793Sthompsa 1588168793Sthompsa /* 1589168793Sthompsa * See if recorded ports status is different from what 1590168793Sthompsa * we want it to be. If it is, flip it. We record ports 1591168793Sthompsa * status in lp_ifflags so that we won't clear ports flag 1592168793Sthompsa * we haven't set. In fact, we don't clear or set ports 1593168793Sthompsa * flags directly, but get or release references to them. 1594168793Sthompsa * That's why we can be sure that recorded flags still are 1595168793Sthompsa * in accord with actual ports flags. 1596168793Sthompsa */ 1597168793Sthompsa if (status != (lp->lp_ifflags & flag)) { 1598168793Sthompsa error = (*func)(ifp, status); 1599168793Sthompsa if (error) 1600168793Sthompsa return (error); 1601168793Sthompsa lp->lp_ifflags &= ~flag; 1602168793Sthompsa lp->lp_ifflags |= status; 1603168793Sthompsa } 1604168793Sthompsa return (0); 1605168793Sthompsa} 1606168793Sthompsa 1607168793Sthompsa/* 1608168793Sthompsa * Handle IFF_* flags that require certain changes on the lagg port 1609168793Sthompsa * if "status" is true, update ports flags respective to the lagg 1610168793Sthompsa * if "status" is false, forcedly clear the flags set on port. 1611168793Sthompsa */ 1612168793Sthompsastatic int 1613168793Sthompsalagg_setflags(struct lagg_port *lp, int status) 1614168793Sthompsa{ 1615168793Sthompsa int error, i; 1616170599Sthompsa 1617168793Sthompsa for (i = 0; lagg_pflags[i].flag; i++) { 1618168793Sthompsa error = lagg_setflag(lp, lagg_pflags[i].flag, 1619168793Sthompsa status, lagg_pflags[i].func); 1620168793Sthompsa if (error) 1621168793Sthompsa return (error); 1622168793Sthompsa } 1623168793Sthompsa return (0); 1624168793Sthompsa} 1625168793Sthompsa 1626240742Sglebiusstatic int 1627240742Sglebiuslagg_transmit(struct ifnet *ifp, struct mbuf *m) 1628168793Sthompsa{ 1629168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1630240742Sglebius int error, len, mcast; 1631255038Sadrian struct rm_priotracker tracker; 1632168793Sthompsa 1633240742Sglebius len = m->m_pkthdr.len; 1634240742Sglebius mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 1635240742Sglebius 1636255038Sadrian LAGG_RLOCK(sc, &tracker); 1637183160Sthompsa /* We need a Tx algorithm and at least one port */ 1638183160Sthompsa if (sc->sc_proto == LAGG_PROTO_NONE || sc->sc_count == 0) { 1639255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1640240742Sglebius m_freem(m); 1641272242Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1642240742Sglebius return (ENXIO); 1643183160Sthompsa } 1644183160Sthompsa 1645240742Sglebius ETHER_BPF_MTAP(ifp, m); 1646168793Sthompsa 1647272178Sglebius error = lagg_proto_start(sc, m); 1648255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1649168793Sthompsa 1650272211Smelifaro if (error != 0) 1651272242Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1652240742Sglebius 1653240742Sglebius return (error); 1654168793Sthompsa} 1655168793Sthompsa 1656240742Sglebius/* 1657240742Sglebius * The ifp->if_qflush entry point for lagg(4) is no-op. 1658240742Sglebius */ 1659240742Sglebiusstatic void 1660240742Sglebiuslagg_qflush(struct ifnet *ifp __unused) 1661240742Sglebius{ 1662240742Sglebius} 1663240742Sglebius 1664168793Sthompsastatic struct mbuf * 1665168793Sthompsalagg_input(struct ifnet *ifp, struct mbuf *m) 1666168793Sthompsa{ 1667168793Sthompsa struct lagg_port *lp = ifp->if_lagg; 1668170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 1669170599Sthompsa struct ifnet *scifp = sc->sc_ifp; 1670255038Sadrian struct rm_priotracker tracker; 1671168793Sthompsa 1672255038Sadrian LAGG_RLOCK(sc, &tracker); 1673170599Sthompsa if ((scifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 1674169227Sthompsa (lp->lp_flags & LAGG_PORT_DISABLED) || 1675168793Sthompsa sc->sc_proto == LAGG_PROTO_NONE) { 1676255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1677168793Sthompsa m_freem(m); 1678168793Sthompsa return (NULL); 1679168793Sthompsa } 1680168793Sthompsa 1681172825Sthompsa ETHER_BPF_MTAP(scifp, m); 1682168793Sthompsa 1683280720Sae if (lp->lp_detaching != 0) { 1684280720Sae m_freem(m); 1685280720Sae m = NULL; 1686280720Sae } else 1687280720Sae m = lagg_proto_input(sc, lp, m); 1688168793Sthompsa 1689168793Sthompsa if (m != NULL) { 1690174278Sthompsa if (scifp->if_flags & IFF_MONITOR) { 1691174278Sthompsa m_freem(m); 1692174278Sthompsa m = NULL; 1693174278Sthompsa } 1694168793Sthompsa } 1695168793Sthompsa 1696255038Sadrian LAGG_RUNLOCK(sc, &tracker); 1697168793Sthompsa return (m); 1698168793Sthompsa} 1699168793Sthompsa 1700168793Sthompsastatic int 1701168793Sthompsalagg_media_change(struct ifnet *ifp) 1702168793Sthompsa{ 1703168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1704168793Sthompsa 1705168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 1706168793Sthompsa printf("%s\n", __func__); 1707168793Sthompsa 1708168793Sthompsa /* Ignore */ 1709168793Sthompsa return (0); 1710168793Sthompsa} 1711168793Sthompsa 1712168793Sthompsastatic void 1713168793Sthompsalagg_media_status(struct ifnet *ifp, struct ifmediareq *imr) 1714168793Sthompsa{ 1715168793Sthompsa struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; 1716168793Sthompsa struct lagg_port *lp; 1717168793Sthompsa 1718168793Sthompsa imr->ifm_status = IFM_AVALID; 1719168793Sthompsa imr->ifm_active = IFM_ETHER | IFM_AUTO; 1720168793Sthompsa 1721318329Smav LAGG_SLOCK(sc); 1722169340Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1723169340Sthompsa if (LAGG_PORTACTIVE(lp)) 1724169340Sthompsa imr->ifm_status |= IFM_ACTIVE; 1725169340Sthompsa } 1726318329Smav LAGG_SUNLOCK(sc); 1727168793Sthompsa} 1728168793Sthompsa 1729168793Sthompsastatic void 1730173895Sthompsalagg_linkstate(struct lagg_softc *sc) 1731173895Sthompsa{ 1732173895Sthompsa struct lagg_port *lp; 1733173895Sthompsa int new_link = LINK_STATE_DOWN; 1734186255Sthompsa uint64_t speed; 1735173895Sthompsa 1736318329Smav LAGG_XLOCK_ASSERT(sc); 1737318329Smav 1738173895Sthompsa /* Our link is considered up if at least one of our ports is active */ 1739173895Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1740272179Sglebius if (lp->lp_ifp->if_link_state == LINK_STATE_UP) { 1741173895Sthompsa new_link = LINK_STATE_UP; 1742173895Sthompsa break; 1743173895Sthompsa } 1744173895Sthompsa } 1745292402Ssmh if_link_state_change(sc->sc_ifp, new_link); 1746186254Sthompsa 1747186254Sthompsa /* Update if_baudrate to reflect the max possible speed */ 1748186254Sthompsa switch (sc->sc_proto) { 1749186254Sthompsa case LAGG_PROTO_FAILOVER: 1750186255Sthompsa sc->sc_ifp->if_baudrate = sc->sc_primary != NULL ? 1751186255Sthompsa sc->sc_primary->lp_ifp->if_baudrate : 0; 1752186254Sthompsa break; 1753186254Sthompsa case LAGG_PROTO_ROUNDROBIN: 1754186254Sthompsa case LAGG_PROTO_LOADBALANCE: 1755272175Sglebius case LAGG_PROTO_BROADCAST: 1756186255Sthompsa speed = 0; 1757186254Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 1758186254Sthompsa speed += lp->lp_ifp->if_baudrate; 1759186254Sthompsa sc->sc_ifp->if_baudrate = speed; 1760186254Sthompsa break; 1761186254Sthompsa case LAGG_PROTO_LACP: 1762186254Sthompsa /* LACP updates if_baudrate itself */ 1763186254Sthompsa break; 1764186254Sthompsa } 1765173895Sthompsa} 1766173895Sthompsa 1767173895Sthompsastatic void 1768292402Ssmhlagg_port_state(struct ifnet *ifp, int state) 1769168793Sthompsa{ 1770168793Sthompsa struct lagg_port *lp = (struct lagg_port *)ifp->if_lagg; 1771168793Sthompsa struct lagg_softc *sc = NULL; 1772168793Sthompsa 1773168793Sthompsa if (lp != NULL) 1774170599Sthompsa sc = lp->lp_softc; 1775168793Sthompsa if (sc == NULL) 1776168793Sthompsa return; 1777168793Sthompsa 1778318329Smav LAGG_XLOCK(sc); 1779173895Sthompsa lagg_linkstate(sc); 1780272178Sglebius lagg_proto_linkstate(sc, lp); 1781318329Smav LAGG_XUNLOCK(sc); 1782168793Sthompsa} 1783168793Sthompsa 1784292402Ssmhstruct lagg_port * 1785168793Sthompsalagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) 1786168793Sthompsa{ 1787168793Sthompsa struct lagg_port *lp_next, *rval = NULL; 1788168793Sthompsa 1789168793Sthompsa /* 1790168793Sthompsa * Search a port which reports an active link state. 1791168793Sthompsa */ 1792168793Sthompsa 1793168793Sthompsa if (lp == NULL) 1794168793Sthompsa goto search; 1795168793Sthompsa if (LAGG_PORTACTIVE(lp)) { 1796168793Sthompsa rval = lp; 1797168793Sthompsa goto found; 1798168793Sthompsa } 1799168793Sthompsa if ((lp_next = SLIST_NEXT(lp, lp_entries)) != NULL && 1800168793Sthompsa LAGG_PORTACTIVE(lp_next)) { 1801168793Sthompsa rval = lp_next; 1802168793Sthompsa goto found; 1803168793Sthompsa } 1804168793Sthompsa 1805168793Sthompsasearch: 1806168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 1807168793Sthompsa if (LAGG_PORTACTIVE(lp_next)) { 1808168793Sthompsa rval = lp_next; 1809168793Sthompsa goto found; 1810168793Sthompsa } 1811168793Sthompsa } 1812168793Sthompsa 1813168793Sthompsafound: 1814168793Sthompsa return (rval); 1815168793Sthompsa} 1816168793Sthompsa 1817168793Sthompsaint 1818168793Sthompsalagg_enqueue(struct ifnet *ifp, struct mbuf *m) 1819168793Sthompsa{ 1820168793Sthompsa 1821185164Skmacy return (ifp->if_transmit)(ifp, m); 1822168793Sthompsa} 1823168793Sthompsa 1824168793Sthompsa/* 1825168793Sthompsa * Simple round robin aggregation 1826168793Sthompsa */ 1827272161Sglebiusstatic void 1828168793Sthompsalagg_rr_attach(struct lagg_softc *sc) 1829168793Sthompsa{ 1830172554Sthompsa sc->sc_seq = 0; 1831294615Saraujo sc->sc_bkt_count = sc->sc_bkt; 1832168793Sthompsa} 1833168793Sthompsa 1834168793Sthompsastatic int 1835168793Sthompsalagg_rr_start(struct lagg_softc *sc, struct mbuf *m) 1836168793Sthompsa{ 1837172554Sthompsa struct lagg_port *lp; 1838172554Sthompsa uint32_t p; 1839168793Sthompsa 1840294615Saraujo if (sc->sc_bkt_count == 0 && sc->sc_bkt > 0) 1841294615Saraujo sc->sc_bkt_count = sc->sc_bkt; 1842294615Saraujo 1843294615Saraujo if (sc->sc_bkt > 0) { 1844294615Saraujo atomic_subtract_int(&sc->sc_bkt_count, 1); 1845294615Saraujo if (atomic_cmpset_int(&sc->sc_bkt_count, 0, sc->sc_bkt)) 1846294615Saraujo p = atomic_fetchadd_32(&sc->sc_seq, 1); 1847294615Saraujo else 1848294615Saraujo p = sc->sc_seq; 1849294615Saraujo } else 1850294615Saraujo p = atomic_fetchadd_32(&sc->sc_seq, 1); 1851294615Saraujo 1852172554Sthompsa p %= sc->sc_count; 1853172554Sthompsa lp = SLIST_FIRST(&sc->sc_ports); 1854294615Saraujo 1855172554Sthompsa while (p--) 1856172554Sthompsa lp = SLIST_NEXT(lp, lp_entries); 1857172554Sthompsa 1858172554Sthompsa /* 1859172554Sthompsa * Check the port's link state. This will return the next active 1860172554Sthompsa * port if the link is down or the port is NULL. 1861172554Sthompsa */ 1862172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 1863172554Sthompsa m_freem(m); 1864251859Sdelphij return (ENETDOWN); 1865172554Sthompsa } 1866168793Sthompsa 1867168793Sthompsa /* Send mbuf */ 1868172554Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1869168793Sthompsa} 1870168793Sthompsa 1871168793Sthompsastatic struct mbuf * 1872168793Sthompsalagg_rr_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1873168793Sthompsa{ 1874168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1875168793Sthompsa 1876168793Sthompsa /* Just pass in the packet to our lagg device */ 1877168793Sthompsa m->m_pkthdr.rcvif = ifp; 1878168793Sthompsa 1879168793Sthompsa return (m); 1880168793Sthompsa} 1881168793Sthompsa 1882168793Sthompsa/* 1883271732Saraujo * Broadcast mode 1884271732Saraujo */ 1885271732Saraujostatic int 1886271732Saraujolagg_bcast_start(struct lagg_softc *sc, struct mbuf *m) 1887271732Saraujo{ 1888272175Sglebius int active_ports = 0; 1889272175Sglebius int errors = 0; 1890272175Sglebius int ret; 1891272175Sglebius struct lagg_port *lp, *last = NULL; 1892272175Sglebius struct mbuf *m0; 1893271732Saraujo 1894272175Sglebius SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { 1895272175Sglebius if (!LAGG_PORTACTIVE(lp)) 1896272175Sglebius continue; 1897271732Saraujo 1898272175Sglebius active_ports++; 1899271732Saraujo 1900272175Sglebius if (last != NULL) { 1901272175Sglebius m0 = m_copym(m, 0, M_COPYALL, M_NOWAIT); 1902272175Sglebius if (m0 == NULL) { 1903272175Sglebius ret = ENOBUFS; 1904272175Sglebius errors++; 1905272175Sglebius break; 1906272175Sglebius } 1907271732Saraujo 1908272175Sglebius ret = lagg_enqueue(last->lp_ifp, m0); 1909272175Sglebius if (ret != 0) 1910272175Sglebius errors++; 1911272175Sglebius } 1912272175Sglebius last = lp; 1913272175Sglebius } 1914272175Sglebius if (last == NULL) { 1915272175Sglebius m_freem(m); 1916272175Sglebius return (ENOENT); 1917272175Sglebius } 1918272175Sglebius if ((last = lagg_link_active(sc, last)) == NULL) { 1919272175Sglebius m_freem(m); 1920272175Sglebius return (ENETDOWN); 1921272175Sglebius } 1922271732Saraujo 1923272175Sglebius ret = lagg_enqueue(last->lp_ifp, m); 1924272175Sglebius if (ret != 0) 1925272175Sglebius errors++; 1926271732Saraujo 1927272175Sglebius if (errors == 0) 1928272175Sglebius return (ret); 1929271732Saraujo 1930272175Sglebius return (0); 1931271732Saraujo} 1932271732Saraujo 1933271732Saraujostatic struct mbuf* 1934272175Sglebiuslagg_bcast_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1935271732Saraujo{ 1936272175Sglebius struct ifnet *ifp = sc->sc_ifp; 1937271732Saraujo 1938272175Sglebius /* Just pass in the packet to our lagg device */ 1939272175Sglebius m->m_pkthdr.rcvif = ifp; 1940272175Sglebius return (m); 1941271732Saraujo} 1942271732Saraujo 1943271732Saraujo/* 1944168793Sthompsa * Active failover 1945168793Sthompsa */ 1946168793Sthompsastatic int 1947168793Sthompsalagg_fail_start(struct lagg_softc *sc, struct mbuf *m) 1948168793Sthompsa{ 1949168793Sthompsa struct lagg_port *lp; 1950168793Sthompsa 1951168793Sthompsa /* Use the master port if active or the next available port */ 1952172554Sthompsa if ((lp = lagg_link_active(sc, sc->sc_primary)) == NULL) { 1953172554Sthompsa m_freem(m); 1954251859Sdelphij return (ENETDOWN); 1955172554Sthompsa } 1956168793Sthompsa 1957168793Sthompsa /* Send mbuf */ 1958168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 1959168793Sthompsa} 1960168793Sthompsa 1961168793Sthompsastatic struct mbuf * 1962168793Sthompsalagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 1963168793Sthompsa{ 1964168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 1965168793Sthompsa struct lagg_port *tmp_tp; 1966168793Sthompsa 1967272386Shrs if (lp == sc->sc_primary || V_lagg_failover_rx_all) { 1968168793Sthompsa m->m_pkthdr.rcvif = ifp; 1969168793Sthompsa return (m); 1970168793Sthompsa } 1971168793Sthompsa 1972174742Sthompsa if (!LAGG_PORTACTIVE(sc->sc_primary)) { 1973174742Sthompsa tmp_tp = lagg_link_active(sc, sc->sc_primary); 1974168793Sthompsa /* 1975298995Spfg * If tmp_tp is null, we've received a packet when all 1976168793Sthompsa * our links are down. Weird, but process it anyways. 1977168793Sthompsa */ 1978168793Sthompsa if ((tmp_tp == NULL || tmp_tp == lp)) { 1979168793Sthompsa m->m_pkthdr.rcvif = ifp; 1980168793Sthompsa return (m); 1981168793Sthompsa } 1982168793Sthompsa } 1983168793Sthompsa 1984168793Sthompsa m_freem(m); 1985168793Sthompsa return (NULL); 1986168793Sthompsa} 1987168793Sthompsa 1988168793Sthompsa/* 1989168793Sthompsa * Loadbalancing 1990168793Sthompsa */ 1991272161Sglebiusstatic void 1992168793Sthompsalagg_lb_attach(struct lagg_softc *sc) 1993168793Sthompsa{ 1994168793Sthompsa struct lagg_port *lp; 1995168793Sthompsa struct lagg_lb *lb; 1996168793Sthompsa 1997272161Sglebius lb = malloc(sizeof(struct lagg_lb), M_DEVBUF, M_WAITOK | M_ZERO); 1998279891Shselasky lb->lb_key = m_ether_tcpip_hash_init(); 1999272175Sglebius sc->sc_psc = lb; 2000168793Sthompsa 2001168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2002168793Sthompsa lagg_lb_port_create(lp); 2003168793Sthompsa} 2004168793Sthompsa 2005272158Sglebiusstatic void 2006168793Sthompsalagg_lb_detach(struct lagg_softc *sc) 2007168793Sthompsa{ 2008272158Sglebius struct lagg_lb *lb; 2009272158Sglebius 2010272158Sglebius lb = (struct lagg_lb *)sc->sc_psc; 2011272161Sglebius LAGG_WUNLOCK(sc); 2012168793Sthompsa if (lb != NULL) 2013168793Sthompsa free(lb, M_DEVBUF); 2014168793Sthompsa} 2015168793Sthompsa 2016168793Sthompsastatic int 2017168793Sthompsalagg_lb_porttable(struct lagg_softc *sc, struct lagg_port *lp) 2018168793Sthompsa{ 2019168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 2020168793Sthompsa struct lagg_port *lp_next; 2021168793Sthompsa int i = 0; 2022168793Sthompsa 2023168793Sthompsa bzero(&lb->lb_ports, sizeof(lb->lb_ports)); 2024168793Sthompsa SLIST_FOREACH(lp_next, &sc->sc_ports, lp_entries) { 2025168793Sthompsa if (lp_next == lp) 2026168793Sthompsa continue; 2027168793Sthompsa if (i >= LAGG_MAX_PORTS) 2028168793Sthompsa return (EINVAL); 2029168793Sthompsa if (sc->sc_ifflags & IFF_DEBUG) 2030168793Sthompsa printf("%s: port %s at index %d\n", 2031272179Sglebius sc->sc_ifname, lp_next->lp_ifp->if_xname, i); 2032168793Sthompsa lb->lb_ports[i++] = lp_next; 2033168793Sthompsa } 2034168793Sthompsa 2035168793Sthompsa return (0); 2036168793Sthompsa} 2037168793Sthompsa 2038168793Sthompsastatic int 2039168793Sthompsalagg_lb_port_create(struct lagg_port *lp) 2040168793Sthompsa{ 2041170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 2042168793Sthompsa return (lagg_lb_porttable(sc, NULL)); 2043168793Sthompsa} 2044168793Sthompsa 2045168793Sthompsastatic void 2046168793Sthompsalagg_lb_port_destroy(struct lagg_port *lp) 2047168793Sthompsa{ 2048170599Sthompsa struct lagg_softc *sc = lp->lp_softc; 2049168793Sthompsa lagg_lb_porttable(sc, lp); 2050168793Sthompsa} 2051168793Sthompsa 2052168793Sthompsastatic int 2053168793Sthompsalagg_lb_start(struct lagg_softc *sc, struct mbuf *m) 2054168793Sthompsa{ 2055168793Sthompsa struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc; 2056168793Sthompsa struct lagg_port *lp = NULL; 2057168793Sthompsa uint32_t p = 0; 2058168793Sthompsa 2059275358Shselasky if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && 2060275358Shselasky M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) 2061260070Sscottl p = m->m_pkthdr.flowid >> sc->flowid_shift; 2062191692Sthompsa else 2063279891Shselasky p = m_ether_tcpip_hash(sc->sc_flags, m, lb->lb_key); 2064180249Sthompsa p %= sc->sc_count; 2065180249Sthompsa lp = lb->lb_ports[p]; 2066168793Sthompsa 2067168793Sthompsa /* 2068168793Sthompsa * Check the port's link state. This will return the next active 2069168793Sthompsa * port if the link is down or the port is NULL. 2070168793Sthompsa */ 2071172554Sthompsa if ((lp = lagg_link_active(sc, lp)) == NULL) { 2072172554Sthompsa m_freem(m); 2073251859Sdelphij return (ENETDOWN); 2074172554Sthompsa } 2075168793Sthompsa 2076168793Sthompsa /* Send mbuf */ 2077168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 2078168793Sthompsa} 2079168793Sthompsa 2080168793Sthompsastatic struct mbuf * 2081168793Sthompsalagg_lb_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 2082168793Sthompsa{ 2083168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 2084168793Sthompsa 2085168793Sthompsa /* Just pass in the packet to our lagg device */ 2086168793Sthompsa m->m_pkthdr.rcvif = ifp; 2087168793Sthompsa 2088168793Sthompsa return (m); 2089168793Sthompsa} 2090168793Sthompsa 2091168793Sthompsa/* 2092168793Sthompsa * 802.3ad LACP 2093168793Sthompsa */ 2094272161Sglebiusstatic void 2095168793Sthompsalagg_lacp_attach(struct lagg_softc *sc) 2096168793Sthompsa{ 2097168793Sthompsa struct lagg_port *lp; 2098168793Sthompsa 2099272161Sglebius lacp_attach(sc); 2100168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2101168793Sthompsa lacp_port_create(lp); 2102168793Sthompsa} 2103168793Sthompsa 2104272158Sglebiusstatic void 2105168793Sthompsalagg_lacp_detach(struct lagg_softc *sc) 2106168793Sthompsa{ 2107168793Sthompsa struct lagg_port *lp; 2108272161Sglebius void *psc; 2109168793Sthompsa 2110168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2111168793Sthompsa lacp_port_destroy(lp); 2112168793Sthompsa 2113272161Sglebius psc = sc->sc_psc; 2114272161Sglebius sc->sc_psc = NULL; 2115169569Sthompsa LAGG_WUNLOCK(sc); 2116272161Sglebius 2117272161Sglebius lacp_detach(psc); 2118168793Sthompsa} 2119168793Sthompsa 2120168793Sthompsastatic void 2121168793Sthompsalagg_lacp_lladdr(struct lagg_softc *sc) 2122168793Sthompsa{ 2123168793Sthompsa struct lagg_port *lp; 2124168793Sthompsa 2125318329Smav LAGG_SXLOCK_ASSERT(sc); 2126318329Smav 2127168793Sthompsa /* purge all the lacp ports */ 2128168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2129168793Sthompsa lacp_port_destroy(lp); 2130168793Sthompsa 2131168793Sthompsa /* add them back in */ 2132168793Sthompsa SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) 2133168793Sthompsa lacp_port_create(lp); 2134168793Sthompsa} 2135168793Sthompsa 2136168793Sthompsastatic int 2137168793Sthompsalagg_lacp_start(struct lagg_softc *sc, struct mbuf *m) 2138168793Sthompsa{ 2139168793Sthompsa struct lagg_port *lp; 2140168793Sthompsa 2141168793Sthompsa lp = lacp_select_tx_port(sc, m); 2142172554Sthompsa if (lp == NULL) { 2143172554Sthompsa m_freem(m); 2144245741Sglebius return (ENETDOWN); 2145172554Sthompsa } 2146168793Sthompsa 2147168793Sthompsa /* Send mbuf */ 2148168793Sthompsa return (lagg_enqueue(lp->lp_ifp, m)); 2149168793Sthompsa} 2150168793Sthompsa 2151168793Sthompsastatic struct mbuf * 2152168793Sthompsalagg_lacp_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m) 2153168793Sthompsa{ 2154168793Sthompsa struct ifnet *ifp = sc->sc_ifp; 2155168793Sthompsa struct ether_header *eh; 2156168793Sthompsa u_short etype; 2157168793Sthompsa 2158168793Sthompsa eh = mtod(m, struct ether_header *); 2159168793Sthompsa etype = ntohs(eh->ether_type); 2160168793Sthompsa 2161168793Sthompsa /* Tap off LACP control messages */ 2162221270Sthompsa if ((m->m_flags & M_VLANTAG) == 0 && etype == ETHERTYPE_SLOW) { 2163175005Sthompsa m = lacp_input(lp, m); 2164175005Sthompsa if (m == NULL) 2165175005Sthompsa return (NULL); 2166168793Sthompsa } 2167168793Sthompsa 2168168793Sthompsa /* 2169168793Sthompsa * If the port is not collecting or not in the active aggregator then 2170168793Sthompsa * free and return. 2171168793Sthompsa */ 2172177274Sthompsa if (lacp_iscollecting(lp) == 0 || lacp_isactive(lp) == 0) { 2173168793Sthompsa m_freem(m); 2174168793Sthompsa return (NULL); 2175168793Sthompsa } 2176168793Sthompsa 2177168793Sthompsa m->m_pkthdr.rcvif = ifp; 2178168793Sthompsa return (m); 2179168793Sthompsa} 2180249506Sglebius 2181