[BACK]Return to if_trunk.c CVS log [TXT][DIR] Up to [local] / sys / net

Annotation of sys/net/if_trunk.c, Revision 1.1.1.1

1.1       nbrk        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: }

CVSweb