123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635 |
- /* $OpenBSD: if_trunk.c,v 1.109 2015/07/17 23:32:18 mpi Exp $ */
- /*
- * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <sys/param.h>
- #include <sys/kernel.h>
- #include <sys/malloc.h>
- #include <sys/mbuf.h>
- #include <sys/queue.h>
- #include <sys/socket.h>
- #include <sys/sockio.h>
- #include <sys/systm.h>
- #include <sys/timeout.h>
- #include <crypto/siphash.h>
- #include <net/if.h>
- #include <net/if_dl.h>
- #include <net/if_media.h>
- #include <net/if_types.h>
- #include <netinet/in.h>
- #include <netinet/if_ether.h>
- #include <netinet/ip.h>
- #ifdef INET6
- #include <netinet/ip6.h>
- #endif
- #include <net/if_vlan_var.h>
- #include <net/if_trunk.h>
- #include <net/trunklacp.h>
- #include "bpfilter.h"
- #if NBPFILTER > 0
- #include <net/bpf.h>
- #endif
- SLIST_HEAD(__trhead, trunk_softc) trunk_list; /* list of trunks */
- void trunkattach(int);
- int trunk_clone_create(struct if_clone *, int);
- int trunk_clone_destroy(struct ifnet *);
- void trunk_lladdr(struct arpcom *, u_int8_t *);
- int trunk_capabilities(struct trunk_softc *);
- void trunk_port_lladdr(struct trunk_port *, u_int8_t *);
- int trunk_port_create(struct trunk_softc *, struct ifnet *);
- int trunk_port_destroy(struct trunk_port *);
- void trunk_port_watchdog(struct ifnet *);
- void trunk_port_state(void *);
- void trunk_port_ifdetach(void *);
- int trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
- int trunk_port_output(struct ifnet *, struct mbuf *, struct sockaddr *,
- struct rtentry *);
- struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
- int trunk_port_checkstacking(struct trunk_softc *);
- void trunk_port2req(struct trunk_port *, struct trunk_reqport *);
- int trunk_ioctl(struct ifnet *, u_long, caddr_t);
- int trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
- int trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
- void trunk_ether_purgemulti(struct trunk_softc *);
- int trunk_ether_cmdmulti(struct trunk_port *, u_long);
- int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
- int trunk_input(struct ifnet *, struct mbuf *);
- void trunk_start(struct ifnet *);
- void trunk_init(struct ifnet *);
- void trunk_stop(struct ifnet *);
- void trunk_watchdog(struct ifnet *);
- int trunk_media_change(struct ifnet *);
- void trunk_media_status(struct ifnet *, struct ifmediareq *);
- struct trunk_port *trunk_link_active(struct trunk_softc *,
- struct trunk_port *);
- const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *);
- struct if_clone trunk_cloner =
- IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
- /* Simple round robin */
- int trunk_rr_attach(struct trunk_softc *);
- int trunk_rr_detach(struct trunk_softc *);
- void trunk_rr_port_destroy(struct trunk_port *);
- int trunk_rr_start(struct trunk_softc *, struct mbuf *);
- int trunk_rr_input(struct trunk_softc *, struct trunk_port *,
- struct mbuf *);
- /* Active failover */
- int trunk_fail_attach(struct trunk_softc *);
- int trunk_fail_detach(struct trunk_softc *);
- int trunk_fail_start(struct trunk_softc *, struct mbuf *);
- int trunk_fail_input(struct trunk_softc *, struct trunk_port *,
- struct mbuf *);
- /* Loadbalancing */
- int trunk_lb_attach(struct trunk_softc *);
- int trunk_lb_detach(struct trunk_softc *);
- int trunk_lb_port_create(struct trunk_port *);
- void trunk_lb_port_destroy(struct trunk_port *);
- int trunk_lb_start(struct trunk_softc *, struct mbuf *);
- int trunk_lb_input(struct trunk_softc *, struct trunk_port *,
- struct mbuf *);
- int trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
- /* Broadcast mode */
- int trunk_bcast_attach(struct trunk_softc *);
- int trunk_bcast_detach(struct trunk_softc *);
- int trunk_bcast_start(struct trunk_softc *, struct mbuf *);
- int trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
- struct mbuf *);
- /* 802.3ad LACP */
- int trunk_lacp_attach(struct trunk_softc *);
- int trunk_lacp_detach(struct trunk_softc *);
- int trunk_lacp_start(struct trunk_softc *, struct mbuf *);
- int trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
- struct mbuf *);
- /* Trunk protocol table */
- static const struct {
- enum trunk_proto ti_proto;
- int (*ti_attach)(struct trunk_softc *);
- } trunk_protos[] = {
- { TRUNK_PROTO_ROUNDROBIN, trunk_rr_attach },
- { TRUNK_PROTO_FAILOVER, trunk_fail_attach },
- { TRUNK_PROTO_LOADBALANCE, trunk_lb_attach },
- { TRUNK_PROTO_BROADCAST, trunk_bcast_attach },
- { TRUNK_PROTO_LACP, trunk_lacp_attach },
- { TRUNK_PROTO_NONE, NULL }
- };
- void
- trunkattach(int count)
- {
- SLIST_INIT(&trunk_list);
- if_clone_attach(&trunk_cloner);
- }
- int
- trunk_clone_create(struct if_clone *ifc, int unit)
- {
- struct trunk_softc *tr;
- struct ifnet *ifp;
- int i, error = 0;
- if ((tr = malloc(sizeof(struct trunk_softc),
- M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
- return (ENOMEM);
- tr->tr_unit = unit;
- tr->tr_proto = TRUNK_PROTO_NONE;
- for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
- if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
- tr->tr_proto = trunk_protos[i].ti_proto;
- if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
- free(tr, M_DEVBUF, 0);
- return (error);
- }
- break;
- }
- }
- SLIST_INIT(&tr->tr_ports);
- /* Initialise pseudo media types */
- ifmedia_init(&tr->tr_media, 0, trunk_media_change,
- trunk_media_status);
- ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
- ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
- ifp = &tr->tr_ac.ac_if;
- ifp->if_softc = tr;
- ifp->if_start = trunk_start;
- ifp->if_watchdog = trunk_watchdog;
- ifp->if_ioctl = trunk_ioctl;
- ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
- ifp->if_capabilities = trunk_capabilities(tr);
- IFQ_SET_MAXLEN(&ifp->if_snd, 1);
- IFQ_SET_READY(&ifp->if_snd);
- snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
- ifc->ifc_name, unit);
- /*
- * Attach as an ordinary ethernet device, children will be attached
- * as special device IFT_IEEE8023ADLAG.
- */
- if_attach(ifp);
- ether_ifattach(ifp);
- /* Insert into the global list of trunks */
- SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
- return (0);
- }
- int
- trunk_clone_destroy(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- struct trunk_port *tp;
- int error, s;
- /* Remove any multicast groups that we may have joined. */
- trunk_ether_purgemulti(tr);
- s = splnet();
- /* Shutdown and remove trunk ports, return on error */
- while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
- if ((error = trunk_port_destroy(tp)) != 0) {
- splx(s);
- return (error);
- }
- }
- ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
- ether_ifdetach(ifp);
- if_detach(ifp);
- SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
- free(tr, M_DEVBUF, 0);
- splx(s);
- return (0);
- }
- void
- trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
- {
- struct ifnet *ifp = &ac->ac_if;
- struct sockaddr_dl *sdl;
- sdl = ifp->if_sadl;
- sdl->sdl_type = IFT_ETHER;
- sdl->sdl_alen = ETHER_ADDR_LEN;
- bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
- bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
- }
- int
- trunk_capabilities(struct trunk_softc *tr)
- {
- struct trunk_port *tp;
- int cap = ~0, priv;
- /* Preserve private capabilities */
- priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
- /* Get capabilities from the trunk ports */
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
- cap &= tp->tp_capabilities;
- if (tr->tr_ifflags & IFF_DEBUG) {
- printf("%s: capabilities 0x%08x\n",
- tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
- }
- return (cap == ~0 ? priv : (cap | priv));
- }
- void
- trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
- {
- struct ifnet *ifp = tp->tp_if;
- /* Set the link layer address */
- trunk_lladdr((struct arpcom *)ifp, lladdr);
- /* Reset the port to update the lladdr */
- ifnewlladdr(ifp);
- }
- int
- trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
- {
- struct trunk_softc *tr_ptr;
- struct trunk_port *tp;
- int error = 0;
- /* Limit the maximal number of trunk ports */
- if (tr->tr_count >= TRUNK_MAX_PORTS)
- return (ENOSPC);
- /* New trunk port has to be in an idle state */
- if (ifp->if_flags & IFF_OACTIVE)
- return (EBUSY);
- /* Check if port has already been associated to a trunk */
- if (trunk_port_get(NULL, ifp) != NULL)
- return (EBUSY);
- /* XXX Disallow non-ethernet interfaces (this should be any of 802) */
- if (ifp->if_type != IFT_ETHER)
- return (EPROTONOSUPPORT);
- /* Take MTU from the first member port */
- if (SLIST_EMPTY(&tr->tr_ports)) {
- if (tr->tr_ifflags & IFF_DEBUG)
- printf("%s: first port, setting trunk mtu %u\n",
- tr->tr_ifname, ifp->if_mtu);
- tr->tr_ac.ac_if.if_mtu = ifp->if_mtu;
- tr->tr_ac.ac_if.if_hardmtu = ifp->if_mtu;
- } else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) {
- printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname,
- ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu);
- return (EINVAL);
- }
- if ((error = ifpromisc(ifp, 1)) != 0)
- return (error);
- if ((tp = malloc(sizeof(struct trunk_port),
- M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
- return (ENOMEM);
- /* Check if port is a stacked trunk */
- SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
- if (ifp == &tr_ptr->tr_ac.ac_if) {
- tp->tp_flags |= TRUNK_PORT_STACK;
- if (trunk_port_checkstacking(tr_ptr) >=
- TRUNK_MAX_STACKING) {
- free(tp, M_DEVBUF, 0);
- return (E2BIG);
- }
- }
- }
- /* Change the interface type */
- tp->tp_iftype = ifp->if_type;
- ifp->if_type = IFT_IEEE8023ADLAG;
- /* Change input handler of the physical interface. */
- tp->tp_ifih.ifih_input = trunk_input;
- SLIST_INSERT_HEAD(&ifp->if_inputs, &tp->tp_ifih, ifih_next);
- ifp->if_tp = (caddr_t)tp;
- tp->tp_ioctl = ifp->if_ioctl;
- ifp->if_ioctl = trunk_port_ioctl;
- timeout_del(ifp->if_slowtimo);
- tp->tp_watchdog = ifp->if_watchdog;
- ifp->if_watchdog = trunk_port_watchdog;
- tp->tp_output = ifp->if_output;
- ifp->if_output = trunk_port_output;
- tp->tp_if = ifp;
- tp->tp_trunk = tr;
- /* Save port link layer address */
- bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
- if (SLIST_EMPTY(&tr->tr_ports)) {
- tr->tr_primary = tp;
- tp->tp_flags |= TRUNK_PORT_MASTER;
- trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
- }
- /* Update link layer address for this port */
- trunk_port_lladdr(tp,
- ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr);
- /* Insert into the list of ports */
- SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
- tr->tr_count++;
- /* Update trunk capabilities */
- tr->tr_capabilities = trunk_capabilities(tr);
- /* Add multicast addresses to this port */
- trunk_ether_cmdmulti(tp, SIOCADDMULTI);
- /* Register callback for physical link state changes */
- tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
- trunk_port_state, tp);
- /* Register callback if parent wants to unregister */
- tp->dh_cookie = hook_establish(ifp->if_detachhooks, 0,
- trunk_port_ifdetach, tp);
- if (tr->tr_port_create != NULL)
- error = (*tr->tr_port_create)(tp);
- return (error);
- }
- int
- trunk_port_checkstacking(struct trunk_softc *tr)
- {
- struct trunk_softc *tr_ptr;
- struct trunk_port *tp;
- int m = 0;
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
- if (tp->tp_flags & TRUNK_PORT_STACK) {
- tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
- m = MAX(m, trunk_port_checkstacking(tr_ptr));
- }
- }
- return (m + 1);
- }
- int
- trunk_port_destroy(struct trunk_port *tp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- struct trunk_port *tp_ptr;
- struct ifnet *ifp = tp->tp_if;
- if (tr->tr_port_destroy != NULL)
- (*tr->tr_port_destroy)(tp);
- /* Remove multicast addresses from this port */
- trunk_ether_cmdmulti(tp, SIOCDELMULTI);
- /* Port has to be down */
- if (ifp->if_flags & IFF_UP)
- if_down(ifp);
- ifpromisc(ifp, 0);
- /* Restore interface type. */
- ifp->if_type = tp->tp_iftype;
- /* Restore previous input handler. */
- SLIST_REMOVE(&ifp->if_inputs, &tp->tp_ifih, ifih, ifih_next);
- ifp->if_watchdog = tp->tp_watchdog;
- ifp->if_ioctl = tp->tp_ioctl;
- ifp->if_output = tp->tp_output;
- ifp->if_tp = NULL;
- hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie);
- hook_disestablish(ifp->if_detachhooks, tp->dh_cookie);
- /* Finally, remove the port from the trunk */
- SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
- tr->tr_count--;
- /* Update the primary interface */
- if (tp == tr->tr_primary) {
- u_int8_t lladdr[ETHER_ADDR_LEN];
- if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
- bzero(&lladdr, ETHER_ADDR_LEN);
- } else {
- bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
- lladdr, ETHER_ADDR_LEN);
- tp_ptr->tp_flags = TRUNK_PORT_MASTER;
- }
- trunk_lladdr(&tr->tr_ac, lladdr);
- tr->tr_primary = tp_ptr;
- /* Update link layer address for each port */
- SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
- trunk_port_lladdr(tp_ptr, lladdr);
- }
- /* Reset the port lladdr */
- trunk_port_lladdr(tp, tp->tp_lladdr);
- free(tp, M_DEVBUF, 0);
- /* Update trunk capabilities */
- tr->tr_capabilities = trunk_capabilities(tr);
- /* Reestablish watchdog timeout */
- if_slowtimo(ifp);
- return (0);
- }
- void
- trunk_port_watchdog(struct ifnet *ifp)
- {
- struct trunk_port *tp;
- /* Should be checked by the caller */
- if (ifp->if_type != IFT_IEEE8023ADLAG)
- return;
- if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
- tp->tp_trunk == NULL)
- return;
- if (tp->tp_watchdog != NULL)
- (*tp->tp_watchdog)(ifp);
- }
- int
- trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
- {
- struct trunk_reqport *rp = (struct trunk_reqport *)data;
- struct trunk_softc *tr;
- struct trunk_port *tp = NULL;
- int s, error = 0;
- s = splnet();
- /* Should be checked by the caller */
- if (ifp->if_type != IFT_IEEE8023ADLAG ||
- (tp = (struct trunk_port *)ifp->if_tp) == NULL ||
- (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
- error = EINVAL;
- goto fallback;
- }
- switch (cmd) {
- case SIOCGTRUNKPORT:
- if (rp->rp_portname[0] == '\0' ||
- ifunit(rp->rp_portname) != ifp) {
- error = EINVAL;
- break;
- }
- /* Search in all trunks if the global flag is set */
- if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
- NULL : tr, ifp)) == NULL) {
- error = ENOENT;
- break;
- }
- trunk_port2req(tp, rp);
- break;
- case SIOCSIFMTU:
- /* Do not allow the MTU to be changed once joined */
- error = EINVAL;
- break;
- default:
- error = ENOTTY;
- goto fallback;
- }
- splx(s);
- return (error);
- fallback:
- splx(s);
- if (tp != NULL)
- error = (*tp->tp_ioctl)(ifp, cmd, data);
- return (error);
- }
- int
- trunk_port_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
- struct rtentry *rt)
- {
- /* restrict transmission on trunk members to bpf only */
- if (ifp->if_type == IFT_IEEE8023ADLAG &&
- (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
- m_freem(m);
- return (EBUSY);
- }
- return (ether_output(ifp, m, dst, rt));
- }
- void
- trunk_port_ifdetach(void *arg)
- {
- struct trunk_port *tp = (struct trunk_port *)arg;
- trunk_port_destroy(tp);
- }
- struct trunk_port *
- trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
- {
- struct trunk_port *tp;
- struct trunk_softc *tr_ptr;
- if (tr != NULL) {
- /* Search port in specified trunk */
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
- if (tp->tp_if == ifp)
- return (tp);
- }
- } else {
- /* Search all trunks for the selected port */
- SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
- SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
- if (tp->tp_if == ifp)
- return (tp);
- }
- }
- }
- return (NULL);
- }
- void
- trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
- strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
- rp->rp_prio = tp->tp_prio;
- if (tr->tr_portreq != NULL)
- (*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc);
- /* Add protocol specific flags */
- switch (tr->tr_proto) {
- case TRUNK_PROTO_FAILOVER:
- rp->rp_flags = tp->tp_flags;
- if (tp == trunk_link_active(tr, tr->tr_primary))
- rp->rp_flags |= TRUNK_PORT_ACTIVE;
- break;
- case TRUNK_PROTO_ROUNDROBIN:
- case TRUNK_PROTO_LOADBALANCE:
- case TRUNK_PROTO_BROADCAST:
- rp->rp_flags = tp->tp_flags;
- if (TRUNK_PORTACTIVE(tp))
- rp->rp_flags |= TRUNK_PORT_ACTIVE;
- break;
- case TRUNK_PROTO_LACP:
- /* LACP has a different definition of active */
- rp->rp_flags = lacp_port_status(tp);
- break;
- default:
- break;
- }
- }
- int
- trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- struct trunk_reqall *ra = (struct trunk_reqall *)data;
- struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
- struct ifreq *ifr = (struct ifreq *)data;
- struct ifaddr *ifa = (struct ifaddr *)data;
- struct trunk_port *tp;
- struct ifnet *tpif;
- int s, i, error = 0;
- s = splnet();
- bzero(&rpbuf, sizeof(rpbuf));
- switch (cmd) {
- case SIOCGTRUNK:
- ra->ra_proto = tr->tr_proto;
- if (tr->tr_req != NULL)
- (*tr->tr_req)(tr, (caddr_t)&ra->ra_psc);
- ra->ra_ports = i = 0;
- tp = SLIST_FIRST(&tr->tr_ports);
- while (tp && ra->ra_size >=
- i + sizeof(struct trunk_reqport)) {
- trunk_port2req(tp, &rpbuf);
- error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
- sizeof(struct trunk_reqport));
- if (error)
- break;
- i += sizeof(struct trunk_reqport);
- ra->ra_ports++;
- tp = SLIST_NEXT(tp, tp_entries);
- }
- break;
- case SIOCSTRUNK:
- if ((error = suser(curproc, 0)) != 0) {
- error = EPERM;
- break;
- }
- if (ra->ra_proto >= TRUNK_PROTO_MAX) {
- error = EPROTONOSUPPORT;
- break;
- }
- if (tr->tr_proto != TRUNK_PROTO_NONE)
- error = tr->tr_detach(tr);
- if (error != 0)
- break;
- for (i = 0; i < (sizeof(trunk_protos) /
- sizeof(trunk_protos[0])); i++) {
- if (trunk_protos[i].ti_proto == ra->ra_proto) {
- if (tr->tr_ifflags & IFF_DEBUG)
- printf("%s: using proto %u\n",
- tr->tr_ifname,
- trunk_protos[i].ti_proto);
- tr->tr_proto = trunk_protos[i].ti_proto;
- if (tr->tr_proto != TRUNK_PROTO_NONE)
- error = trunk_protos[i].ti_attach(tr);
- goto out;
- }
- }
- error = EPROTONOSUPPORT;
- break;
- case SIOCGTRUNKPORT:
- if (rp->rp_portname[0] == '\0' ||
- (tpif = ifunit(rp->rp_portname)) == NULL) {
- error = EINVAL;
- break;
- }
- /* Search in all trunks if the global flag is set */
- if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
- NULL : tr, tpif)) == NULL) {
- error = ENOENT;
- break;
- }
- trunk_port2req(tp, rp);
- break;
- case SIOCSTRUNKPORT:
- if ((error = suser(curproc, 0)) != 0) {
- error = EPERM;
- break;
- }
- if (rp->rp_portname[0] == '\0' ||
- (tpif = ifunit(rp->rp_portname)) == NULL) {
- error = EINVAL;
- break;
- }
- error = trunk_port_create(tr, tpif);
- break;
- case SIOCSTRUNKDELPORT:
- if ((error = suser(curproc, 0)) != 0) {
- error = EPERM;
- break;
- }
- if (rp->rp_portname[0] == '\0' ||
- (tpif = ifunit(rp->rp_portname)) == NULL) {
- error = EINVAL;
- break;
- }
- /* Search in all trunks if the global flag is set */
- if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
- NULL : tr, tpif)) == NULL) {
- error = ENOENT;
- break;
- }
- error = trunk_port_destroy(tp);
- break;
- case SIOCSIFADDR:
- ifp->if_flags |= IFF_UP;
- if (ifa->ifa_addr->sa_family == AF_INET)
- arp_ifinit(&tr->tr_ac, ifa);
- error = ENETRESET;
- break;
- case SIOCSIFFLAGS:
- error = ENETRESET;
- break;
- case SIOCADDMULTI:
- error = trunk_ether_addmulti(tr, ifr);
- break;
- case SIOCDELMULTI:
- error = trunk_ether_delmulti(tr, ifr);
- break;
- case SIOCSIFMEDIA:
- case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
- break;
- case SIOCSIFLLADDR:
- /* Update the port lladdrs as well */
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
- trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
- error = ENETRESET;
- break;
- default:
- error = ether_ioctl(ifp, &tr->tr_ac, cmd, data);
- }
- if (error == ENETRESET) {
- if (ifp->if_flags & IFF_UP) {
- if ((ifp->if_flags & IFF_RUNNING) == 0)
- trunk_init(ifp);
- } else {
- if (ifp->if_flags & IFF_RUNNING)
- trunk_stop(ifp);
- }
- error = 0;
- }
- out:
- splx(s);
- return (error);
- }
- int
- trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
- {
- struct trunk_mc *mc;
- u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
- int error;
- /* Ignore ENETRESET error code */
- if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
- return (error);
- if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
- error = ENOMEM;
- goto failed;
- }
- ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
- ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
- bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
- SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
- if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
- (caddr_t)ifr)) != 0) {
- trunk_ether_delmulti(tr, ifr);
- return (error);
- }
- return (error);
- failed:
- ether_delmulti(ifr, &tr->tr_ac);
- return (error);
- }
- int
- trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
- {
- struct ether_multi *enm;
- struct trunk_mc *mc;
- u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
- int error;
- if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
- return (error);
- ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
- if (enm == NULL)
- return (EINVAL);
- SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
- if (mc->mc_enm == enm)
- break;
- /* We won't delete entries we didn't add */
- if (mc == NULL)
- return (EINVAL);
- if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
- return (error);
- /* We no longer use this multicast address. Tell parent so. */
- error = trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
- if (error == 0) {
- SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
- free(mc, M_DEVBUF, sizeof(*mc));
- } else {
- /* XXX At least one port failed to remove the address */
- if (tr->tr_ifflags & IFF_DEBUG) {
- printf("%s: failed to remove multicast address "
- "on all ports (%d)\n", tr->tr_ifname, error);
- }
- (void)ether_addmulti(ifr, &tr->tr_ac);
- }
- return (0);
- }
- void
- trunk_ether_purgemulti(struct trunk_softc *tr)
- {
- struct trunk_mc *mc;
- struct trunk_ifreq ifs;
- struct ifreq *ifr = &ifs.ifreq.ifreq;
- while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
- bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
- /* Try to remove multicast address on all ports */
- trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
- SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
- free(mc, M_DEVBUF, sizeof(*mc));
- }
- }
- int
- trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- struct trunk_mc *mc;
- struct trunk_ifreq ifs;
- struct ifreq *ifr = &ifs.ifreq.ifreq;
- int ret, error = 0;
- bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
- SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
- bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
- if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
- if (tr->tr_ifflags & IFF_DEBUG) {
- printf("%s: ioctl %lu failed on %s: %d\n",
- tr->tr_ifname, cmd, tp->tp_ifname, ret);
- }
- /* Store last known error and continue */
- error = ret;
- }
- }
- return (error);
- }
- int
- trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
- {
- struct ifreq *ifr = (struct ifreq *)data;
- struct trunk_port *tp;
- int ret, error = 0;
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
- bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
- if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
- if (tr->tr_ifflags & IFF_DEBUG) {
- printf("%s: ioctl %lu failed on %s: %d\n",
- tr->tr_ifname, cmd, tp->tp_ifname, ret);
- }
- /* Store last known error and continue */
- error = ret;
- }
- }
- return (error);
- }
- void
- trunk_start(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- struct mbuf *m;
- int error;
- for (;;) {
- IFQ_DEQUEUE(&ifp->if_snd, m);
- if (m == NULL)
- break;
- #if NBPFILTER > 0
- if (ifp->if_bpf)
- bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
- #endif
- if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) {
- error = (*tr->tr_start)(tr, m);
- if (error == 0)
- ifp->if_opackets++;
- else
- ifp->if_oerrors++;
- } else {
- m_freem(m);
- if (tr->tr_proto != TRUNK_PROTO_NONE)
- ifp->if_oerrors++;
- }
- }
- }
- u_int32_t
- trunk_hashmbuf(struct mbuf *m, SIPHASH_KEY *key)
- {
- u_int16_t etype, ether_vtag;
- u_int32_t p = 0;
- u_int16_t *vlan, vlanbuf[2];
- int off;
- struct ether_header *eh;
- struct ip *ip, ipbuf;
- #ifdef INET6
- u_int32_t flow;
- struct ip6_hdr *ip6, ip6buf;
- #endif
- SIPHASH_CTX ctx;
- SipHash24_Init(&ctx, key);
- off = sizeof(*eh);
- if (m->m_len < off)
- goto done;
- eh = mtod(m, struct ether_header *);
- etype = ntohs(eh->ether_type);
- SipHash24_Update(&ctx, &eh->ether_shost, ETHER_ADDR_LEN);
- SipHash24_Update(&ctx, &eh->ether_dhost, ETHER_ADDR_LEN);
- /* Special handling for encapsulating VLAN frames */
- if (m->m_flags & M_VLANTAG) {
- ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
- SipHash24_Update(&ctx, ðer_vtag, sizeof(ether_vtag));
- } else if (etype == ETHERTYPE_VLAN) {
- if ((vlan = (u_int16_t *)
- trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
- return (p);
- ether_vtag = EVL_VLANOFTAG(*vlan);
- SipHash24_Update(&ctx, ðer_vtag, sizeof(ether_vtag));
- etype = ntohs(vlan[1]);
- off += EVL_ENCAPLEN;
- }
- switch (etype) {
- case ETHERTYPE_IP:
- if ((ip = (struct ip *)
- trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
- return (p);
- SipHash24_Update(&ctx, &ip->ip_src, sizeof(struct in_addr));
- SipHash24_Update(&ctx, &ip->ip_dst, sizeof(struct in_addr));
- break;
- #ifdef INET6
- case ETHERTYPE_IPV6:
- if ((ip6 = (struct ip6_hdr *)
- trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
- return (p);
- SipHash24_Update(&ctx, &ip6->ip6_src, sizeof(struct in6_addr));
- SipHash24_Update(&ctx, &ip6->ip6_dst, sizeof(struct in6_addr));
- flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
- SipHash24_Update(&ctx, &flow, sizeof(flow)); /* IPv6 flow label */
- break;
- #endif
- }
- done:
- return SipHash24_End(&ctx);
- }
- void
- trunk_init(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- int s;
- s = splnet();
- ifp->if_flags |= IFF_RUNNING;
- ifp->if_flags &= ~IFF_OACTIVE;
- if (tr->tr_init != NULL)
- (*tr->tr_init)(tr);
- splx(s);
- }
- void
- trunk_stop(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- int s;
- s = splnet();
- ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
- if (tr->tr_stop != NULL)
- (*tr->tr_stop)(tr);
- splx(s);
- }
- void
- trunk_watchdog(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- if (tr->tr_proto != TRUNK_PROTO_NONE &&
- (*tr->tr_watchdog)(tr) != 0) {
- ifp->if_oerrors++;
- }
- }
- int
- trunk_input(struct ifnet *ifp, struct mbuf *m)
- {
- struct trunk_softc *tr;
- struct trunk_port *tp;
- struct ifnet *trifp = NULL;
- struct ether_header *eh;
- struct mbuf_list ml = MBUF_LIST_INITIALIZER();
- eh = mtod(m, struct ether_header *);
- if (ETHER_IS_MULTICAST(eh->ether_dhost))
- ifp->if_imcasts++;
- /* Should be checked by the caller */
- if (ifp->if_type != IFT_IEEE8023ADLAG)
- goto bad;
- if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
- (tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
- goto bad;
- trifp = &tr->tr_ac.ac_if;
- if (tr->tr_proto == TRUNK_PROTO_NONE)
- goto bad;
- if ((*tr->tr_input)(tr, tp, m)) {
- /*
- * We stop here if the packet has been consumed
- * by the protocol routine.
- */
- m_freem(m);
- return (1);
- }
- if ((trifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
- goto bad;
- /*
- * Drop promiscuously received packets if we are not in
- * promiscuous mode.
- */
- if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
- (ifp->if_flags & IFF_PROMISC) &&
- (trifp->if_flags & IFF_PROMISC) == 0) {
- if (bcmp(&tr->tr_ac.ac_enaddr, eh->ether_dhost,
- ETHER_ADDR_LEN)) {
- m_freem(m);
- return (1);
- }
- }
- ml_enqueue(&ml, m);
- if_input(trifp, &ml);
- return (1);
- bad:
- if (trifp != NULL)
- trifp->if_ierrors++;
- m_freem(m);
- return (1);
- }
- int
- trunk_media_change(struct ifnet *ifp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- if (tr->tr_ifflags & IFF_DEBUG)
- printf("%s\n", __func__);
- /* Ignore */
- return (0);
- }
- void
- trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
- {
- struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
- struct trunk_port *tp;
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_ETHER | IFM_AUTO;
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
- if (TRUNK_PORTACTIVE(tp))
- imr->ifm_status |= IFM_ACTIVE;
- }
- }
- void
- trunk_port_state(void *arg)
- {
- struct trunk_port *tp = (struct trunk_port *)arg;
- struct trunk_softc *tr = NULL;
- if (tp != NULL)
- tr = (struct trunk_softc *)tp->tp_trunk;
- if (tr == NULL)
- return;
- if (tr->tr_linkstate != NULL)
- (*tr->tr_linkstate)(tp);
- trunk_link_active(tr, tp);
- }
- struct trunk_port *
- trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
- {
- struct trunk_port *tp_next, *rval = NULL;
- int new_link = LINK_STATE_DOWN;
- /*
- * Search a port which reports an active link state.
- */
- if (tp == NULL)
- goto search;
- if (TRUNK_PORTACTIVE(tp)) {
- rval = tp;
- goto found;
- }
- if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
- TRUNK_PORTACTIVE(tp_next)) {
- rval = tp_next;
- goto found;
- }
- search:
- SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
- if (TRUNK_PORTACTIVE(tp_next)) {
- rval = tp_next;
- goto found;
- }
- }
- found:
- if (rval != NULL) {
- /*
- * The IEEE 802.1D standard assumes that a trunk with
- * multiple ports is always full duplex. This is valid
- * for load sharing trunks and if at least two links
- * are active. Unfortunately, checking the latter would
- * be too expensive at this point.
- */
- if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
- (tr->tr_count > 1))
- new_link = LINK_STATE_FULL_DUPLEX;
- else
- new_link = rval->tp_link_state;
- }
- if (tr->tr_ac.ac_if.if_link_state != new_link) {
- tr->tr_ac.ac_if.if_link_state = new_link;
- if_link_state_change(&tr->tr_ac.ac_if);
- }
- return (rval);
- }
- const void *
- trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
- {
- if (m->m_pkthdr.len < (off + len))
- return (NULL);
- else if (m->m_len < (off + len)) {
- m_copydata(m, off, len, buf);
- return (buf);
- }
- return (mtod(m, caddr_t) + off);
- }
- /*
- * Simple round robin trunking
- */
- int
- trunk_rr_attach(struct trunk_softc *tr)
- {
- struct trunk_port *tp;
- tr->tr_detach = trunk_rr_detach;
- tr->tr_start = trunk_rr_start;
- tr->tr_input = trunk_rr_input;
- tr->tr_init = NULL;
- tr->tr_stop = NULL;
- tr->tr_linkstate = NULL;
- tr->tr_port_create = NULL;
- tr->tr_port_destroy = trunk_rr_port_destroy;
- tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
- tr->tr_req = NULL;
- tr->tr_portreq = NULL;
- tp = SLIST_FIRST(&tr->tr_ports);
- tr->tr_psc = (caddr_t)tp;
- return (0);
- }
- int
- trunk_rr_detach(struct trunk_softc *tr)
- {
- tr->tr_psc = NULL;
- return (0);
- }
- void
- trunk_rr_port_destroy(struct trunk_port *tp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- if (tp == (struct trunk_port *)tr->tr_psc)
- tr->tr_psc = NULL;
- }
- int
- trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
- {
- struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
- int error = 0;
- if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) {
- m_freem(m);
- return (ENOENT);
- }
- if ((error = if_enqueue(tp->tp_if, m)) != 0)
- return (error);
- /* Get next active port */
- tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
- tr->tr_psc = (caddr_t)tp_next;
- return (0);
- }
- int
- trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
- {
- /* Just pass in the packet to our trunk device */
- return (0);
- }
- /*
- * Active failover
- */
- int
- trunk_fail_attach(struct trunk_softc *tr)
- {
- tr->tr_detach = trunk_fail_detach;
- tr->tr_start = trunk_fail_start;
- tr->tr_input = trunk_fail_input;
- tr->tr_init = NULL;
- tr->tr_stop = NULL;
- tr->tr_port_create = NULL;
- tr->tr_port_destroy = NULL;
- tr->tr_linkstate = NULL;
- tr->tr_req = NULL;
- tr->tr_portreq = NULL;
- return (0);
- }
- int
- trunk_fail_detach(struct trunk_softc *tr)
- {
- return (0);
- }
- int
- trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
- {
- struct trunk_port *tp;
- /* Use the master port if active or the next available port */
- if ((tp = trunk_link_active(tr, tr->tr_primary)) == NULL) {
- m_freem(m);
- return (ENOENT);
- }
- return (if_enqueue(tp->tp_if, m));
- }
- int
- trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
- {
- struct trunk_port *tmp_tp;
- int accept = 0;
- if (tp == tr->tr_primary) {
- accept = 1;
- } else if (tr->tr_primary->tp_link_state == LINK_STATE_DOWN) {
- tmp_tp = trunk_link_active(tr, NULL);
- /*
- * If tmp_tp is null, we've received a packet when all
- * our links are down. Weird, but process it anyways.
- */
- if ((tmp_tp == NULL || tmp_tp == tp))
- accept = 1;
- }
- if (!accept)
- return (-1);
- return (0);
- }
- /*
- * Loadbalancing
- */
- int
- trunk_lb_attach(struct trunk_softc *tr)
- {
- struct trunk_lb *lb;
- if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
- return (ENOMEM);
- tr->tr_detach = trunk_lb_detach;
- tr->tr_start = trunk_lb_start;
- tr->tr_input = trunk_lb_input;
- tr->tr_port_create = trunk_lb_port_create;
- tr->tr_port_destroy = trunk_lb_port_destroy;
- tr->tr_linkstate = NULL;
- tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
- tr->tr_req = NULL;
- tr->tr_portreq = NULL;
- tr->tr_init = NULL;
- tr->tr_stop = NULL;
- arc4random_buf(&lb->lb_key, sizeof(lb->lb_key));
- tr->tr_psc = (caddr_t)lb;
- return (0);
- }
- int
- trunk_lb_detach(struct trunk_softc *tr)
- {
- struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
- if (lb != NULL)
- free(lb, M_DEVBUF, 0);
- return (0);
- }
- int
- trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
- {
- struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
- struct trunk_port *tp_next;
- int i = 0;
- bzero(&lb->lb_ports, sizeof(lb->lb_ports));
- SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
- if (tp_next == tp)
- continue;
- if (i >= TRUNK_MAX_PORTS)
- return (EINVAL);
- if (tr->tr_ifflags & IFF_DEBUG)
- printf("%s: port %s at index %d\n",
- tr->tr_ifname, tp_next->tp_ifname, i);
- lb->lb_ports[i++] = tp_next;
- }
- return (0);
- }
- int
- trunk_lb_port_create(struct trunk_port *tp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- return (trunk_lb_porttable(tr, NULL));
- }
- void
- trunk_lb_port_destroy(struct trunk_port *tp)
- {
- struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
- trunk_lb_porttable(tr, tp);
- }
- int
- trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
- {
- struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
- struct trunk_port *tp = NULL;
- u_int32_t p = 0;
- p = trunk_hashmbuf(m, &lb->lb_key);
- p %= tr->tr_count;
- tp = lb->lb_ports[p];
- /*
- * Check the port's link state. This will return the next active
- * port if the link is down or the port is NULL.
- */
- if ((tp = trunk_link_active(tr, tp)) == NULL) {
- m_freem(m);
- return (ENOENT);
- }
- return (if_enqueue(tp->tp_if, m));
- }
- int
- trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
- {
- /* Just pass in the packet to our trunk device */
- return (0);
- }
- /*
- * Broadcast mode
- */
- int
- trunk_bcast_attach(struct trunk_softc *tr)
- {
- tr->tr_detach = trunk_bcast_detach;
- tr->tr_start = trunk_bcast_start;
- tr->tr_input = trunk_bcast_input;
- tr->tr_init = NULL;
- tr->tr_stop = NULL;
- tr->tr_port_create = NULL;
- tr->tr_port_destroy = NULL;
- tr->tr_linkstate = NULL;
- tr->tr_req = NULL;
- tr->tr_portreq = NULL;
- return (0);
- }
- int
- trunk_bcast_detach(struct trunk_softc *tr)
- {
- return (0);
- }
- int
- trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0)
- {
- int active_ports = 0;
- int errors = 0;
- int ret;
- struct trunk_port *tp, *last = NULL;
- struct mbuf *m;
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
- if (!TRUNK_PORTACTIVE(tp))
- continue;
- active_ports++;
- if (last != NULL) {
- m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
- if (m == NULL) {
- ret = ENOBUFS;
- errors++;
- break;
- }
- ret = if_enqueue(last->tp_if, m);
- if (ret != 0)
- errors++;
- }
- last = tp;
- }
- if (last == NULL) {
- m_freem(m0);
- return (ENOENT);
- }
- ret = if_enqueue(last->tp_if, m0);
- if (ret != 0)
- errors++;
- if (errors == active_ports)
- return (ret);
- return (0);
- }
- int
- trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
- {
- return (0);
- }
- /*
- * 802.3ad LACP
- */
- int
- trunk_lacp_attach(struct trunk_softc *tr)
- {
- struct trunk_port *tp;
- int error;
- tr->tr_detach = trunk_lacp_detach;
- tr->tr_port_create = lacp_port_create;
- tr->tr_port_destroy = lacp_port_destroy;
- tr->tr_linkstate = lacp_linkstate;
- tr->tr_start = trunk_lacp_start;
- tr->tr_input = trunk_lacp_input;
- tr->tr_init = lacp_init;
- tr->tr_stop = lacp_stop;
- tr->tr_req = lacp_req;
- tr->tr_portreq = lacp_portreq;
- error = lacp_attach(tr);
- if (error)
- return (error);
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
- lacp_port_create(tp);
- return (error);
- }
- int
- trunk_lacp_detach(struct trunk_softc *tr)
- {
- struct trunk_port *tp;
- int error;
- SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
- lacp_port_destroy(tp);
- /* unlocking is safe here */
- error = lacp_detach(tr);
- return (error);
- }
- int
- trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
- {
- struct trunk_port *tp;
- tp = lacp_select_tx_port(tr, m);
- if (tp == NULL) {
- m_freem(m);
- return (EBUSY);
- }
- return (if_enqueue(tp->tp_if, m));
- }
- int
- trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
- {
- return (lacp_input(tp, m));
- }
|