LCOV - code coverage report
Current view: top level - net - if_trunk.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 788 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: if_trunk.c,v 1.137 2018/08/12 23:50:31 ccardenas Exp $        */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
       5             :  *
       6             :  * Permission to use, copy, modify, and distribute this software for any
       7             :  * purpose with or without fee is hereby granted, provided that the above
       8             :  * copyright notice and this permission notice appear in all copies.
       9             :  *
      10             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      11             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      12             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      13             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      14             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      15             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      16             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      17             :  */
      18             : 
      19             : #include <sys/param.h>
      20             : #include <sys/kernel.h>
      21             : #include <sys/malloc.h>
      22             : #include <sys/mbuf.h>
      23             : #include <sys/queue.h>
      24             : #include <sys/socket.h>
      25             : #include <sys/sockio.h>
      26             : #include <sys/systm.h>
      27             : #include <sys/task.h>
      28             : #include <sys/timeout.h>
      29             : 
      30             : #include <crypto/siphash.h>
      31             : 
      32             : #include <net/if.h>
      33             : #include <net/if_dl.h>
      34             : #include <net/if_media.h>
      35             : #include <net/if_types.h>
      36             : 
      37             : #include <netinet/in.h>
      38             : #include <netinet/if_ether.h>
      39             : #include <netinet/ip.h>
      40             : 
      41             : #ifdef INET6
      42             : #include <netinet/ip6.h>
      43             : #endif
      44             : 
      45             : #include <net/if_vlan_var.h>
      46             : #include <net/if_trunk.h>
      47             : #include <net/trunklacp.h>
      48             : 
      49             : #include "bpfilter.h"
      50             : #if NBPFILTER > 0
      51             : #include <net/bpf.h>
      52             : #endif
      53             : 
      54             : SLIST_HEAD(__trhead, trunk_softc) trunk_list;   /* list of trunks */
      55             : 
      56             : void     trunkattach(int);
      57             : int      trunk_clone_create(struct if_clone *, int);
      58             : int      trunk_clone_destroy(struct ifnet *);
      59             : void     trunk_lladdr(struct arpcom *, u_int8_t *);
      60             : int      trunk_capabilities(struct trunk_softc *);
      61             : void     trunk_port_lladdr(struct trunk_port *, u_int8_t *);
      62             : int      trunk_port_create(struct trunk_softc *, struct ifnet *);
      63             : int      trunk_port_destroy(struct trunk_port *);
      64             : void     trunk_port_state(void *);
      65             : void     trunk_port_ifdetach(void *);
      66             : int      trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
      67             : int      trunk_port_output(struct ifnet *, struct mbuf *, struct sockaddr *,
      68             :             struct rtentry *);
      69             : struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
      70             : int      trunk_port_checkstacking(struct trunk_softc *);
      71             : void     trunk_port2req(struct trunk_port *, struct trunk_reqport *);
      72             : int      trunk_ioctl(struct ifnet *, u_long, caddr_t);
      73             : int      trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
      74             : int      trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
      75             : void     trunk_ether_purgemulti(struct trunk_softc *);
      76             : int      trunk_ether_cmdmulti(struct trunk_port *, u_long);
      77             : int      trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
      78             : int      trunk_input(struct ifnet *, struct mbuf *, void *);
      79             : void     trunk_start(struct ifnet *);
      80             : void     trunk_init(struct ifnet *);
      81             : void     trunk_stop(struct ifnet *);
      82             : int      trunk_media_change(struct ifnet *);
      83             : void     trunk_media_status(struct ifnet *, struct ifmediareq *);
      84             : struct trunk_port *trunk_link_active(struct trunk_softc *,
      85             :             struct trunk_port *);
      86             : const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *);
      87             : 
      88             : struct if_clone trunk_cloner =
      89             :     IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
      90             : 
      91             : /* Simple round robin */
      92             : int      trunk_rr_attach(struct trunk_softc *);
      93             : int      trunk_rr_detach(struct trunk_softc *);
      94             : void     trunk_rr_port_destroy(struct trunk_port *);
      95             : int      trunk_rr_start(struct trunk_softc *, struct mbuf *);
      96             : int      trunk_rr_input(struct trunk_softc *, struct trunk_port *,
      97             :             struct mbuf *);
      98             : 
      99             : /* Active failover */
     100             : int      trunk_fail_attach(struct trunk_softc *);
     101             : int      trunk_fail_detach(struct trunk_softc *);
     102             : int      trunk_fail_port_create(struct trunk_port *);
     103             : void     trunk_fail_port_destroy(struct trunk_port *);
     104             : int      trunk_fail_start(struct trunk_softc *, struct mbuf *);
     105             : int      trunk_fail_input(struct trunk_softc *, struct trunk_port *,
     106             :             struct mbuf *);
     107             : void     trunk_fail_linkstate(struct trunk_port *);
     108             : 
     109             : /* Loadbalancing */
     110             : int      trunk_lb_attach(struct trunk_softc *);
     111             : int      trunk_lb_detach(struct trunk_softc *);
     112             : int      trunk_lb_port_create(struct trunk_port *);
     113             : void     trunk_lb_port_destroy(struct trunk_port *);
     114             : int      trunk_lb_start(struct trunk_softc *, struct mbuf *);
     115             : int      trunk_lb_input(struct trunk_softc *, struct trunk_port *,
     116             :             struct mbuf *);
     117             : int      trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
     118             : 
     119             : /* Broadcast mode */
     120             : int      trunk_bcast_attach(struct trunk_softc *);
     121             : int      trunk_bcast_detach(struct trunk_softc *);
     122             : int      trunk_bcast_start(struct trunk_softc *, struct mbuf *);
     123             : int      trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
     124             :             struct mbuf *);
     125             : 
     126             : /* 802.3ad LACP */
     127             : int      trunk_lacp_attach(struct trunk_softc *);
     128             : int      trunk_lacp_detach(struct trunk_softc *);
     129             : int      trunk_lacp_start(struct trunk_softc *, struct mbuf *);
     130             : int      trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
     131             :             struct mbuf *);
     132             : 
     133             : /* Trunk protocol table */
     134             : static const struct {
     135             :         enum trunk_proto        ti_proto;
     136             :         int                     (*ti_attach)(struct trunk_softc *);
     137             : } trunk_protos[] = {
     138             :         { TRUNK_PROTO_ROUNDROBIN,       trunk_rr_attach },
     139             :         { TRUNK_PROTO_FAILOVER,         trunk_fail_attach },
     140             :         { TRUNK_PROTO_LOADBALANCE,      trunk_lb_attach },
     141             :         { TRUNK_PROTO_BROADCAST,        trunk_bcast_attach },
     142             :         { TRUNK_PROTO_LACP,             trunk_lacp_attach },
     143             :         { TRUNK_PROTO_NONE,             NULL }
     144             : };
     145             : 
     146             : void
     147           0 : trunkattach(int count)
     148             : {
     149           0 :         SLIST_INIT(&trunk_list);
     150           0 :         if_clone_attach(&trunk_cloner);
     151           0 : }
     152             : 
     153             : int
     154           0 : trunk_clone_create(struct if_clone *ifc, int unit)
     155             : {
     156             :         struct trunk_softc *tr;
     157             :         struct ifnet *ifp;
     158             :         int i, error = 0;
     159             : 
     160           0 :         tr = malloc(sizeof(*tr), M_DEVBUF, M_WAITOK|M_ZERO);
     161           0 :         tr->tr_unit = unit;
     162           0 :         tr->tr_proto = TRUNK_PROTO_NONE;
     163           0 :         for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
     164           0 :                 if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
     165           0 :                         tr->tr_proto = trunk_protos[i].ti_proto;
     166           0 :                         if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
     167           0 :                                 free(tr, M_DEVBUF, sizeof *tr);
     168           0 :                                 return (error);
     169             :                         }
     170             :                         break;
     171             :                 }
     172             :         }
     173           0 :         SLIST_INIT(&tr->tr_ports);
     174             : 
     175             :         /* Initialise pseudo media types */
     176           0 :         ifmedia_init(&tr->tr_media, 0, trunk_media_change,
     177             :             trunk_media_status);
     178           0 :         ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
     179           0 :         ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
     180             : 
     181           0 :         ifp = &tr->tr_ac.ac_if;
     182           0 :         ifp->if_softc = tr;
     183           0 :         ifp->if_start = trunk_start;
     184           0 :         ifp->if_ioctl = trunk_ioctl;
     185           0 :         ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
     186           0 :         ifp->if_capabilities = trunk_capabilities(tr);
     187             : 
     188           0 :         IFQ_SET_MAXLEN(&ifp->if_snd, 1);
     189             : 
     190           0 :         snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
     191           0 :             ifc->ifc_name, unit);
     192             : 
     193             :         /*
     194             :          * Attach as an ordinary ethernet device, children will be attached
     195             :          * as special device IFT_IEEE8023ADLAG.
     196             :          */
     197           0 :         if_attach(ifp);
     198           0 :         ether_ifattach(ifp);
     199             : 
     200             :         /* Insert into the global list of trunks */
     201           0 :         SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
     202             : 
     203           0 :         return (0);
     204           0 : }
     205             : 
     206             : int
     207           0 : trunk_clone_destroy(struct ifnet *ifp)
     208             : {
     209           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
     210             :         struct trunk_port *tp;
     211             :         int error;
     212             : 
     213             :         /* Remove any multicast groups that we may have joined. */
     214           0 :         trunk_ether_purgemulti(tr);
     215             : 
     216             :         /* Shutdown and remove trunk ports, return on error */
     217           0 :         NET_LOCK();
     218           0 :         while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
     219           0 :                 if ((error = trunk_port_destroy(tp)) != 0) {
     220           0 :                         NET_UNLOCK();
     221           0 :                         return (error);
     222             :                 }
     223             :         }
     224           0 :         NET_UNLOCK();
     225             : 
     226           0 :         ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
     227           0 :         ether_ifdetach(ifp);
     228           0 :         if_detach(ifp);
     229             : 
     230           0 :         SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
     231           0 :         free(tr, M_DEVBUF, sizeof *tr);
     232             : 
     233           0 :         return (0);
     234           0 : }
     235             : 
     236             : void
     237           0 : trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
     238             : {
     239           0 :         struct ifnet *ifp = &ac->ac_if;
     240             :         struct sockaddr_dl *sdl;
     241             : 
     242           0 :         sdl = ifp->if_sadl;
     243           0 :         sdl->sdl_type = IFT_ETHER;
     244           0 :         sdl->sdl_alen = ETHER_ADDR_LEN;
     245           0 :         bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
     246           0 :         bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
     247           0 : }
     248             : 
     249             : int
     250           0 : trunk_capabilities(struct trunk_softc *tr)
     251             : {
     252             :         struct trunk_port *tp;
     253             :         int cap = ~0, priv;
     254             : 
     255             :         /* Preserve private capabilities */
     256           0 :         priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
     257             : 
     258             :         /* Get capabilities from the trunk ports */
     259           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
     260           0 :                 cap &= tp->tp_capabilities;
     261             : 
     262           0 :         if (tr->tr_ifflags & IFF_DEBUG) {
     263           0 :                 printf("%s: capabilities 0x%08x\n",
     264           0 :                     tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
     265           0 :         }
     266             : 
     267           0 :         return (cap == ~0 ? priv : (cap | priv));
     268             : }
     269             : 
     270             : void
     271           0 : trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
     272             : {
     273           0 :         struct ifnet *ifp = tp->tp_if;
     274             : 
     275             :         /* Set the link layer address */
     276           0 :         trunk_lladdr((struct arpcom *)ifp, lladdr);
     277             : 
     278             :         /* Reset the port to update the lladdr */
     279           0 :         ifnewlladdr(ifp);
     280           0 : }
     281             : 
     282             : int
     283           0 : trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
     284             : {
     285             :         struct trunk_softc *tr_ptr;
     286             :         struct trunk_port *tp;
     287             :         int error = 0;
     288             : 
     289             :         /* Limit the maximal number of trunk ports */
     290           0 :         if (tr->tr_count >= TRUNK_MAX_PORTS)
     291           0 :                 return (ENOSPC);
     292             : 
     293             :         /* Check if port has already been associated to a trunk */
     294           0 :         if (trunk_port_get(NULL, ifp) != NULL)
     295           0 :                 return (EBUSY);
     296             : 
     297             :         /* XXX Disallow non-ethernet interfaces (this should be any of 802) */
     298           0 :         if (ifp->if_type != IFT_ETHER)
     299           0 :                 return (EPROTONOSUPPORT);
     300             : 
     301             :         /* Take MTU from the first member port */
     302           0 :         if (SLIST_EMPTY(&tr->tr_ports)) {
     303           0 :                 if (tr->tr_ifflags & IFF_DEBUG)
     304           0 :                         printf("%s: first port, setting trunk mtu %u\n",
     305           0 :                             tr->tr_ifname, ifp->if_mtu);
     306           0 :                 tr->tr_ac.ac_if.if_mtu = ifp->if_mtu;
     307           0 :                 tr->tr_ac.ac_if.if_hardmtu = ifp->if_mtu;
     308           0 :         } else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) {
     309           0 :                 printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname,
     310           0 :                     ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu);
     311           0 :                 return (EINVAL);
     312             :         }
     313             : 
     314           0 :         if ((error = ifpromisc(ifp, 1)) != 0)
     315           0 :                 return (error);
     316             : 
     317           0 :         if ((tp = malloc(sizeof *tp, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
     318           0 :                 return (ENOMEM);
     319             : 
     320             :         /* Check if port is a stacked trunk */
     321           0 :         SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
     322           0 :                 if (ifp == &tr_ptr->tr_ac.ac_if) {
     323           0 :                         tp->tp_flags |= TRUNK_PORT_STACK;
     324           0 :                         if (trunk_port_checkstacking(tr_ptr) >=
     325             :                             TRUNK_MAX_STACKING) {
     326           0 :                                 free(tp, M_DEVBUF, sizeof *tp);
     327           0 :                                 return (E2BIG);
     328             :                         }
     329             :                 }
     330             :         }
     331             : 
     332             :         /* Change the interface type */
     333           0 :         tp->tp_iftype = ifp->if_type;
     334           0 :         ifp->if_type = IFT_IEEE8023ADLAG;
     335             : 
     336           0 :         tp->tp_ioctl = ifp->if_ioctl;
     337           0 :         ifp->if_ioctl = trunk_port_ioctl;
     338             : 
     339           0 :         tp->tp_output = ifp->if_output;
     340           0 :         ifp->if_output = trunk_port_output;
     341             : 
     342           0 :         tp->tp_if = ifp;
     343           0 :         tp->tp_trunk = tr;
     344             : 
     345             :         /* Save port link layer address */
     346           0 :         bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
     347             : 
     348           0 :         if (SLIST_EMPTY(&tr->tr_ports)) {
     349           0 :                 tr->tr_primary = tp;
     350           0 :                 tp->tp_flags |= TRUNK_PORT_MASTER;
     351           0 :                 trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
     352           0 :         }
     353             : 
     354             :         /* Insert into the list of ports */
     355           0 :         SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
     356           0 :         tr->tr_count++;
     357             : 
     358             :         /* Update link layer address for this port */
     359           0 :         trunk_port_lladdr(tp,
     360           0 :             ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr);
     361             : 
     362             :         /* Update trunk capabilities */
     363           0 :         tr->tr_capabilities = trunk_capabilities(tr);
     364             : 
     365             :         /* Add multicast addresses to this port */
     366           0 :         trunk_ether_cmdmulti(tp, SIOCADDMULTI);
     367             : 
     368             :         /* Register callback for physical link state changes */
     369           0 :         tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
     370             :             trunk_port_state, tp);
     371             : 
     372             :         /* Register callback if parent wants to unregister */
     373           0 :         tp->dh_cookie = hook_establish(ifp->if_detachhooks, 0,
     374             :             trunk_port_ifdetach, tp);
     375             : 
     376           0 :         if (tr->tr_port_create != NULL)
     377           0 :                 error = (*tr->tr_port_create)(tp);
     378             : 
     379             :         /* Change input handler of the physical interface. */
     380           0 :         if_ih_insert(ifp, trunk_input, tp);
     381             : 
     382           0 :         return (error);
     383           0 : }
     384             : 
     385             : int
     386           0 : trunk_port_checkstacking(struct trunk_softc *tr)
     387             : {
     388             :         struct trunk_softc *tr_ptr;
     389             :         struct trunk_port *tp;
     390             :         int m = 0;
     391             : 
     392           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
     393           0 :                 if (tp->tp_flags & TRUNK_PORT_STACK) {
     394           0 :                         tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
     395           0 :                         m = MAX(m, trunk_port_checkstacking(tr_ptr));
     396           0 :                 }
     397             :         }
     398             : 
     399           0 :         return (m + 1);
     400             : }
     401             : 
     402             : int
     403           0 : trunk_port_destroy(struct trunk_port *tp)
     404             : {
     405           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
     406             :         struct trunk_port *tp_ptr;
     407           0 :         struct ifnet *ifp = tp->tp_if;
     408             : 
     409             :         /* Restore previous input handler. */
     410           0 :         if_ih_remove(ifp, trunk_input, tp);
     411             : 
     412             :         /* Remove multicast addresses from this port */
     413           0 :         trunk_ether_cmdmulti(tp, SIOCDELMULTI);
     414             : 
     415             :         /* Port has to be down */
     416           0 :         if (ifp->if_flags & IFF_UP)
     417           0 :                 if_down(ifp);
     418             : 
     419           0 :         ifpromisc(ifp, 0);
     420             : 
     421           0 :         if (tr->tr_port_destroy != NULL)
     422           0 :                 (*tr->tr_port_destroy)(tp);
     423             : 
     424             :         /* Restore interface type. */
     425           0 :         ifp->if_type = tp->tp_iftype;
     426             : 
     427           0 :         ifp->if_ioctl = tp->tp_ioctl;
     428           0 :         ifp->if_output = tp->tp_output;
     429             : 
     430           0 :         hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie);
     431           0 :         hook_disestablish(ifp->if_detachhooks, tp->dh_cookie);
     432             : 
     433             :         /* Finally, remove the port from the trunk */
     434           0 :         SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
     435           0 :         tr->tr_count--;
     436             : 
     437             :         /* Update the primary interface */
     438           0 :         if (tp == tr->tr_primary) {
     439           0 :                 u_int8_t lladdr[ETHER_ADDR_LEN];
     440             : 
     441           0 :                 if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
     442           0 :                         bzero(&lladdr, ETHER_ADDR_LEN);
     443           0 :                 } else {
     444           0 :                         bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
     445           0 :                             lladdr, ETHER_ADDR_LEN);
     446           0 :                         tp_ptr->tp_flags = TRUNK_PORT_MASTER;
     447             :                 }
     448           0 :                 trunk_lladdr(&tr->tr_ac, lladdr);
     449           0 :                 tr->tr_primary = tp_ptr;
     450             : 
     451             :                 /* Update link layer address for each port */
     452           0 :                 SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
     453           0 :                         trunk_port_lladdr(tp_ptr, lladdr);
     454           0 :         }
     455             : 
     456             :         /* Reset the port lladdr */
     457           0 :         trunk_port_lladdr(tp, tp->tp_lladdr);
     458             : 
     459           0 :         free(tp, M_DEVBUF, sizeof *tp);
     460             : 
     461             :         /* Update trunk capabilities */
     462           0 :         tr->tr_capabilities = trunk_capabilities(tr);
     463             : 
     464           0 :         return (0);
     465             : }
     466             : 
     467             : int
     468           0 : trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
     469             : {
     470           0 :         struct trunk_reqport *rp = (struct trunk_reqport *)data;
     471             :         struct trunk_softc *tr;
     472             :         struct trunk_port *tp = NULL;
     473             :         int error = 0;
     474             : 
     475             :         /* Should be checked by the caller */
     476           0 :         if (ifp->if_type != IFT_IEEE8023ADLAG ||
     477           0 :             (tp = trunk_port_get(NULL, ifp)) == NULL ||
     478           0 :             (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
     479             :                 error = EINVAL;
     480           0 :                 goto fallback;
     481             :         }
     482             : 
     483           0 :         switch (cmd) {
     484             :         case SIOCGTRUNKPORT:
     485           0 :                 if (rp->rp_portname[0] == '\0' ||
     486           0 :                     ifunit(rp->rp_portname) != ifp) {
     487             :                         error = EINVAL;
     488           0 :                         break;
     489             :                 }
     490             : 
     491             :                 /* Search in all trunks if the global flag is set */
     492           0 :                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
     493           0 :                     NULL : tr, ifp)) == NULL) {
     494             :                         error = ENOENT;
     495           0 :                         break;
     496             :                 }
     497             : 
     498           0 :                 trunk_port2req(tp, rp);
     499           0 :                 break;
     500             :         case SIOCSIFMTU:
     501             :                 /* Do not allow the MTU to be changed once joined */
     502             :                 error = EINVAL;
     503           0 :                 break;
     504             :         default:
     505             :                 error = ENOTTY;
     506           0 :                 goto fallback;
     507             :         }
     508             : 
     509           0 :         return (error);
     510             : 
     511             :  fallback:
     512           0 :         if (tp != NULL)
     513           0 :                 error = (*tp->tp_ioctl)(ifp, cmd, data);
     514             : 
     515           0 :         return (error);
     516           0 : }
     517             : 
     518             : int
     519           0 : trunk_port_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
     520             :     struct rtentry *rt)
     521             : {
     522             :         /* restrict transmission on trunk members to bpf only */
     523           0 :         if (ifp->if_type == IFT_IEEE8023ADLAG &&
     524           0 :             (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
     525           0 :                 m_freem(m);
     526           0 :                 return (EBUSY);
     527             :         }
     528             : 
     529           0 :         return (ether_output(ifp, m, dst, rt));
     530           0 : }
     531             : 
     532             : void
     533           0 : trunk_port_ifdetach(void *arg)
     534             : {
     535           0 :         struct trunk_port *tp = (struct trunk_port *)arg;
     536             : 
     537           0 :         trunk_port_destroy(tp);
     538           0 : }
     539             : 
     540             : struct trunk_port *
     541           0 : trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
     542             : {
     543             :         struct trunk_port *tp;
     544             :         struct trunk_softc *tr_ptr;
     545             : 
     546           0 :         if (tr != NULL) {
     547             :                 /* Search port in specified trunk */
     548           0 :                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
     549           0 :                         if (tp->tp_if == ifp)
     550           0 :                                 return (tp);
     551             :                 }
     552             :         } else {
     553             :                 /* Search all trunks for the selected port */
     554           0 :                 SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
     555           0 :                         SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
     556           0 :                                 if (tp->tp_if == ifp)
     557           0 :                                         return (tp);
     558             :                         }
     559             :                 }
     560             :         }
     561             : 
     562           0 :         return (NULL);
     563           0 : }
     564             : 
     565             : void
     566           0 : trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
     567             : {
     568           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
     569             : 
     570           0 :         strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
     571           0 :         strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
     572           0 :         rp->rp_prio = tp->tp_prio;
     573           0 :         if (tr->tr_portreq != NULL)
     574           0 :                 (*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc);
     575             : 
     576             :         /* Add protocol specific flags */
     577           0 :         switch (tr->tr_proto) {
     578             :         case TRUNK_PROTO_FAILOVER:
     579           0 :                 rp->rp_flags = tp->tp_flags;
     580           0 :                 if (tp == trunk_link_active(tr, tr->tr_primary))
     581           0 :                         rp->rp_flags |= TRUNK_PORT_ACTIVE;
     582             :                 break;
     583             : 
     584             :         case TRUNK_PROTO_ROUNDROBIN:
     585             :         case TRUNK_PROTO_LOADBALANCE:
     586             :         case TRUNK_PROTO_BROADCAST:
     587           0 :                 rp->rp_flags = tp->tp_flags;
     588           0 :                 if (TRUNK_PORTACTIVE(tp))
     589           0 :                         rp->rp_flags |= TRUNK_PORT_ACTIVE;
     590             :                 break;
     591             : 
     592             :         case TRUNK_PROTO_LACP:
     593             :                 /* LACP has a different definition of active */
     594           0 :                 rp->rp_flags = lacp_port_status(tp);
     595           0 :                 break;
     596             :         default:
     597             :                 break;
     598             :         }
     599           0 : }
     600             : 
     601             : int
     602           0 : trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
     603             : {
     604           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
     605           0 :         struct trunk_reqall *ra = (struct trunk_reqall *)data;
     606           0 :         struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
     607           0 :         struct trunk_opts *tro = (struct trunk_opts *)data;
     608           0 :         struct ifreq *ifr = (struct ifreq *)data;
     609             :         struct lacp_softc *lsc;
     610             :         struct trunk_port *tp;
     611             :         struct lacp_port *lp;
     612             :         struct ifnet *tpif;
     613             :         int i, error = 0;
     614             : 
     615           0 :         bzero(&rpbuf, sizeof(rpbuf));
     616             : 
     617           0 :         switch (cmd) {
     618             :         case SIOCGTRUNK:
     619           0 :                 ra->ra_proto = tr->tr_proto;
     620           0 :                 if (tr->tr_req != NULL)
     621           0 :                         (*tr->tr_req)(tr, (caddr_t)&ra->ra_psc);
     622           0 :                 ra->ra_ports = i = 0;
     623           0 :                 tp = SLIST_FIRST(&tr->tr_ports);
     624           0 :                 while (tp && ra->ra_size >=
     625           0 :                     i + sizeof(struct trunk_reqport)) {
     626           0 :                         trunk_port2req(tp, &rpbuf);
     627           0 :                         error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
     628             :                             sizeof(struct trunk_reqport));
     629           0 :                         if (error)
     630             :                                 break;
     631           0 :                         i += sizeof(struct trunk_reqport);
     632           0 :                         ra->ra_ports++;
     633           0 :                         tp = SLIST_NEXT(tp, tp_entries);
     634             :                 }
     635             :                 break;
     636             :         case SIOCSTRUNK:
     637           0 :                 if ((error = suser(curproc)) != 0) {
     638             :                         error = EPERM;
     639           0 :                         break;
     640             :                 }
     641           0 :                 if (ra->ra_proto >= TRUNK_PROTO_MAX) {
     642             :                         error = EPROTONOSUPPORT;
     643           0 :                         break;
     644             :                 }
     645             :                 /*
     646             :                  * Serialize modifications to the trunk and trunk
     647             :                  * ports via the ifih SRP: detaching trunk_input
     648             :                  * from the trunk port will require all currently
     649             :                  * running trunk_input's on this port to finish
     650             :                  * granting us an exclusive access to it.
     651             :                  */
     652           0 :                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
     653           0 :                         if_ih_remove(tp->tp_if, trunk_input, tp);
     654           0 :                 if (tr->tr_proto != TRUNK_PROTO_NONE)
     655           0 :                         error = tr->tr_detach(tr);
     656           0 :                 if (error != 0)
     657             :                         break;
     658           0 :                 for (i = 0; i < nitems(trunk_protos); i++) {
     659           0 :                         if (trunk_protos[i].ti_proto == ra->ra_proto) {
     660           0 :                                 if (tr->tr_ifflags & IFF_DEBUG)
     661           0 :                                         printf("%s: using proto %u\n",
     662           0 :                                             tr->tr_ifname,
     663             :                                             trunk_protos[i].ti_proto);
     664           0 :                                 tr->tr_proto = trunk_protos[i].ti_proto;
     665           0 :                                 if (tr->tr_proto != TRUNK_PROTO_NONE)
     666           0 :                                         error = trunk_protos[i].ti_attach(tr);
     667           0 :                                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
     668           0 :                                         if_ih_insert(tp->tp_if,
     669           0 :                                             trunk_input, tp);
     670             :                                 /* Update trunk capabilities */
     671           0 :                                 tr->tr_capabilities = trunk_capabilities(tr);
     672           0 :                                 goto out;
     673             :                         }
     674             :                 }
     675             :                 error = EPROTONOSUPPORT;
     676           0 :                 break;
     677             :         case SIOCGTRUNKOPTS:
     678             :                 /* Only LACP trunks have options atm */
     679           0 :                 if (tro->to_proto != TRUNK_PROTO_LACP) {
     680             :                         error = EPROTONOSUPPORT;
     681           0 :                         break;
     682             :                 }
     683           0 :                 lsc = LACP_SOFTC(tr);
     684           0 :                 tro->to_lacpopts.lacp_mode = lsc->lsc_mode;
     685           0 :                 tro->to_lacpopts.lacp_timeout = lsc->lsc_timeout;
     686           0 :                 tro->to_lacpopts.lacp_prio = lsc->lsc_sys_prio;
     687           0 :                 tro->to_lacpopts.lacp_portprio = lsc->lsc_port_prio;
     688           0 :                 tro->to_lacpopts.lacp_ifqprio = lsc->lsc_ifq_prio;
     689           0 :                 break;
     690             :         case SIOCSTRUNKOPTS:
     691           0 :                 if ((error = suser(curproc)) != 0) {
     692             :                         error = EPERM;
     693           0 :                         break;
     694             :                 }
     695             :                 /* Only LACP trunks have options atm */
     696           0 :                 if (tro->to_proto != TRUNK_PROTO_LACP) {
     697             :                         error = EPROTONOSUPPORT;
     698           0 :                         break;
     699             :                 }
     700           0 :                 lsc = LACP_SOFTC(tr);
     701           0 :                 switch(tro->to_opts) {
     702             :                         case TRUNK_OPT_LACP_MODE:
     703             :                                 /*
     704             :                                  * Ensure mode changes occur immediately
     705             :                                  * on all ports
     706             :                                  */
     707           0 :                                 lsc->lsc_mode = tro->to_lacpopts.lacp_mode;
     708           0 :                                 if (lsc->lsc_mode == 0) {
     709           0 :                                         LIST_FOREACH(lp, &lsc->lsc_ports,
     710             :                                             lp_next)
     711           0 :                                                 lp->lp_state &=
     712             :                                                     ~LACP_STATE_ACTIVITY;
     713             :                                 } else {
     714           0 :                                         LIST_FOREACH(lp, &lsc->lsc_ports,
     715             :                                             lp_next)
     716           0 :                                                 lp->lp_state |=
     717             :                                                     LACP_STATE_ACTIVITY;
     718             :                                 }
     719             :                                 break;
     720             :                         case TRUNK_OPT_LACP_TIMEOUT:
     721             :                                 /*
     722             :                                  * Ensure timeout changes occur immediately
     723             :                                  * on all ports
     724             :                                  */
     725           0 :                                 lsc->lsc_timeout =
     726           0 :                                     tro->to_lacpopts.lacp_timeout;
     727           0 :                                 if (lsc->lsc_timeout == 0) {
     728           0 :                                         LIST_FOREACH(lp, &lsc->lsc_ports,
     729             :                                             lp_next)
     730           0 :                                                 lp->lp_state &=
     731             :                                                     ~LACP_STATE_TIMEOUT;
     732             :                                 } else {
     733           0 :                                         LIST_FOREACH(lp, &lsc->lsc_ports,
     734             :                                             lp_next)
     735           0 :                                                 lp->lp_state |=
     736             :                                                     LACP_STATE_TIMEOUT;
     737             :                                 }
     738             :                                 break;
     739             :                         case TRUNK_OPT_LACP_SYS_PRIO:
     740           0 :                                 if (tro->to_lacpopts.lacp_prio == 0) {
     741             :                                         error = EINVAL; 
     742           0 :                                         break;
     743             :                                 }
     744           0 :                                 lsc->lsc_sys_prio = tro->to_lacpopts.lacp_prio;
     745           0 :                                 break;
     746             :                         case TRUNK_OPT_LACP_PORT_PRIO:
     747           0 :                                 if (tro->to_lacpopts.lacp_portprio == 0) {
     748             :                                         error = EINVAL; 
     749           0 :                                         break;
     750             :                                 }
     751           0 :                                 lsc->lsc_port_prio =
     752             :                                     tro->to_lacpopts.lacp_portprio;
     753           0 :                                 break;
     754             :                         case TRUNK_OPT_LACP_IFQ_PRIO:
     755           0 :                                 if (tro->to_lacpopts.lacp_ifqprio >
     756             :                                     IFQ_MAXPRIO) {
     757             :                                         error = EINVAL; 
     758           0 :                                         break;
     759             :                                 }
     760           0 :                                 lsc->lsc_ifq_prio =
     761             :                                     tro->to_lacpopts.lacp_ifqprio;
     762           0 :                                 break;
     763             :                 }
     764             :                 break;
     765             :         case SIOCGTRUNKPORT:
     766           0 :                 if (rp->rp_portname[0] == '\0' ||
     767           0 :                     (tpif = ifunit(rp->rp_portname)) == NULL) {
     768             :                         error = EINVAL;
     769           0 :                         break;
     770             :                 }
     771             : 
     772             :                 /* Search in all trunks if the global flag is set */
     773           0 :                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
     774           0 :                     NULL : tr, tpif)) == NULL) {
     775             :                         error = ENOENT;
     776           0 :                         break;
     777             :                 }
     778             : 
     779           0 :                 trunk_port2req(tp, rp);
     780           0 :                 break;
     781             :         case SIOCSTRUNKPORT:
     782           0 :                 if ((error = suser(curproc)) != 0) {
     783             :                         error = EPERM;
     784           0 :                         break;
     785             :                 }
     786           0 :                 if (rp->rp_portname[0] == '\0' ||
     787           0 :                     (tpif = ifunit(rp->rp_portname)) == NULL) {
     788             :                         error = EINVAL;
     789           0 :                         break;
     790             :                 }
     791           0 :                 error = trunk_port_create(tr, tpif);
     792           0 :                 break;
     793             :         case SIOCSTRUNKDELPORT:
     794           0 :                 if ((error = suser(curproc)) != 0) {
     795             :                         error = EPERM;
     796           0 :                         break;
     797             :                 }
     798           0 :                 if (rp->rp_portname[0] == '\0' ||
     799           0 :                     (tpif = ifunit(rp->rp_portname)) == NULL) {
     800             :                         error = EINVAL;
     801           0 :                         break;
     802             :                 }
     803             : 
     804             :                 /* Search in all trunks if the global flag is set */
     805           0 :                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
     806           0 :                     NULL : tr, tpif)) == NULL) {
     807             :                         error = ENOENT;
     808           0 :                         break;
     809             :                 }
     810             : 
     811           0 :                 error = trunk_port_destroy(tp);
     812           0 :                 break;
     813             :         case SIOCSIFADDR:
     814           0 :                 ifp->if_flags |= IFF_UP;
     815             :                 /* FALLTHROUGH */
     816             :         case SIOCSIFFLAGS:
     817             :                 error = ENETRESET;
     818           0 :                 break;
     819             :         case SIOCADDMULTI:
     820           0 :                 error = trunk_ether_addmulti(tr, ifr);
     821           0 :                 break;
     822             :         case SIOCDELMULTI:
     823           0 :                 error = trunk_ether_delmulti(tr, ifr);
     824           0 :                 break;
     825             :         case SIOCSIFMEDIA:
     826             :         case SIOCGIFMEDIA:
     827           0 :                 error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
     828           0 :                 break;
     829             :         case SIOCSIFLLADDR:
     830             :                 /* Update the port lladdrs as well */
     831           0 :                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
     832           0 :                         trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
     833             :                 error = ENETRESET;
     834           0 :                 break;
     835             :         default:
     836           0 :                 error = ether_ioctl(ifp, &tr->tr_ac, cmd, data);
     837           0 :         }
     838             : 
     839           0 :         if (error == ENETRESET) {
     840           0 :                 if (ifp->if_flags & IFF_UP) {
     841           0 :                         if ((ifp->if_flags & IFF_RUNNING) == 0)
     842           0 :                                 trunk_init(ifp);
     843             :                 } else {
     844           0 :                         if (ifp->if_flags & IFF_RUNNING)
     845           0 :                                 trunk_stop(ifp);
     846             :                 }
     847             :                 error = 0;
     848           0 :         }
     849             : 
     850             :  out:
     851           0 :         return (error);
     852           0 : }
     853             : 
     854             : int
     855           0 : trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
     856             : {
     857             :         struct trunk_mc *mc;
     858           0 :         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
     859             :         int error;
     860             : 
     861             :         /* Ignore ENETRESET error code */
     862           0 :         if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
     863           0 :                 return (error);
     864             : 
     865           0 :         if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
     866             :                 error = ENOMEM;
     867             :                 goto failed;
     868             :         }
     869             : 
     870           0 :         ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
     871           0 :         ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
     872           0 :         bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
     873           0 :         SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
     874             : 
     875           0 :         if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
     876           0 :             (caddr_t)ifr)) != 0) {
     877           0 :                 trunk_ether_delmulti(tr, ifr);
     878           0 :                 return (error);
     879             :         }
     880             : 
     881           0 :         return (error);
     882             : 
     883             :  failed:
     884           0 :         ether_delmulti(ifr, &tr->tr_ac);
     885             : 
     886           0 :         return (error);
     887           0 : }
     888             : 
     889             : int
     890           0 : trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
     891             : {
     892             :         struct ether_multi *enm;
     893             :         struct trunk_mc *mc;
     894           0 :         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
     895             :         int error;
     896             : 
     897           0 :         if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
     898           0 :                 return (error);
     899           0 :         ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
     900           0 :         if (enm == NULL)
     901           0 :                 return (EINVAL);
     902             : 
     903           0 :         SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
     904           0 :                 if (mc->mc_enm == enm)
     905             :                         break;
     906             : 
     907             :         /* We won't delete entries we didn't add */
     908           0 :         if (mc == NULL)
     909           0 :                 return (EINVAL);
     910             : 
     911           0 :         if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
     912           0 :                 return (error);
     913             : 
     914             :         /* We no longer use this multicast address.  Tell parent so. */
     915           0 :         error = trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
     916           0 :         if (error == 0) {
     917           0 :                 SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
     918           0 :                 free(mc, M_DEVBUF, sizeof(*mc));
     919           0 :         } else {
     920             :                 /* XXX At least one port failed to remove the address */
     921           0 :                 if (tr->tr_ifflags & IFF_DEBUG) {
     922           0 :                         printf("%s: failed to remove multicast address "
     923           0 :                             "on all ports (%d)\n", tr->tr_ifname, error);
     924           0 :                 }
     925           0 :                 (void)ether_addmulti(ifr, &tr->tr_ac);
     926             :         }
     927             : 
     928           0 :         return (0);
     929           0 : }
     930             : 
     931             : void
     932           0 : trunk_ether_purgemulti(struct trunk_softc *tr)
     933             : {
     934             :         struct trunk_mc *mc;
     935           0 :         struct trunk_ifreq ifs;
     936           0 :         struct ifreq *ifr = &ifs.ifreq.ifreq;
     937             : 
     938           0 :         while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
     939           0 :                 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
     940             : 
     941             :                 /* Try to remove multicast address on all ports */
     942           0 :                 trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
     943             : 
     944           0 :                 SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
     945           0 :                 free(mc, M_DEVBUF, sizeof(*mc));
     946             :         }
     947           0 : }
     948             : 
     949             : int
     950           0 : trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
     951             : {
     952           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
     953             :         struct trunk_mc *mc;
     954           0 :         struct trunk_ifreq ifs;
     955           0 :         struct ifreq *ifr = &ifs.ifreq.ifreq;
     956             :         int ret, error = 0;
     957             : 
     958           0 :         bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
     959           0 :         SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
     960           0 :                 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
     961             : 
     962           0 :                 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
     963           0 :                         if (tr->tr_ifflags & IFF_DEBUG) {
     964           0 :                                 printf("%s: ioctl %lu failed on %s: %d\n",
     965           0 :                                     tr->tr_ifname, cmd, tp->tp_ifname, ret);
     966           0 :                         }
     967             :                         /* Store last known error and continue */
     968             :                         error = ret;
     969           0 :                 }
     970             :         }
     971             : 
     972           0 :         return (error);
     973           0 : }
     974             : 
     975             : int
     976           0 : trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
     977             : {
     978           0 :         struct ifreq *ifr = (struct ifreq *)data;
     979             :         struct trunk_port *tp;
     980             :         int ret, error = 0;
     981             : 
     982           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
     983           0 :                 bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
     984           0 :                 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
     985           0 :                         if (tr->tr_ifflags & IFF_DEBUG) {
     986           0 :                                 printf("%s: ioctl %lu failed on %s: %d\n",
     987           0 :                                     tr->tr_ifname, cmd, tp->tp_ifname, ret);
     988           0 :                         }
     989             :                         /* Store last known error and continue */
     990             :                         error = ret;
     991           0 :                 }
     992             :         }
     993             : 
     994           0 :         return (error);
     995             : }
     996             : 
     997             : void
     998           0 : trunk_start(struct ifnet *ifp)
     999             : {
    1000           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
    1001             :         struct mbuf *m;
    1002             :         int error;
    1003             : 
    1004           0 :         for (;;) {
    1005           0 :                 IFQ_DEQUEUE(&ifp->if_snd, m);
    1006           0 :                 if (m == NULL)
    1007             :                         break;
    1008             : 
    1009             : #if NBPFILTER > 0
    1010           0 :                 if (ifp->if_bpf)
    1011           0 :                         bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
    1012             : #endif
    1013             : 
    1014           0 :                 if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) {
    1015           0 :                         error = (*tr->tr_start)(tr, m);
    1016           0 :                         if (error != 0)
    1017           0 :                                 ifp->if_oerrors++;
    1018             :                 } else {
    1019           0 :                         m_freem(m);
    1020           0 :                         if (tr->tr_proto != TRUNK_PROTO_NONE)
    1021           0 :                                 ifp->if_oerrors++;
    1022             :                 }
    1023             :         }
    1024           0 : }
    1025             : 
    1026             : u_int32_t
    1027           0 : trunk_hashmbuf(struct mbuf *m, SIPHASH_KEY *key)
    1028             : {
    1029           0 :         u_int16_t etype, ether_vtag;
    1030             :         u_int32_t p = 0;
    1031           0 :         u_int16_t *vlan, vlanbuf[2];
    1032             :         int off;
    1033             :         struct ether_header *eh;
    1034           0 :         struct ip *ip, ipbuf;
    1035             : #ifdef INET6
    1036           0 :         u_int32_t flow;
    1037           0 :         struct ip6_hdr *ip6, ip6buf;
    1038             : #endif
    1039           0 :         SIPHASH_CTX ctx;
    1040             : 
    1041           0 :         if (m->m_pkthdr.ph_flowid & M_FLOWID_VALID)
    1042           0 :                 return (m->m_pkthdr.ph_flowid & M_FLOWID_MASK);
    1043             : 
    1044           0 :         SipHash24_Init(&ctx, key);
    1045             :         off = sizeof(*eh);
    1046           0 :         if (m->m_len < off)
    1047             :                 goto done;
    1048           0 :         eh = mtod(m, struct ether_header *);
    1049           0 :         etype = ntohs(eh->ether_type);
    1050           0 :         SipHash24_Update(&ctx, &eh->ether_shost, ETHER_ADDR_LEN);
    1051           0 :         SipHash24_Update(&ctx, &eh->ether_dhost, ETHER_ADDR_LEN);
    1052             : 
    1053             :         /* Special handling for encapsulating VLAN frames */
    1054           0 :         if (m->m_flags & M_VLANTAG) {
    1055           0 :                 ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
    1056           0 :                 SipHash24_Update(&ctx, &ether_vtag, sizeof(ether_vtag));
    1057           0 :         } else if (etype == ETHERTYPE_VLAN) {
    1058           0 :                 if ((vlan = (u_int16_t *)
    1059           0 :                     trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
    1060           0 :                         return (p);
    1061           0 :                 ether_vtag = EVL_VLANOFTAG(*vlan);
    1062           0 :                 SipHash24_Update(&ctx, &ether_vtag, sizeof(ether_vtag));
    1063           0 :                 etype = ntohs(vlan[1]);
    1064             :                 off += EVL_ENCAPLEN;
    1065           0 :         }
    1066             : 
    1067           0 :         switch (etype) {
    1068             :         case ETHERTYPE_IP:
    1069           0 :                 if ((ip = (struct ip *)
    1070           0 :                     trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
    1071           0 :                         return (p);
    1072           0 :                 SipHash24_Update(&ctx, &ip->ip_src, sizeof(struct in_addr));
    1073           0 :                 SipHash24_Update(&ctx, &ip->ip_dst, sizeof(struct in_addr));
    1074           0 :                 break;
    1075             : #ifdef INET6
    1076             :         case ETHERTYPE_IPV6:
    1077           0 :                 if ((ip6 = (struct ip6_hdr *)
    1078           0 :                     trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
    1079           0 :                         return (p);
    1080           0 :                 SipHash24_Update(&ctx, &ip6->ip6_src, sizeof(struct in6_addr));
    1081           0 :                 SipHash24_Update(&ctx, &ip6->ip6_dst, sizeof(struct in6_addr));
    1082           0 :                 flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
    1083           0 :                 SipHash24_Update(&ctx, &flow, sizeof(flow)); /* IPv6 flow label */
    1084           0 :                 break;
    1085             : #endif
    1086             :         }
    1087             : 
    1088             : done:
    1089           0 :         return SipHash24_End(&ctx);
    1090           0 : }
    1091             : 
    1092             : void
    1093           0 : trunk_init(struct ifnet *ifp)
    1094             : {
    1095           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
    1096             : 
    1097           0 :         ifp->if_flags |= IFF_RUNNING;
    1098             : 
    1099           0 :         if (tr->tr_init != NULL)
    1100           0 :                 (*tr->tr_init)(tr);
    1101           0 : }
    1102             : 
    1103             : void
    1104           0 : trunk_stop(struct ifnet *ifp)
    1105             : {
    1106           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
    1107             : 
    1108           0 :         ifp->if_flags &= ~IFF_RUNNING;
    1109             : 
    1110           0 :         if (tr->tr_stop != NULL)
    1111           0 :                 (*tr->tr_stop)(tr);
    1112           0 : }
    1113             : 
    1114             : int
    1115           0 : trunk_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
    1116             : {
    1117             :         struct trunk_softc *tr;
    1118             :         struct trunk_port *tp;
    1119             :         struct ifnet *trifp = NULL;
    1120             :         struct ether_header *eh;
    1121           0 :         struct mbuf_list ml = MBUF_LIST_INITIALIZER();
    1122             : 
    1123           0 :         eh = mtod(m, struct ether_header *);
    1124           0 :         if (ETHER_IS_MULTICAST(eh->ether_dhost))
    1125           0 :                 ifp->if_imcasts++;
    1126             : 
    1127             :         /* Should be checked by the caller */
    1128           0 :         if (ifp->if_type != IFT_IEEE8023ADLAG)
    1129             :                 goto bad;
    1130             : 
    1131           0 :         tp = (struct trunk_port *)cookie;
    1132           0 :         if ((tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
    1133             :                 goto bad;
    1134             : 
    1135           0 :         trifp = &tr->tr_ac.ac_if;
    1136           0 :         if (tr->tr_proto == TRUNK_PROTO_NONE)
    1137             :                 goto bad;
    1138             : 
    1139           0 :         if ((*tr->tr_input)(tr, tp, m)) {
    1140             :                 /*
    1141             :                  * We stop here if the packet has been consumed
    1142             :                  * by the protocol routine.
    1143             :                  */
    1144           0 :                 return (1);
    1145             :         }
    1146             : 
    1147           0 :         if ((trifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
    1148             :                 goto bad;
    1149             : 
    1150             :         /*
    1151             :          * Drop promiscuously received packets if we are not in
    1152             :          * promiscuous mode.
    1153             :          */
    1154           0 :         if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
    1155           0 :             (ifp->if_flags & IFF_PROMISC) &&
    1156           0 :             (trifp->if_flags & IFF_PROMISC) == 0) {
    1157           0 :                 if (bcmp(&tr->tr_ac.ac_enaddr, eh->ether_dhost,
    1158             :                     ETHER_ADDR_LEN)) {
    1159           0 :                         m_freem(m);
    1160           0 :                         return (1);
    1161             :                 }
    1162             :         }
    1163             : 
    1164             : 
    1165           0 :         ml_enqueue(&ml, m);
    1166           0 :         if_input(trifp, &ml);
    1167           0 :         return (1);
    1168             : 
    1169             :  bad:
    1170           0 :         if (trifp != NULL)
    1171           0 :                 trifp->if_ierrors++;
    1172           0 :         m_freem(m);
    1173           0 :         return (1);
    1174           0 : }
    1175             : 
    1176             : int
    1177           0 : trunk_media_change(struct ifnet *ifp)
    1178             : {
    1179           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
    1180             : 
    1181           0 :         if (tr->tr_ifflags & IFF_DEBUG)
    1182           0 :                 printf("%s\n", __func__);
    1183             : 
    1184             :         /* Ignore */
    1185           0 :         return (0);
    1186             : }
    1187             : 
    1188             : void
    1189           0 : trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
    1190             : {
    1191           0 :         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
    1192             :         struct trunk_port *tp;
    1193             : 
    1194           0 :         imr->ifm_status = IFM_AVALID;
    1195           0 :         imr->ifm_active = IFM_ETHER | IFM_AUTO;
    1196             : 
    1197           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
    1198           0 :                 if (TRUNK_PORTACTIVE(tp))
    1199           0 :                         imr->ifm_status |= IFM_ACTIVE;
    1200             :         }
    1201           0 : }
    1202             : 
    1203             : void
    1204           0 : trunk_port_state(void *arg)
    1205             : {
    1206           0 :         struct trunk_port *tp = (struct trunk_port *)arg;
    1207             :         struct trunk_softc *tr = NULL;
    1208             : 
    1209           0 :         if (tp != NULL)
    1210           0 :                 tr = (struct trunk_softc *)tp->tp_trunk;
    1211           0 :         if (tr == NULL)
    1212           0 :                 return;
    1213           0 :         if (tr->tr_linkstate != NULL)
    1214           0 :                 (*tr->tr_linkstate)(tp);
    1215           0 :         trunk_link_active(tr, tp);
    1216           0 : }
    1217             : 
    1218             : struct trunk_port *
    1219           0 : trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
    1220             : {
    1221             :         struct trunk_port *tp_next, *rval = NULL;
    1222             :         int new_link = LINK_STATE_DOWN;
    1223             : 
    1224             :         /*
    1225             :          * Search a port which reports an active link state.
    1226             :          */
    1227             : 
    1228           0 :         if (tp == NULL)
    1229             :                 goto search;
    1230           0 :         if (TRUNK_PORTACTIVE(tp)) {
    1231             :                 rval = tp;
    1232           0 :                 goto found;
    1233             :         }
    1234           0 :         if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
    1235           0 :             TRUNK_PORTACTIVE(tp_next)) {
    1236             :                 rval = tp_next;
    1237           0 :                 goto found;
    1238             :         }
    1239             : 
    1240             :  search:
    1241           0 :         SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
    1242           0 :                 if (TRUNK_PORTACTIVE(tp_next)) {
    1243             :                         rval = tp_next;
    1244           0 :                         goto found;
    1245             :                 }
    1246             :         }
    1247             : 
    1248             :  found:
    1249           0 :         if (rval != NULL) {
    1250             :                 /*
    1251             :                  * The IEEE 802.1D standard assumes that a trunk with
    1252             :                  * multiple ports is always full duplex. This is valid
    1253             :                  * for load sharing trunks and if at least two links
    1254             :                  * are active. Unfortunately, checking the latter would
    1255             :                  * be too expensive at this point.
    1256             :                  */
    1257           0 :                 if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
    1258           0 :                     (tr->tr_count > 1))
    1259           0 :                         new_link = LINK_STATE_FULL_DUPLEX;
    1260             :                 else
    1261           0 :                         new_link = rval->tp_link_state;
    1262             :         }
    1263             : 
    1264           0 :         if (tr->tr_ac.ac_if.if_link_state != new_link) {
    1265           0 :                 tr->tr_ac.ac_if.if_link_state = new_link;
    1266           0 :                 if_link_state_change(&tr->tr_ac.ac_if);
    1267           0 :         }
    1268             : 
    1269           0 :         return (rval);
    1270             : }
    1271             : 
    1272             : const void *
    1273           0 : trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
    1274             : {
    1275           0 :         if (m->m_pkthdr.len < (off + len))
    1276           0 :                 return (NULL);
    1277           0 :         else if (m->m_len < (off + len)) {
    1278           0 :                 m_copydata(m, off, len, buf);
    1279           0 :                 return (buf);
    1280             :         }
    1281           0 :         return (mtod(m, caddr_t) + off);
    1282           0 : }
    1283             : 
    1284             : /*
    1285             :  * Simple round robin trunking
    1286             :  */
    1287             : 
    1288             : int
    1289           0 : trunk_rr_attach(struct trunk_softc *tr)
    1290             : {
    1291             :         struct trunk_port *tp;
    1292             : 
    1293           0 :         tr->tr_detach = trunk_rr_detach;
    1294           0 :         tr->tr_start = trunk_rr_start;
    1295           0 :         tr->tr_input = trunk_rr_input;
    1296           0 :         tr->tr_init = NULL;
    1297           0 :         tr->tr_stop = NULL;
    1298           0 :         tr->tr_linkstate = NULL;
    1299           0 :         tr->tr_port_create = NULL;
    1300           0 :         tr->tr_port_destroy = trunk_rr_port_destroy;
    1301           0 :         tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
    1302           0 :         tr->tr_req = NULL;
    1303           0 :         tr->tr_portreq = NULL;
    1304             : 
    1305           0 :         tp = SLIST_FIRST(&tr->tr_ports);
    1306           0 :         tr->tr_psc = (caddr_t)tp;
    1307             : 
    1308           0 :         return (0);
    1309             : }
    1310             : 
    1311             : int
    1312           0 : trunk_rr_detach(struct trunk_softc *tr)
    1313             : {
    1314           0 :         tr->tr_psc = NULL;
    1315           0 :         return (0);
    1316             : }
    1317             : 
    1318             : void
    1319           0 : trunk_rr_port_destroy(struct trunk_port *tp)
    1320             : {
    1321           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1322             : 
    1323           0 :         if (tp == (struct trunk_port *)tr->tr_psc)
    1324           0 :                 tr->tr_psc = NULL;
    1325           0 : }
    1326             : 
    1327             : int
    1328           0 : trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
    1329             : {
    1330           0 :         struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
    1331             :         int error = 0;
    1332             : 
    1333           0 :         if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) {
    1334           0 :                 m_freem(m);
    1335           0 :                 return (ENOENT);
    1336             :         }
    1337             : 
    1338           0 :         if ((error = if_enqueue(tp->tp_if, m)) != 0)
    1339           0 :                 return (error);
    1340             : 
    1341             :         /* Get next active port */
    1342           0 :         tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
    1343           0 :         tr->tr_psc = (caddr_t)tp_next;
    1344             : 
    1345           0 :         return (0);
    1346           0 : }
    1347             : 
    1348             : int
    1349           0 : trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
    1350             : {
    1351             :         /* Just pass in the packet to our trunk device */
    1352           0 :         return (0);
    1353             : }
    1354             : 
    1355             : /*
    1356             :  * Active failover
    1357             :  */
    1358             : 
    1359             : int
    1360           0 : trunk_fail_attach(struct trunk_softc *tr)
    1361             : {
    1362           0 :         tr->tr_detach = trunk_fail_detach;
    1363           0 :         tr->tr_start = trunk_fail_start;
    1364           0 :         tr->tr_input = trunk_fail_input;
    1365           0 :         tr->tr_init = NULL;
    1366           0 :         tr->tr_stop = NULL;
    1367           0 :         tr->tr_port_create = trunk_fail_port_create;
    1368           0 :         tr->tr_port_destroy = trunk_fail_port_destroy;
    1369           0 :         tr->tr_linkstate = trunk_fail_linkstate;
    1370           0 :         tr->tr_req = NULL;
    1371           0 :         tr->tr_portreq = NULL;
    1372             : 
    1373             :         /* Get primary or the next active port */
    1374           0 :         tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
    1375             : 
    1376           0 :         return (0);
    1377             : }
    1378             : 
    1379             : int
    1380           0 : trunk_fail_detach(struct trunk_softc *tr)
    1381             : {
    1382           0 :         tr->tr_psc = NULL;
    1383           0 :         return (0);
    1384             : }
    1385             : 
    1386             : int
    1387           0 : trunk_fail_port_create(struct trunk_port *tp)
    1388             : {
    1389           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1390             : 
    1391             :         /* Get primary or the next active port */
    1392           0 :         tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
    1393           0 :         return (0);
    1394             : }
    1395             : 
    1396             : void
    1397           0 : trunk_fail_port_destroy(struct trunk_port *tp)
    1398             : {
    1399           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1400             :         struct trunk_port *tp_next;
    1401             : 
    1402           0 :         if ((caddr_t)tp == tr->tr_psc) {
    1403             :                 /* Get the next active port */
    1404           0 :                 tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
    1405           0 :                 if (tp_next == tp)
    1406           0 :                         tr->tr_psc = NULL;
    1407             :                 else
    1408           0 :                         tr->tr_psc = (caddr_t)tp_next;
    1409             :         } else {
    1410             :                 /* Get primary or the next active port */
    1411           0 :                 tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
    1412             :         }
    1413           0 : }
    1414             : 
    1415             : int
    1416           0 : trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
    1417             : {
    1418           0 :         struct trunk_port *tp = (struct trunk_port *)tr->tr_psc;
    1419             : 
    1420             :         /* Use the master port if active or the next available port */
    1421           0 :         if (tp == NULL) {
    1422           0 :                 m_freem(m);
    1423           0 :                 return (ENOENT);
    1424             :         }
    1425             : 
    1426           0 :         return (if_enqueue(tp->tp_if, m));
    1427           0 : }
    1428             : 
    1429             : int
    1430           0 : trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
    1431             : {
    1432           0 :         if ((caddr_t)tp == tr->tr_psc)
    1433           0 :                 return (0);
    1434           0 :         m_freem(m);
    1435           0 :         return (-1);
    1436           0 : }
    1437             : 
    1438             : void
    1439           0 : trunk_fail_linkstate(struct trunk_port *tp)
    1440             : {
    1441           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1442             : 
    1443           0 :         tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
    1444           0 : }
    1445             : 
    1446             : /*
    1447             :  * Loadbalancing
    1448             :  */
    1449             : 
    1450             : int
    1451           0 : trunk_lb_attach(struct trunk_softc *tr)
    1452             : {
    1453             :         struct trunk_lb *lb;
    1454             : 
    1455           0 :         if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
    1456           0 :                 return (ENOMEM);
    1457             : 
    1458           0 :         tr->tr_detach = trunk_lb_detach;
    1459           0 :         tr->tr_start = trunk_lb_start;
    1460           0 :         tr->tr_input = trunk_lb_input;
    1461           0 :         tr->tr_port_create = trunk_lb_port_create;
    1462           0 :         tr->tr_port_destroy = trunk_lb_port_destroy;
    1463           0 :         tr->tr_linkstate = NULL;
    1464           0 :         tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
    1465           0 :         tr->tr_req = NULL;
    1466           0 :         tr->tr_portreq = NULL;
    1467           0 :         tr->tr_init = NULL;
    1468           0 :         tr->tr_stop = NULL;
    1469             : 
    1470           0 :         arc4random_buf(&lb->lb_key, sizeof(lb->lb_key));
    1471           0 :         tr->tr_psc = (caddr_t)lb;
    1472             : 
    1473           0 :         return (0);
    1474           0 : }
    1475             : 
    1476             : int
    1477           0 : trunk_lb_detach(struct trunk_softc *tr)
    1478             : {
    1479           0 :         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
    1480             : 
    1481           0 :         free(lb, M_DEVBUF, sizeof *lb);
    1482           0 :         return (0);
    1483             : }
    1484             : 
    1485             : int
    1486           0 : trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
    1487             : {
    1488           0 :         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
    1489             :         struct trunk_port *tp_next;
    1490             :         int i = 0;
    1491             : 
    1492           0 :         bzero(&lb->lb_ports, sizeof(lb->lb_ports));
    1493           0 :         SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
    1494           0 :                 if (tp_next == tp)
    1495             :                         continue;
    1496           0 :                 if (i >= TRUNK_MAX_PORTS)
    1497           0 :                         return (EINVAL);
    1498           0 :                 if (tr->tr_ifflags & IFF_DEBUG)
    1499           0 :                         printf("%s: port %s at index %d\n",
    1500           0 :                             tr->tr_ifname, tp_next->tp_ifname, i);
    1501           0 :                 lb->lb_ports[i++] = tp_next;
    1502           0 :         }
    1503             : 
    1504           0 :         return (0);
    1505           0 : }
    1506             : 
    1507             : int
    1508           0 : trunk_lb_port_create(struct trunk_port *tp)
    1509             : {
    1510           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1511           0 :         return (trunk_lb_porttable(tr, NULL));
    1512             : }
    1513             : 
    1514             : void
    1515           0 : trunk_lb_port_destroy(struct trunk_port *tp)
    1516             : {
    1517           0 :         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
    1518           0 :         trunk_lb_porttable(tr, tp);
    1519           0 : }
    1520             : 
    1521             : int
    1522           0 : trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
    1523             : {
    1524           0 :         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
    1525             :         struct trunk_port *tp = NULL;
    1526             :         u_int32_t p = 0;
    1527             : 
    1528           0 :         p = trunk_hashmbuf(m, &lb->lb_key);
    1529           0 :         p %= tr->tr_count;
    1530           0 :         tp = lb->lb_ports[p];
    1531             : 
    1532             :         /*
    1533             :          * Check the port's link state. This will return the next active
    1534             :          * port if the link is down or the port is NULL.
    1535             :          */
    1536           0 :         if ((tp = trunk_link_active(tr, tp)) == NULL) {
    1537           0 :                 m_freem(m);
    1538           0 :                 return (ENOENT);
    1539             :         }
    1540             : 
    1541           0 :         return (if_enqueue(tp->tp_if, m));
    1542           0 : }
    1543             : 
    1544             : int
    1545           0 : trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
    1546             : {
    1547             :         /* Just pass in the packet to our trunk device */
    1548           0 :         return (0);
    1549             : }
    1550             : 
    1551             : /*
    1552             :  * Broadcast mode
    1553             :  */
    1554             : 
    1555             : int
    1556           0 : trunk_bcast_attach(struct trunk_softc *tr)
    1557             : {
    1558           0 :         tr->tr_detach = trunk_bcast_detach;
    1559           0 :         tr->tr_start = trunk_bcast_start;
    1560           0 :         tr->tr_input = trunk_bcast_input;
    1561           0 :         tr->tr_init = NULL;
    1562           0 :         tr->tr_stop = NULL;
    1563           0 :         tr->tr_port_create = NULL;
    1564           0 :         tr->tr_port_destroy = NULL;
    1565           0 :         tr->tr_linkstate = NULL;
    1566           0 :         tr->tr_req = NULL;
    1567           0 :         tr->tr_portreq = NULL;
    1568             : 
    1569           0 :         return (0);
    1570             : }
    1571             : 
    1572             : int
    1573           0 : trunk_bcast_detach(struct trunk_softc *tr)
    1574             : {
    1575           0 :         return (0);
    1576             : }
    1577             : 
    1578             : int
    1579           0 : trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0)
    1580             : {
    1581             :         int                      active_ports = 0;
    1582             :         int                      errors = 0;
    1583             :         struct trunk_port       *tp, *last = NULL;
    1584             :         struct mbuf             *m;
    1585             : 
    1586           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
    1587           0 :                 if (!TRUNK_PORTACTIVE(tp))
    1588             :                         continue;
    1589             : 
    1590           0 :                 active_ports++;
    1591             : 
    1592           0 :                 if (last != NULL) {
    1593           0 :                         m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
    1594           0 :                         if (m == NULL) {
    1595           0 :                                 errors++;
    1596           0 :                                 break;
    1597             :                         }
    1598             : 
    1599           0 :                         if (if_enqueue(last->tp_if, m) != 0)
    1600           0 :                                 errors++;
    1601             :                 }
    1602             :                 last = tp;
    1603           0 :         }
    1604           0 :         if (last == NULL) {
    1605           0 :                 m_freem(m0);
    1606           0 :                 return (ENOENT);
    1607             :         }
    1608             : 
    1609           0 :         if (if_enqueue(last->tp_if, m0) != 0)
    1610           0 :                 errors++;
    1611             : 
    1612           0 :         if (errors == active_ports)
    1613           0 :                 return (ENOBUFS);
    1614             : 
    1615           0 :         return (0);
    1616           0 : }
    1617             : 
    1618             : int
    1619           0 : trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
    1620             : {
    1621           0 :         return (0);
    1622             : }
    1623             : 
    1624             : /*
    1625             :  * 802.3ad LACP
    1626             :  */
    1627             : 
    1628             : int
    1629           0 : trunk_lacp_attach(struct trunk_softc *tr)
    1630             : {
    1631             :         struct trunk_port *tp;
    1632             :         int error;
    1633             : 
    1634           0 :         tr->tr_detach = trunk_lacp_detach;
    1635           0 :         tr->tr_port_create = lacp_port_create;
    1636           0 :         tr->tr_port_destroy = lacp_port_destroy;
    1637           0 :         tr->tr_linkstate = lacp_linkstate;
    1638           0 :         tr->tr_start = trunk_lacp_start;
    1639           0 :         tr->tr_input = trunk_lacp_input;
    1640           0 :         tr->tr_init = lacp_init;
    1641           0 :         tr->tr_stop = lacp_stop;
    1642           0 :         tr->tr_req = lacp_req;
    1643           0 :         tr->tr_portreq = lacp_portreq;
    1644             : 
    1645           0 :         error = lacp_attach(tr);
    1646           0 :         if (error)
    1647           0 :                 return (error);
    1648             : 
    1649           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
    1650           0 :                 lacp_port_create(tp);
    1651             : 
    1652           0 :         return (error);
    1653           0 : }
    1654             : 
    1655             : int
    1656           0 : trunk_lacp_detach(struct trunk_softc *tr)
    1657             : {
    1658             :         struct trunk_port *tp;
    1659             :         int error;
    1660             : 
    1661           0 :         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
    1662           0 :                 lacp_port_destroy(tp);
    1663             : 
    1664             :         /* unlocking is safe here */
    1665           0 :         error = lacp_detach(tr);
    1666             : 
    1667           0 :         return (error);
    1668             : }
    1669             : 
    1670             : int
    1671           0 : trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
    1672             : {
    1673             :         struct trunk_port *tp;
    1674             : 
    1675           0 :         tp = lacp_select_tx_port(tr, m);
    1676           0 :         if (tp == NULL) {
    1677           0 :                 m_freem(m);
    1678           0 :                 return (EBUSY);
    1679             :         }
    1680             : 
    1681           0 :         return (if_enqueue(tp->tp_if, m));
    1682           0 : }
    1683             : 
    1684             : int
    1685           0 : trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
    1686             : {
    1687           0 :         return (lacp_input(tp, m));
    1688             : }

Generated by: LCOV version 1.13