[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     ! 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