root/net/if_trunk.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trunkattach
  2. trunk_clone_create
  3. trunk_clone_destroy
  4. trunk_lladdr
  5. trunk_capabilities
  6. trunk_port_lladdr
  7. trunk_port_create
  8. trunk_port_checkstacking
  9. trunk_port_destroy
  10. trunk_port_watchdog
  11. trunk_port_ioctl
  12. trunk_port_ifdetach
  13. trunk_port_get
  14. trunk_port2req
  15. trunk_ioctl
  16. trunk_ether_addmulti
  17. trunk_ether_delmulti
  18. trunk_ether_purgemulti
  19. trunk_ether_cmdmulti
  20. trunk_ioctl_allports
  21. trunk_start
  22. trunk_enqueue
  23. trunk_hashmbuf
  24. trunk_init
  25. trunk_stop
  26. trunk_watchdog
  27. trunk_input
  28. trunk_media_change
  29. trunk_media_status
  30. trunk_port_state
  31. trunk_link_active
  32. trunk_rr_attach
  33. trunk_rr_detach
  34. trunk_rr_port_destroy
  35. trunk_rr_start
  36. trunk_rr_input
  37. trunk_fail_attach
  38. trunk_fail_detach
  39. trunk_fail_start
  40. trunk_fail_input
  41. trunk_lb_attach
  42. trunk_lb_detach
  43. trunk_lb_porttable
  44. trunk_lb_port_create
  45. trunk_lb_port_destroy
  46. trunk_lb_gethdr
  47. trunk_lb_start
  48. trunk_lb_input

    1 /*      $OpenBSD: if_trunk.c,v 1.32 2007/05/26 17:13:31 jason Exp $     */
    2 
    3 /*
    4  * Copyright (c) 2005, 2006 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 "bpfilter.h"
   20 #include "trunk.h"
   21 
   22 #include <sys/param.h>
   23 #include <sys/kernel.h>
   24 #include <sys/malloc.h>
   25 #include <sys/mbuf.h>
   26 #include <sys/queue.h>
   27 #include <sys/socket.h>
   28 #include <sys/sockio.h>
   29 #include <sys/sysctl.h>
   30 #include <sys/systm.h>
   31 #include <sys/proc.h>
   32 #include <sys/hash.h>
   33 
   34 #include <dev/rndvar.h>
   35 
   36 #include <net/if.h>
   37 #include <net/if_arp.h>
   38 #include <net/if_dl.h>
   39 #include <net/if_llc.h>
   40 #include <net/if_media.h>
   41 #include <net/if_types.h>
   42 #if NBPFILTER > 0
   43 #include <net/bpf.h>
   44 #endif
   45 
   46 #ifdef INET
   47 #include <netinet/in.h>
   48 #include <netinet/in_systm.h>
   49 #include <netinet/if_ether.h>
   50 #include <netinet/ip.h>
   51 #endif
   52 
   53 #ifdef INET6
   54 #include <netinet/ip6.h>
   55 #endif
   56 
   57 #include <net/if_vlan_var.h>
   58 #include <net/if_trunk.h>
   59 
   60 SLIST_HEAD(__trhead, trunk_softc) trunk_list;   /* list of trunks */
   61 
   62 extern struct ifaddr **ifnet_addrs;
   63 
   64 void     trunkattach(int);
   65 int      trunk_clone_create(struct if_clone *, int);
   66 int      trunk_clone_destroy(struct ifnet *);
   67 void     trunk_lladdr(struct arpcom *, u_int8_t *);
   68 int      trunk_capabilities(struct trunk_softc *);
   69 void     trunk_port_lladdr(struct trunk_port *, u_int8_t *);
   70 int      trunk_port_create(struct trunk_softc *, struct ifnet *);
   71 int      trunk_port_destroy(struct trunk_port *);
   72 void     trunk_port_watchdog(struct ifnet *);
   73 void     trunk_port_state(void *);
   74 int      trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
   75 struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
   76 int      trunk_port_checkstacking(struct trunk_softc *);
   77 void     trunk_port2req(struct trunk_port *, struct trunk_reqport *);
   78 int      trunk_ioctl(struct ifnet *, u_long, caddr_t);
   79 int      trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
   80 int      trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
   81 void     trunk_ether_purgemulti(struct trunk_softc *);
   82 int      trunk_ether_cmdmulti(struct trunk_port *, u_long);
   83 int      trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
   84 void     trunk_start(struct ifnet *);
   85 void     trunk_init(struct ifnet *);
   86 void     trunk_stop(struct ifnet *);
   87 void     trunk_watchdog(struct ifnet *);
   88 int      trunk_media_change(struct ifnet *);
   89 void     trunk_media_status(struct ifnet *, struct ifmediareq *);
   90 struct trunk_port *trunk_link_active(struct trunk_softc *,
   91             struct trunk_port *);
   92 
   93 struct if_clone trunk_cloner =
   94     IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
   95 
   96 /* Simple round robin */
   97 int      trunk_rr_attach(struct trunk_softc *);
   98 int      trunk_rr_detach(struct trunk_softc *);
   99 void     trunk_rr_port_destroy(struct trunk_port *);
  100 int      trunk_rr_start(struct trunk_softc *, struct mbuf *);
  101 int      trunk_rr_input(struct trunk_softc *, struct trunk_port *,
  102             struct ether_header *, struct mbuf *);
  103 
  104 /* Active failover */
  105 int      trunk_fail_attach(struct trunk_softc *);
  106 int      trunk_fail_detach(struct trunk_softc *);
  107 int      trunk_fail_start(struct trunk_softc *, struct mbuf *);
  108 int      trunk_fail_input(struct trunk_softc *, struct trunk_port *,
  109             struct ether_header *, struct mbuf *);
  110 
  111 /* Loadbalancing */
  112 int      trunk_lb_attach(struct trunk_softc *);
  113 int      trunk_lb_detach(struct trunk_softc *);
  114 int      trunk_lb_port_create(struct trunk_port *);
  115 void     trunk_lb_port_destroy(struct trunk_port *);
  116 int      trunk_lb_start(struct trunk_softc *, struct mbuf *);
  117 int      trunk_lb_input(struct trunk_softc *, struct trunk_port *,
  118             struct ether_header *, struct mbuf *);
  119 int      trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
  120 const void *trunk_lb_gethdr(struct mbuf *, u_int, u_int, void *);
  121 
  122 /* Trunk protocol table */
  123 static const struct {
  124         enum trunk_proto        ti_proto;
  125         int                     (*ti_attach)(struct trunk_softc *);
  126 } trunk_protos[] = {
  127         { TRUNK_PROTO_ROUNDROBIN,       trunk_rr_attach },
  128         { TRUNK_PROTO_FAILOVER,         trunk_fail_attach },
  129         { TRUNK_PROTO_LOADBALANCE,      trunk_lb_attach },
  130         { TRUNK_PROTO_NONE,             NULL }
  131 };
  132 
  133 void
  134 trunkattach(int count)
  135 {
  136         SLIST_INIT(&trunk_list);
  137         if_clone_attach(&trunk_cloner);
  138 }
  139 
  140 int
  141 trunk_clone_create(struct if_clone *ifc, int unit)
  142 {
  143         struct trunk_softc *tr;
  144         struct ifnet *ifp;
  145         int i, error = 0;
  146 
  147         if ((tr = malloc(sizeof(struct trunk_softc),
  148             M_DEVBUF, M_NOWAIT)) == NULL)
  149                 return (ENOMEM);
  150 
  151         bzero(tr, sizeof(struct trunk_softc));
  152 
  153         tr->tr_unit = unit;
  154         tr->tr_proto = TRUNK_PROTO_NONE;
  155         for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
  156                 if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
  157                         tr->tr_proto = trunk_protos[i].ti_proto;
  158                         if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
  159                                 free(tr, M_DEVBUF);
  160                                 return (error);
  161                         }
  162                         break;
  163                 }
  164         }
  165         SLIST_INIT(&tr->tr_ports);
  166 
  167         /* Initialise pseudo media types */
  168         ifmedia_init(&tr->tr_media, 0, trunk_media_change,
  169             trunk_media_status);
  170         ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
  171         ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
  172 
  173         ifp = &tr->tr_ac.ac_if;
  174         ifp->if_carp = NULL;
  175         ifp->if_type = IFT_ETHER;
  176         ifp->if_softc = tr;
  177         ifp->if_start = trunk_start;
  178         ifp->if_watchdog = trunk_watchdog;
  179         ifp->if_ioctl = trunk_ioctl;
  180         ifp->if_output = ether_output;
  181         ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
  182         ifp->if_capabilities = trunk_capabilities(tr);
  183 
  184         IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
  185         IFQ_SET_READY(&ifp->if_snd);
  186 
  187         snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
  188             ifc->ifc_name, unit);
  189 
  190         /*
  191          * Attach as an ordinary ethernet device, childs will be attached
  192          * as special device IFT_IEEE8023ADLAG.
  193          */
  194         if_attach(ifp);
  195         ether_ifattach(ifp);
  196 
  197         /* Insert into the global list of trunks */
  198         SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
  199 
  200         return (0);
  201 }
  202 
  203 int
  204 trunk_clone_destroy(struct ifnet *ifp)
  205 {
  206         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
  207         struct trunk_port *tp;
  208         int error, s;
  209 
  210         /* Remove any multicast groups that we may have joined. */
  211         trunk_ether_purgemulti(tr);
  212 
  213         s = splnet();
  214 
  215         /* Shutdown and remove trunk ports, return on error */
  216         while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
  217                 if ((error = trunk_port_destroy(tp)) != 0) {
  218                         splx(s);
  219                         return (error);
  220                 }
  221         }
  222 
  223         ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
  224         ether_ifdetach(ifp);
  225         if_detach(ifp);
  226 
  227         SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
  228         free(tr, M_DEVBUF);
  229 
  230         splx(s);
  231 
  232         return (0);
  233 }
  234 
  235 void
  236 trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
  237 {
  238         struct ifnet *ifp = &ac->ac_if;
  239         struct ifaddr *ifa;
  240         struct sockaddr_dl *sdl;
  241 
  242         ifa = ifnet_addrs[ifp->if_index];
  243         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
  244         sdl->sdl_type = IFT_ETHER;
  245         sdl->sdl_alen = ETHER_ADDR_LEN;
  246         bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
  247         bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
  248 }
  249 
  250 int
  251 trunk_capabilities(struct trunk_softc *tr)
  252 {
  253         struct trunk_port *tp;
  254         int cap = ~0, priv;
  255 
  256         /* Preserve private capabilities */
  257         priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
  258 
  259         /* Get capabilities from the trunk ports */
  260         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
  261                 cap &= tp->tp_capabilities;
  262 
  263         if (tr->tr_ifflags & IFF_DEBUG) {
  264                 printf("%s: capabilities 0x%08x\n",
  265                     tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
  266         }
  267 
  268         return (cap == ~0 ? priv : (cap | priv));
  269 }
  270 
  271 void
  272 trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
  273 {
  274         struct ifnet *ifp = tp->tp_if;
  275         struct ifaddr *ifa;
  276         struct ifreq ifr;
  277 
  278         /* Set the link layer address */
  279         trunk_lladdr((struct arpcom *)ifp, lladdr);
  280 
  281         /* Reset the port to update the lladdr */
  282         if (ifp->if_flags & IFF_UP) {
  283                 int s = splnet();
  284                 ifp->if_flags &= ~IFF_UP;
  285                 (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
  286                 ifp->if_flags |= IFF_UP;
  287                 (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
  288                 splx(s);
  289                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
  290                         if (ifa->ifa_addr != NULL &&
  291                             ifa->ifa_addr->sa_family == AF_INET)
  292                                 arp_ifinit((struct arpcom *)ifp, ifa);
  293                 }
  294         }
  295 }
  296 
  297 int
  298 trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
  299 {
  300         struct trunk_softc *tr_ptr;
  301         struct trunk_port *tp;
  302         int error = 0;
  303 
  304         /* Limit the maximal number of trunk ports */
  305         if (tr->tr_count >= TRUNK_MAX_PORTS)
  306                 return (ENOSPC);
  307 
  308         /* New trunk port has to be in an idle state */
  309         if (ifp->if_flags & IFF_OACTIVE)
  310                 return (EBUSY);
  311 
  312         /* Check if port has already been associated to a trunk */
  313         if (trunk_port_get(NULL, ifp) != NULL)
  314                 return (EBUSY);
  315 
  316         /* XXX Disallow non-ethernet interfaces (this should be any of 802) */
  317         if (ifp->if_type != IFT_ETHER)
  318                 return (EPROTONOSUPPORT);
  319 
  320         if ((error = ifpromisc(ifp, 1)) != 0)
  321                 return (error);
  322 
  323         if ((tp = malloc(sizeof(struct trunk_port),
  324             M_DEVBUF, M_NOWAIT)) == NULL)
  325                 return (ENOMEM);
  326 
  327         bzero(tp, sizeof(struct trunk_port));
  328 
  329         /* Check if port is a stacked trunk */
  330         SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
  331                 if (ifp == &tr_ptr->tr_ac.ac_if) {
  332                         tp->tp_flags |= TRUNK_PORT_STACK;
  333                         if (trunk_port_checkstacking(tr_ptr) >=
  334                             TRUNK_MAX_STACKING) {
  335                                 free(tp, M_DEVBUF);
  336                                 return (E2BIG);
  337                         }
  338                 }
  339         }
  340 
  341         /* Change the interface type */
  342         tp->tp_iftype = ifp->if_type;
  343         ifp->if_type = IFT_IEEE8023ADLAG;
  344         ifp->if_tp = (caddr_t)tp;
  345         tp->tp_watchdog = ifp->if_watchdog;
  346         ifp->if_watchdog = trunk_port_watchdog;
  347         tp->tp_ioctl = ifp->if_ioctl;
  348         ifp->if_ioctl = trunk_port_ioctl;
  349 
  350         tp->tp_if = ifp;
  351         tp->tp_trunk = tr;
  352 
  353         /* Save port link layer address */
  354         bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
  355 
  356         if (SLIST_EMPTY(&tr->tr_ports)) {
  357                 tr->tr_primary = tp;
  358                 tp->tp_flags |= TRUNK_PORT_MASTER;
  359                 trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
  360         }
  361 
  362         /* Update link layer address for this port */
  363         trunk_port_lladdr(tp, tr->tr_primary->tp_lladdr);
  364 
  365         /* Insert into the list of ports */
  366         SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
  367         tr->tr_count++;
  368 
  369         /* Update trunk capabilities */
  370         tr->tr_capabilities = trunk_capabilities(tr);
  371 
  372         /* Add multicast addresses to this port */
  373         trunk_ether_cmdmulti(tp, SIOCADDMULTI);
  374 
  375         /* Register callback for physical link state changes */
  376         if (ifp->if_linkstatehooks != NULL)
  377                 tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
  378                     trunk_port_state, tp);
  379 
  380         if (tr->tr_port_create != NULL)
  381                 error = (*tr->tr_port_create)(tp);
  382 
  383         return (error);
  384 }
  385 
  386 int
  387 trunk_port_checkstacking(struct trunk_softc *tr)
  388 {
  389         struct trunk_softc *tr_ptr;
  390         struct trunk_port *tp;
  391         int m = 0;
  392 
  393         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
  394                 if (tp->tp_flags & TRUNK_PORT_STACK) {
  395                         tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
  396                         m = MAX(m, trunk_port_checkstacking(tr_ptr));
  397                 }
  398         }
  399 
  400         return (m + 1);
  401 }
  402 
  403 int
  404 trunk_port_destroy(struct trunk_port *tp)
  405 {
  406         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
  407         struct trunk_port *tp_ptr;
  408         struct ifnet *ifp = tp->tp_if;
  409 
  410         if (tr->tr_port_destroy != NULL)
  411                 (*tr->tr_port_destroy)(tp);
  412 
  413         /* Remove multicast addresses from this port */
  414         trunk_ether_cmdmulti(tp, SIOCDELMULTI);
  415 
  416         /* Port has to be down */
  417         if (ifp->if_flags & IFF_UP)
  418                 if_down(ifp);
  419 
  420         ifpromisc(ifp, 0);
  421 
  422         /* Restore interface */
  423         ifp->if_type = tp->tp_iftype;
  424         ifp->if_watchdog = tp->tp_watchdog;
  425         ifp->if_ioctl = tp->tp_ioctl;
  426         ifp->if_tp = NULL;
  427 
  428         if (ifp->if_linkstatehooks != NULL)
  429                 hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie);
  430 
  431         /* Finally, remove the port from the trunk */
  432         SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
  433         tr->tr_count--;
  434 
  435         /* Update the primary interface */
  436         if (tp == tr->tr_primary) {
  437                 u_int8_t lladdr[ETHER_ADDR_LEN];
  438 
  439                 if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
  440                         bzero(&lladdr, ETHER_ADDR_LEN);
  441                 } else {
  442                         bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
  443                             lladdr, ETHER_ADDR_LEN);
  444                         tp_ptr->tp_flags = TRUNK_PORT_MASTER;
  445                 }
  446                 trunk_lladdr(&tr->tr_ac, lladdr);
  447                 tr->tr_primary = tp_ptr;
  448 
  449                 /* Update link layer address for each port */
  450                 SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
  451                         trunk_port_lladdr(tp_ptr, lladdr);
  452         }
  453 
  454         /* Reset the port lladdr */
  455         trunk_port_lladdr(tp, tp->tp_lladdr);
  456 
  457         free(tp, M_DEVBUF);
  458 
  459         /* Update trunk capabilities */
  460         tr->tr_capabilities = trunk_capabilities(tr);
  461 
  462         return (0);
  463 }
  464 
  465 void
  466 trunk_port_watchdog(struct ifnet *ifp)
  467 {
  468         struct trunk_softc *tr;
  469         struct trunk_port *tp;
  470 
  471         /* Should be checked by the caller */
  472         if (ifp->if_type != IFT_IEEE8023ADLAG)
  473                 return;
  474         if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
  475             (tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
  476                 return;
  477 
  478         if (tp->tp_watchdog != NULL)
  479                 (*tp->tp_watchdog)(ifp);
  480 }
  481 
  482 
  483 int
  484 trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  485 {
  486         struct trunk_reqport *rp = (struct trunk_reqport *)data;
  487         struct trunk_softc *tr;
  488         struct trunk_port *tp;
  489         int s, error = 0;
  490 
  491         s = splnet();
  492 
  493         /* Should be checked by the caller */
  494         if (ifp->if_type != IFT_IEEE8023ADLAG ||
  495             (tp = (struct trunk_port *)ifp->if_tp) == NULL ||
  496             (tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
  497                 goto fallback;
  498 
  499         switch (cmd) {
  500         case SIOCGTRUNKPORT:
  501                 if (rp->rp_portname[0] == '\0' ||
  502                     ifunit(rp->rp_portname) != ifp) {
  503                         error = EINVAL;
  504                         break;
  505                 }
  506 
  507                 /* Search in all trunks if the global flag is set */
  508                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
  509                     NULL : tr, ifp)) == NULL) {
  510                         error = ENOENT;
  511                         break;
  512                 }
  513 
  514                 trunk_port2req(tp, rp);
  515                 break;
  516         default:
  517                 goto fallback;
  518         }
  519 
  520         splx(s);
  521         return (error);
  522 
  523  fallback:
  524         splx(s);
  525 
  526         if (tp != NULL)
  527                 return ((*tp->tp_ioctl)(ifp, cmd, data));
  528 
  529         return (EINVAL);
  530 }
  531 
  532 void
  533 trunk_port_ifdetach(struct ifnet *ifp)
  534 {
  535         struct trunk_port *tp;
  536 
  537         if ((tp = (struct trunk_port *)ifp->if_tp) == NULL)
  538                 return;
  539 
  540         trunk_port_destroy(tp);
  541 }
  542 
  543 struct trunk_port *
  544 trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
  545 {
  546         struct trunk_port *tp;
  547         struct trunk_softc *tr_ptr;
  548 
  549         if (tr != NULL) {
  550                 /* Search port in specified trunk */
  551                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
  552                         if (tp->tp_if == ifp)
  553                                 return (tp);
  554                 }
  555         } else {
  556                 /* Search all trunks for the selected port */
  557                 SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
  558                         SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
  559                                 if (tp->tp_if == ifp)
  560                                         return (tp);
  561                         }
  562                 }
  563         }
  564 
  565         return (NULL);
  566 }
  567 
  568 void
  569 trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
  570 {
  571         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
  572         strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
  573         strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
  574         rp->rp_prio = tp->tp_prio;
  575         rp->rp_flags = tp->tp_flags;
  576         if (TRUNK_PORTACTIVE(tp))
  577                 rp->rp_flags |= TRUNK_PORT_ACTIVE;
  578 }
  579 
  580 int
  581 trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  582 {
  583         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
  584         struct trunk_reqall *ra = (struct trunk_reqall *)data;
  585         struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
  586         struct ifreq *ifr = (struct ifreq *)data;
  587         struct ifaddr *ifa = (struct ifaddr *)data;
  588         struct trunk_port *tp;
  589         struct ifnet *tpif;
  590         int s, i, error = 0;
  591 
  592         s = splnet();
  593 
  594         if ((error = ether_ioctl(ifp, &tr->tr_ac, cmd, data)) > 0)
  595                 goto out;
  596 
  597         bzero(&rpbuf, sizeof(rpbuf));
  598 
  599         switch (cmd) {
  600         case SIOCGTRUNK:
  601                 ra->ra_proto = tr->tr_proto;
  602                 ra->ra_ports = i = 0;
  603                 tp = SLIST_FIRST(&tr->tr_ports);
  604                 while (tp && ra->ra_size >=
  605                     i + sizeof(struct trunk_reqport)) {
  606                         trunk_port2req(tp, &rpbuf);
  607                         error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
  608                             sizeof(struct trunk_reqport));
  609                         if (error)
  610                                 break;
  611                         i += sizeof(struct trunk_reqport);
  612                         ra->ra_ports++;
  613                         tp = SLIST_NEXT(tp, tp_entries);
  614                 }
  615                 break;
  616         case SIOCSTRUNK:
  617                 if ((error = suser(curproc, 0)) != 0) {
  618                         error = EPERM;
  619                         break;
  620                 }
  621                 if (ra->ra_proto >= TRUNK_PROTO_MAX) {
  622                         error = EPROTONOSUPPORT;
  623                         break;
  624                 }
  625                 if (tr->tr_proto != TRUNK_PROTO_NONE)
  626                         error = tr->tr_detach(tr);
  627                 if (error != 0)
  628                         break;
  629                 for (i = 0; i < (sizeof(trunk_protos) /
  630                     sizeof(trunk_protos[0])); i++) {
  631                         if (trunk_protos[i].ti_proto == ra->ra_proto) {
  632                                 if (tr->tr_ifflags & IFF_DEBUG)
  633                                         printf("%s: using proto %u\n",
  634                                             tr->tr_ifname,
  635                                             trunk_protos[i].ti_proto);
  636                                 tr->tr_proto = trunk_protos[i].ti_proto;
  637                                 if (tr->tr_proto != TRUNK_PROTO_NONE)
  638                                         error = trunk_protos[i].ti_attach(tr);
  639                                 goto out;
  640                         }
  641                 }
  642                 error = EPROTONOSUPPORT;
  643                 break;
  644         case SIOCGTRUNKPORT:
  645                 if (rp->rp_portname[0] == '\0' ||
  646                     (tpif = ifunit(rp->rp_portname)) == NULL) {
  647                         error = EINVAL;
  648                         break;
  649                 }
  650 
  651                 /* Search in all trunks if the global flag is set */
  652                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
  653                     NULL : tr, tpif)) == NULL) {
  654                         error = ENOENT;
  655                         break;
  656                 }
  657 
  658                 trunk_port2req(tp, rp);
  659                 break;
  660         case SIOCSTRUNKPORT:
  661                 if ((error = suser(curproc, 0)) != 0) {
  662                         error = EPERM;
  663                         break;
  664                 }
  665                 if (rp->rp_portname[0] == '\0' ||
  666                     (tpif = ifunit(rp->rp_portname)) == NULL) {
  667                         error = EINVAL;
  668                         break;
  669                 }
  670                 error = trunk_port_create(tr, tpif);
  671                 break;
  672         case SIOCSTRUNKDELPORT:
  673                 if ((error = suser(curproc, 0)) != 0) {
  674                         error = EPERM;
  675                         break;
  676                 }
  677                 if (rp->rp_portname[0] == '\0' ||
  678                     (tpif = ifunit(rp->rp_portname)) == NULL) {
  679                         error = EINVAL;
  680                         break;
  681                 }
  682 
  683                 /* Search in all trunks if the global flag is set */
  684                 if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
  685                     NULL : tr, tpif)) == NULL) {
  686                         error = ENOENT;
  687                         break;
  688                 }
  689 
  690                 error = trunk_port_destroy(tp);
  691                 break;
  692         case SIOCSIFADDR:
  693                 ifp->if_flags |= IFF_UP;
  694 
  695 #ifdef INET
  696                 if (ifa->ifa_addr->sa_family == AF_INET)
  697                         arp_ifinit(&tr->tr_ac, ifa);
  698 #endif /* INET */
  699                 error = ENETRESET;
  700                 break;
  701         case SIOCSIFMTU:
  702                 if (ifr->ifr_mtu > ETHERMTU) {
  703                         error = EINVAL;
  704                         break;
  705                 }
  706                 ifp->if_mtu = ifr->ifr_mtu;
  707                 break;
  708         case SIOCSIFFLAGS:
  709                 error = ENETRESET;
  710                 break;
  711         case SIOCADDMULTI:
  712                 error = trunk_ether_addmulti(tr, ifr);
  713                 break;
  714         case SIOCDELMULTI:
  715                 error = trunk_ether_delmulti(tr, ifr);
  716                 break;
  717         case SIOCSIFMEDIA:
  718         case SIOCGIFMEDIA:
  719                 error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
  720                 break;
  721         case SIOCSIFLLADDR:
  722                 /* Update the port lladdrs as well */
  723                 SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
  724                         trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
  725                 error = ENETRESET;
  726                 break;
  727         default:
  728                 error = EINVAL;
  729                 break;
  730         }
  731 
  732         if (error == ENETRESET) {
  733                 if (ifp->if_flags & IFF_UP) {
  734                         if ((ifp->if_flags & IFF_RUNNING) == 0)
  735                                 trunk_init(ifp);
  736                 } else {
  737                         if (ifp->if_flags & IFF_RUNNING)
  738                                 trunk_stop(ifp);
  739                 }
  740                 error = 0;
  741         }
  742 
  743  out:
  744         splx(s);
  745         return (error);
  746 }
  747 
  748 int
  749 trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
  750 {
  751         struct trunk_mc *mc;
  752         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  753         int error;
  754 
  755         /* Ignore ENETRESET error code */
  756         if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
  757                 return (error);
  758 
  759         if ((mc = (struct trunk_mc *)malloc(sizeof(struct trunk_mc),
  760             M_DEVBUF, M_NOWAIT)) == NULL) {
  761                 error = ENOMEM;
  762                 goto failed;
  763         }
  764 
  765         ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
  766         ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
  767         bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
  768         SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
  769 
  770         if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
  771             (caddr_t)ifr)) != 0) {
  772                 trunk_ether_delmulti(tr, ifr);
  773                 return (error);
  774         }
  775 
  776         return (error);
  777 
  778  failed:
  779         ether_delmulti(ifr, &tr->tr_ac);
  780 
  781         return (error);
  782 }
  783 
  784 int
  785 trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
  786 {
  787         struct ether_multi *enm;
  788         struct trunk_mc *mc;
  789         u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
  790         int error;
  791 
  792         if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
  793                 return (error);
  794         ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
  795         if (enm == NULL)
  796                 return (EINVAL);
  797 
  798         SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
  799                 if (mc->mc_enm == enm)
  800                         break;
  801 
  802         /* We won't delete entries we didn't add */
  803         if (mc == NULL)
  804                 return (EINVAL);
  805 
  806         if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
  807                 return (error);
  808 
  809         if ((error = trunk_ioctl_allports(tr, SIOCDELMULTI,
  810             (caddr_t)ifr)) != 0) {
  811                 /* XXX At least one port failed to remove the address */
  812                 if (tr->tr_ifflags & IFF_DEBUG) {
  813                         printf("%s: failed to remove multicast address "
  814                             "on all ports\n", tr->tr_ifname);
  815                 }
  816         }
  817 
  818         SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
  819         free(mc, M_DEVBUF);
  820 
  821         return (0);
  822 }
  823 
  824 void
  825 trunk_ether_purgemulti(struct trunk_softc *tr)
  826 {
  827         struct trunk_mc *mc;
  828         struct trunk_ifreq ifs;
  829         struct ifreq *ifr = &ifs.ifreq.ifreq;
  830 
  831         while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
  832                 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
  833 
  834                 /* Try to remove multicast address on all ports */
  835                 trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
  836 
  837                 SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
  838                 free(mc, M_DEVBUF);
  839         }
  840 }
  841 
  842 int
  843 trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
  844 {
  845         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
  846         struct trunk_mc *mc;
  847         struct trunk_ifreq ifs;
  848         struct ifreq *ifr = &ifs.ifreq.ifreq;
  849         int ret, error = 0;
  850 
  851         bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
  852         SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
  853                 bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
  854 
  855                 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
  856                         if (tr->tr_ifflags & IFF_DEBUG) {
  857                                 printf("%s: ioctl %lu failed on %s: %d\n",
  858                                     tr->tr_ifname, cmd, tp->tp_ifname, ret);
  859                         }
  860                         /* Store last known error and continue */
  861                         error = ret;
  862                 }
  863         }
  864 
  865         return (error);
  866 }
  867 
  868 int
  869 trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
  870 {
  871         struct ifreq *ifr = (struct ifreq *)data;
  872         struct trunk_port *tp;
  873         int ret, error = 0;
  874 
  875         SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
  876                 bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
  877                 if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
  878                         if (tr->tr_ifflags & IFF_DEBUG) {
  879                                 printf("%s: ioctl %lu failed on %s: %d\n",
  880                                     tr->tr_ifname, cmd, tp->tp_ifname, ret);
  881                         }
  882                         /* Store last known error and continue */
  883                         error = ret;
  884                 }
  885         }
  886 
  887         return (error);
  888 }
  889 
  890 void
  891 trunk_start(struct ifnet *ifp)
  892 {
  893         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
  894         struct mbuf *m;
  895         int error = 0;
  896 
  897         for (;; error = 0) {
  898                 IFQ_DEQUEUE(&ifp->if_snd, m);
  899                 if (m == NULL)
  900                         break;
  901 
  902 #if NBPFILTER > 0
  903                 if (ifp->if_bpf)
  904                         bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
  905 #endif
  906 
  907                 if (tr->tr_proto != TRUNK_PROTO_NONE)
  908                         error = (*tr->tr_start)(tr, m);
  909                 else
  910                         m_free(m);
  911 
  912                 if (error == 0)
  913                         ifp->if_opackets++;
  914                 else
  915                         ifp->if_oerrors++;
  916         }
  917 
  918         return;
  919 }
  920 
  921 int
  922 trunk_enqueue(struct ifnet *ifp, struct mbuf *m)
  923 {
  924         int error = 0;
  925 
  926         /* Send mbuf */
  927         IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
  928         if (error)
  929                 return (error);
  930         if ((ifp->if_flags & IFF_OACTIVE) == 0)
  931                 (*ifp->if_start)(ifp);
  932 
  933         ifp->if_obytes += m->m_pkthdr.len;
  934         if (m->m_flags & M_MCAST)
  935                 ifp->if_omcasts++;
  936 
  937         return (error);
  938 }
  939 
  940 u_int32_t
  941 trunk_hashmbuf(struct mbuf *m, u_int32_t key)
  942 {
  943         u_int16_t etype;
  944         u_int32_t p = 0;
  945         u_int16_t *vlan, vlanbuf[2];
  946         int off;
  947         struct ether_header *eh;
  948 #ifdef INET
  949         struct ip *ip, ipbuf;
  950 #endif
  951 #ifdef INET6
  952         struct ip6_hdr *ip6, ip6buf;
  953 #endif
  954 
  955         off = sizeof(*eh);
  956         if (m->m_len < off)
  957                 return (p);
  958         eh = mtod(m, struct ether_header *);
  959         etype = ntohs(eh->ether_type);
  960         p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, key);
  961         p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
  962 
  963         /* Special handling for encapsulating VLAN frames */
  964         if (etype == ETHERTYPE_VLAN) {
  965                 if ((vlan = (u_int16_t *)
  966                     trunk_lb_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
  967                         return (p);
  968                 p = hash32_buf(vlan, sizeof(*vlan), p);
  969                 etype = ntohs(vlan[1]);
  970                 off += EVL_ENCAPLEN;
  971         }
  972 
  973         switch (etype) {
  974 #ifdef INET
  975         case ETHERTYPE_IP:
  976                 if ((ip = (struct ip *)
  977                     trunk_lb_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
  978                         return (p);
  979                 p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p);
  980                 p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
  981                 break;
  982 #endif
  983 #ifdef INET6
  984         case ETHERTYPE_IPV6:
  985                 if ((ip6 = (struct ip6_hdr *)
  986                     trunk_lb_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
  987                         return (p);
  988                 p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
  989                 p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
  990                 break;
  991 #endif
  992         }
  993 
  994         return (p);
  995 }
  996 
  997 void
  998 trunk_init(struct ifnet *ifp)
  999 {
 1000         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 1001         int s;
 1002 
 1003         s = splnet();
 1004 
 1005         ifp->if_flags |= IFF_RUNNING;
 1006         ifp->if_flags &= ~IFF_OACTIVE;
 1007 
 1008         if (tr->tr_init != NULL)
 1009                 (*tr->tr_init)(tr);
 1010 
 1011         splx(s);
 1012 }
 1013 
 1014 void
 1015 trunk_stop(struct ifnet *ifp)
 1016 {
 1017         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 1018         int s;
 1019 
 1020         s = splnet();
 1021 
 1022         ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 1023 
 1024         if (tr->tr_stop != NULL)
 1025                 (*tr->tr_stop)(tr);
 1026 
 1027         splx(s);
 1028 }
 1029 
 1030 void
 1031 trunk_watchdog(struct ifnet *ifp)
 1032 {
 1033         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 1034 
 1035         if (tr->tr_proto != TRUNK_PROTO_NONE &&
 1036             (*tr->tr_watchdog)(tr) != 0) {
 1037                 ifp->if_oerrors++;
 1038         }
 1039 
 1040 }
 1041 
 1042 int
 1043 trunk_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
 1044 {
 1045         struct trunk_softc *tr;
 1046         struct trunk_port *tp;
 1047         struct ifnet *trifp = NULL;
 1048         int error = 0;
 1049 
 1050         /* Should be checked by the caller */
 1051         if (ifp->if_type != IFT_IEEE8023ADLAG) {
 1052                 error = EPROTONOSUPPORT;
 1053                 goto bad;
 1054         }
 1055         if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
 1056             (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
 1057                 error = ENOENT;
 1058                 goto bad;
 1059         }
 1060         if (tr->tr_proto == TRUNK_PROTO_NONE)
 1061                 goto bad;
 1062         trifp = &tr->tr_ac.ac_if;
 1063 
 1064         error = (*tr->tr_input)(tr, tp, eh, m);
 1065         if (error != 0)
 1066                 goto bad;
 1067 
 1068 #if NBPFILTER > 0
 1069         if (trifp->if_bpf)
 1070                 bpf_mtap_hdr(trifp->if_bpf, (char *)eh, ETHER_HDR_LEN, m,
 1071                     BPF_DIRECTION_IN);
 1072 #endif
 1073 
 1074         trifp->if_ipackets++;
 1075 
 1076         return (0);
 1077 
 1078  bad:
 1079         if (error && trifp != NULL)
 1080                 trifp->if_ierrors++;
 1081         return (error);
 1082 }
 1083 
 1084 int
 1085 trunk_media_change(struct ifnet *ifp)
 1086 {
 1087         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 1088 
 1089         if (tr->tr_ifflags & IFF_DEBUG)
 1090                 printf("%s\n", __func__);
 1091 
 1092         /* Ignore */
 1093         return (0);
 1094 }
 1095 
 1096 void
 1097 trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
 1098 {
 1099         struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 1100         struct trunk_port *tp;
 1101 
 1102         imr->ifm_status = IFM_AVALID;
 1103         imr->ifm_active = IFM_ETHER | IFM_AUTO;
 1104 
 1105         tp = tr->tr_primary;
 1106         if (tp != NULL && tp->tp_if->if_flags & IFF_UP)
 1107                 imr->ifm_status |= IFM_ACTIVE;
 1108 }
 1109 
 1110 void
 1111 trunk_port_state(void *arg)
 1112 {
 1113         struct trunk_port *tp = (struct trunk_port *)arg;
 1114         struct trunk_softc *tr = NULL;
 1115 
 1116         if (tp != NULL)
 1117                 tr = (struct trunk_softc *)tp->tp_trunk;
 1118         if (tr == NULL)
 1119                 return;
 1120         if (tr->tr_linkstate != NULL)
 1121                 (*tr->tr_linkstate)(tp);
 1122         trunk_link_active(tr, tp);
 1123 }
 1124 
 1125 struct trunk_port *
 1126 trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
 1127 {
 1128         struct trunk_port *tp_next, *rval = NULL;
 1129         int new_link = LINK_STATE_DOWN;
 1130 
 1131         /*
 1132          * Search a port which reports an active link state.
 1133          */
 1134 
 1135         if (tp == NULL)
 1136                 goto search;
 1137         if (TRUNK_PORTACTIVE(tp)) {
 1138                 rval = tp;
 1139                 goto found;
 1140         }
 1141         if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
 1142             TRUNK_PORTACTIVE(tp_next)) {
 1143                 rval = tp_next;
 1144                 goto found;
 1145         }
 1146 
 1147  search:
 1148         SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
 1149                 if (TRUNK_PORTACTIVE(tp_next)) {
 1150                         rval = tp_next;
 1151                         goto found;
 1152                 }
 1153         }
 1154 
 1155  found:
 1156         if (rval != NULL) {
 1157                 /*
 1158                  * The IEEE 802.1D standard assumes that a trunk with
 1159                  * multiple ports is always full duplex. This is valid
 1160                  * for load sharing trunks and if at least two links
 1161                  * are active. Unfortunately, checking the latter would
 1162                  * be too expensive at this point.
 1163                  */
 1164                 if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
 1165                     (tr->tr_count > 1))
 1166                         new_link = LINK_STATE_FULL_DUPLEX;
 1167                 else
 1168                         new_link = rval->tp_link_state;
 1169         }
 1170 
 1171         if (tr->tr_ac.ac_if.if_link_state != new_link) {
 1172                 tr->tr_ac.ac_if.if_link_state = new_link;
 1173                 if_link_state_change(&tr->tr_ac.ac_if);
 1174         }
 1175 
 1176         return (rval);
 1177 }
 1178 
 1179 /*
 1180  * Simple round robin trunking
 1181  */
 1182 
 1183 int
 1184 trunk_rr_attach(struct trunk_softc *tr)
 1185 {
 1186         struct trunk_port *tp;
 1187 
 1188         tr->tr_detach = trunk_rr_detach;
 1189         tr->tr_start = trunk_rr_start;
 1190         tr->tr_input = trunk_rr_input;
 1191         tr->tr_init = NULL;
 1192         tr->tr_stop = NULL;
 1193         tr->tr_port_create = NULL;
 1194         tr->tr_port_destroy = trunk_rr_port_destroy;
 1195         tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
 1196 
 1197         tp = SLIST_FIRST(&tr->tr_ports);
 1198         tr->tr_psc = (caddr_t)tp;
 1199 
 1200         return (0);
 1201 }
 1202 
 1203 int
 1204 trunk_rr_detach(struct trunk_softc *tr)
 1205 {
 1206         tr->tr_psc = NULL;
 1207         return (0);
 1208 }
 1209 
 1210 void
 1211 trunk_rr_port_destroy(struct trunk_port *tp)
 1212 {
 1213         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
 1214 
 1215         if (tp == (struct trunk_port *)tr->tr_psc)
 1216                 tr->tr_psc = NULL;
 1217 }
 1218 
 1219 int
 1220 trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
 1221 {
 1222         struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
 1223         int error = 0;
 1224 
 1225         if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL)
 1226                 return (ENOENT);
 1227 
 1228         /* Send mbuf */
 1229         if ((error = trunk_enqueue(tp->tp_if, m)) != 0)
 1230                 return (error);
 1231 
 1232         /* Get next active port */
 1233         tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
 1234         tr->tr_psc = (caddr_t)tp_next;
 1235 
 1236         return (0);
 1237 }
 1238 
 1239 int
 1240 trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp,
 1241     struct ether_header *eh, struct mbuf *m)
 1242 {
 1243         struct ifnet *ifp = &tr->tr_ac.ac_if;
 1244 
 1245         /* Just pass in the packet to our trunk device */
 1246         m->m_pkthdr.rcvif = ifp;
 1247 
 1248         return (0);
 1249 }
 1250 
 1251 /*
 1252  * Active failover
 1253  */
 1254 
 1255 int
 1256 trunk_fail_attach(struct trunk_softc *tr)
 1257 {
 1258         tr->tr_detach = trunk_fail_detach;
 1259         tr->tr_start = trunk_fail_start;
 1260         tr->tr_input = trunk_fail_input;
 1261         tr->tr_init = NULL;
 1262         tr->tr_stop = NULL;
 1263         tr->tr_port_create = NULL;
 1264         tr->tr_port_destroy = NULL;
 1265         tr->tr_linkstate = NULL;
 1266 
 1267         return (0);
 1268 }
 1269 
 1270 int
 1271 trunk_fail_detach(struct trunk_softc *tr)
 1272 {
 1273         return (0);
 1274 }
 1275 
 1276 int
 1277 trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
 1278 {
 1279         struct trunk_port *tp;
 1280 
 1281         /* Use the master port if active or the next available port */
 1282         if ((tp = trunk_link_active(tr, tr->tr_primary)) == NULL)
 1283                 return (ENOENT);
 1284 
 1285         /* Send mbuf */
 1286         return (trunk_enqueue(tp->tp_if, m));
 1287 }
 1288 
 1289 int
 1290 trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp,
 1291     struct ether_header *eh, struct mbuf *m)
 1292 {
 1293         struct ifnet *ifp = &tr->tr_ac.ac_if;
 1294         struct trunk_port *tmp_tp;
 1295 
 1296         if (tp == tr->tr_primary) {
 1297                 m->m_pkthdr.rcvif = ifp;
 1298                 return (0);
 1299         }
 1300 
 1301         if (tr->tr_primary->tp_link_state == LINK_STATE_DOWN) {
 1302                 tmp_tp = trunk_link_active(tr, NULL);
 1303                 /*
 1304                  * If tmp_tp is null, we've recieved a packet when all
 1305                  * our links are down. Weird, but process it anyways.
 1306                  */
 1307                 if ((tmp_tp == NULL || tmp_tp == tp)) {
 1308                         m->m_pkthdr.rcvif = ifp;
 1309                         return (0);
 1310                 }
 1311         }
 1312 
 1313         return (-1);
 1314 }
 1315 
 1316 /*
 1317  * Loadbalancing
 1318  */
 1319 
 1320 int
 1321 trunk_lb_attach(struct trunk_softc *tr)
 1322 {
 1323         struct trunk_lb *lb;
 1324 
 1325         if ((lb = (struct trunk_lb *)malloc(sizeof(struct trunk_lb),
 1326             M_DEVBUF, M_NOWAIT)) == NULL)
 1327                 return (ENOMEM);
 1328         bzero(lb, sizeof(struct trunk_lb));
 1329 
 1330         tr->tr_detach = trunk_lb_detach;
 1331         tr->tr_start = trunk_lb_start;
 1332         tr->tr_input = trunk_lb_input;
 1333         tr->tr_port_create = trunk_lb_port_create;
 1334         tr->tr_port_destroy = trunk_lb_port_destroy;
 1335         tr->tr_linkstate = NULL;
 1336         tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
 1337 
 1338         lb->lb_key = arc4random();
 1339         tr->tr_psc = (caddr_t)lb;
 1340 
 1341         return (0);
 1342 }
 1343 
 1344 int
 1345 trunk_lb_detach(struct trunk_softc *tr)
 1346 {
 1347         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
 1348         if (lb != NULL)
 1349                 free(lb, M_DEVBUF);
 1350         return (0);
 1351 }
 1352 
 1353 int
 1354 trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
 1355 {
 1356         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
 1357         struct trunk_port *tp_next;
 1358         int i = 0;
 1359 
 1360         bzero(&lb->lb_ports, sizeof(lb->lb_ports));
 1361         SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
 1362                 if (tp_next == tp)
 1363                         continue;
 1364                 if (i >= TRUNK_MAX_PORTS)
 1365                         return (EINVAL);
 1366                 if (tr->tr_ifflags & IFF_DEBUG)
 1367                         printf("%s: port %s at index %d\n",
 1368                             tr->tr_ifname, tp_next->tp_ifname, i);
 1369                 lb->lb_ports[i++] = tp_next;
 1370         }
 1371 
 1372         return (0);
 1373 }
 1374 
 1375 int
 1376 trunk_lb_port_create(struct trunk_port *tp)
 1377 {
 1378         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
 1379         return (trunk_lb_porttable(tr, NULL));
 1380 }
 1381 
 1382 void
 1383 trunk_lb_port_destroy(struct trunk_port *tp)
 1384 {
 1385         struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
 1386         trunk_lb_porttable(tr, tp);
 1387 }
 1388 
 1389 const void *
 1390 trunk_lb_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
 1391 {
 1392         if (m->m_pkthdr.len < (off + len)) {
 1393                 return (NULL);
 1394         } else if (m->m_len < (off + len)) {
 1395                 m_copydata(m, off, len, buf);
 1396                 return (buf);
 1397         }
 1398         return (mtod(m, const void *) + off);
 1399 }
 1400 
 1401 int
 1402 trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
 1403 {
 1404         struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
 1405         struct trunk_port *tp = NULL;
 1406         u_int32_t p = 0;
 1407         int idx;
 1408 
 1409         p = trunk_hashmbuf(m, lb->lb_key);
 1410         if ((idx = p % tr->tr_count) >= TRUNK_MAX_PORTS)
 1411                 return (EINVAL);
 1412         tp = lb->lb_ports[idx];
 1413 
 1414         /*
 1415          * Check the port's link state. This will return the next active
 1416          * port if the link is down or the port is NULL.
 1417          */
 1418         if ((tp = trunk_link_active(tr, tp)) == NULL)
 1419                 return (ENOENT);
 1420 
 1421         /* Send mbuf */
 1422         return (trunk_enqueue(tp->tp_if, m));
 1423 }
 1424 
 1425 int
 1426 trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp,
 1427     struct ether_header *eh, struct mbuf *m)
 1428 {
 1429         struct ifnet *ifp = &tr->tr_ac.ac_if;
 1430 
 1431         /* Just pass in the packet to our trunk device */
 1432         m->m_pkthdr.rcvif = ifp;
 1433 
 1434         return (0);
 1435 }

/* [<][>][^][v][top][bottom][index][help] */