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

Annotation of sys/net/if_vlan.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: if_vlan.c,v 1.70 2007/06/06 14:05:58 henning Exp $    */
        !             2:
        !             3: /*
        !             4:  * Copyright 1998 Massachusetts Institute of Technology
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software and
        !             7:  * its documentation for any purpose and without fee is hereby
        !             8:  * granted, provided that both the above copyright notice and this
        !             9:  * permission notice appear in all copies, that both the above
        !            10:  * copyright notice and this permission notice appear in all
        !            11:  * supporting documentation, and that the name of M.I.T. not be used
        !            12:  * in advertising or publicity pertaining to distribution of the
        !            13:  * software without specific, written prior permission.  M.I.T. makes
        !            14:  * no representations about the suitability of this software for any
        !            15:  * purpose.  It is provided "as is" without express or implied
        !            16:  * warranty.
        !            17:  *
        !            18:  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
        !            19:  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
        !            20:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
        !            21:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
        !            22:  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
        !            23:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
        !            24:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
        !            25:  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
        !            26:  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
        !            27:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
        !            28:  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            29:  * SUCH DAMAGE.
        !            30:  *
        !            31:  * $FreeBSD: src/sys/net/if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp $
        !            32:  */
        !            33:
        !            34: /*
        !            35:  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
        !            36:  * Might be extended some day to also handle IEEE 802.1p priority
        !            37:  * tagging.  This is sort of sneaky in the implementation, since
        !            38:  * we need to pretend to be enough of an Ethernet implementation
        !            39:  * to make arp work.  The way we do this is by telling everyone
        !            40:  * that we are an Ethernet, and then catch the packets that
        !            41:  * ether_output() left on our output queue when it calls
        !            42:  * if_start(), rewrite them for use by the real outgoing interface,
        !            43:  * and ask it to send them.
        !            44:  *
        !            45:  * Some devices support 802.1Q tag insertion in firmware.  The
        !            46:  * vlan interface behavior changes when the IFCAP_VLAN_HWTAGGING
        !            47:  * capability is set on the parent.  In this case, vlan_start()
        !            48:  * will not modify the ethernet header.
        !            49:  */
        !            50:
        !            51: #include "vlan.h"
        !            52:
        !            53: #include <sys/param.h>
        !            54: #include <sys/kernel.h>
        !            55: #include <sys/malloc.h>
        !            56: #include <sys/mbuf.h>
        !            57: #include <sys/queue.h>
        !            58: #include <sys/socket.h>
        !            59: #include <sys/sockio.h>
        !            60: #include <sys/sysctl.h>
        !            61: #include <sys/systm.h>
        !            62: #include <sys/proc.h>
        !            63:
        !            64: #include "bpfilter.h"
        !            65: #if NBPFILTER > 0
        !            66: #include <net/bpf.h>
        !            67: #endif
        !            68:
        !            69: #include <net/if.h>
        !            70: #include <net/if_dl.h>
        !            71: #include <net/if_types.h>
        !            72:
        !            73: #ifdef INET
        !            74: #include <netinet/in.h>
        !            75: #include <netinet/if_ether.h>
        !            76: #endif
        !            77:
        !            78: #include <net/if_vlan_var.h>
        !            79:
        !            80: extern struct  ifaddr  **ifnet_addrs;
        !            81: u_long vlan_tagmask;
        !            82:
        !            83: #define TAG_HASH_SIZE  32
        !            84: #define TAG_HASH(tag)  (tag & vlan_tagmask)
        !            85: LIST_HEAD(, ifvlan)    *vlan_tagh;
        !            86:
        !            87: void   vlan_start (struct ifnet *ifp);
        !            88: int    vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr);
        !            89: int    vlan_unconfig (struct ifnet *ifp);
        !            90: int    vlan_config (struct ifvlan *, struct ifnet *, u_int16_t);
        !            91: void   vlan_vlandev_state(void *);
        !            92: void   vlanattach (int count);
        !            93: int    vlan_set_promisc (struct ifnet *ifp);
        !            94: int    vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
        !            95: int    vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
        !            96: void   vlan_ether_purgemulti(struct ifvlan *);
        !            97: int    vlan_clone_create(struct if_clone *, int);
        !            98: int    vlan_clone_destroy(struct ifnet *);
        !            99: void   vlan_ifdetach(void *);
        !           100:
        !           101: struct if_clone vlan_cloner =
        !           102:     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
        !           103:
        !           104: /* ARGSUSED */
        !           105: void
        !           106: vlanattach(int count)
        !           107: {
        !           108:        vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask);
        !           109:        if (vlan_tagh == NULL)
        !           110:                panic("vlanattach: hashinit");
        !           111:
        !           112:        if_clone_attach(&vlan_cloner);
        !           113: }
        !           114:
        !           115: int
        !           116: vlan_clone_create(struct if_clone *ifc, int unit)
        !           117: {
        !           118:        struct ifvlan *ifv;
        !           119:        struct ifnet *ifp;
        !           120:
        !           121:        ifv = malloc(sizeof(*ifv), M_DEVBUF, M_NOWAIT);
        !           122:        if (!ifv)
        !           123:                return (ENOMEM);
        !           124:        bzero(ifv, sizeof(*ifv));
        !           125:
        !           126:        LIST_INIT(&ifv->vlan_mc_listhead);
        !           127:        ifp = &ifv->ifv_if;
        !           128:        ifp->if_softc = ifv;
        !           129:        snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
        !           130:            unit);
        !           131:        /* NB: flags are not set here */
        !           132:        /* NB: mtu is not set here */
        !           133:
        !           134:        ifp->if_start = vlan_start;
        !           135:        ifp->if_ioctl = vlan_ioctl;
        !           136:        ifp->if_output = ether_output;
        !           137:        IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
        !           138:        IFQ_SET_READY(&ifp->if_snd);
        !           139:        if_attach(ifp);
        !           140:        ether_ifattach(ifp);
        !           141:        /* Now undo some of the damage... */
        !           142:        ifp->if_type = IFT_L2VLAN;
        !           143:        ifp->if_hdrlen = EVL_ENCAPLEN;
        !           144:
        !           145:        return (0);
        !           146: }
        !           147:
        !           148: int
        !           149: vlan_clone_destroy(struct ifnet *ifp)
        !           150: {
        !           151:        struct ifvlan *ifv = ifp->if_softc;
        !           152:
        !           153:        vlan_unconfig(ifp);
        !           154:        ether_ifdetach(ifp);
        !           155:        if_detach(ifp);
        !           156:
        !           157:        free(ifv, M_DEVBUF);
        !           158:        return (0);
        !           159: }
        !           160:
        !           161: void
        !           162: vlan_ifdetach(void *ptr)
        !           163: {
        !           164:        struct ifvlan *ifv = (struct ifvlan *)ptr;
        !           165:        /*
        !           166:         * Destroy the vlan interface because the parent has been
        !           167:         * detached. Set the dh_cookie to NULL because we're running
        !           168:         * inside of dohooks which is told to disestablish the hook
        !           169:         * for us (otherwise we would kill the TAILQ element...).
        !           170:         */
        !           171:        ifv->dh_cookie = NULL;
        !           172:        vlan_clone_destroy(&ifv->ifv_if);
        !           173: }
        !           174:
        !           175: void
        !           176: vlan_start(struct ifnet *ifp)
        !           177: {
        !           178:        struct ifvlan *ifv;
        !           179:        struct ifnet *p;
        !           180:        struct mbuf *m;
        !           181:        int error;
        !           182:
        !           183:        ifv = ifp->if_softc;
        !           184:        p = ifv->ifv_p;
        !           185:
        !           186:        ifp->if_flags |= IFF_OACTIVE;
        !           187:        for (;;) {
        !           188:                IFQ_DEQUEUE(&ifp->if_snd, m);
        !           189:                if (m == NULL)
        !           190:                        break;
        !           191:
        !           192:                if ((p->if_flags & (IFF_UP|IFF_RUNNING)) !=
        !           193:                    (IFF_UP|IFF_RUNNING)) {
        !           194:                        IF_DROP(&p->if_snd);
        !           195:                                /* XXX stats */
        !           196:                        ifp->if_oerrors++;
        !           197:                        m_freem(m);
        !           198:                        continue;
        !           199:                }
        !           200:
        !           201: #if NBPFILTER > 0
        !           202:                if (ifp->if_bpf)
        !           203:                        bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
        !           204: #endif
        !           205:
        !           206:                /*
        !           207:                 * If the IFCAP_VLAN_HWTAGGING capability is set on the parent,
        !           208:                 * it can do VLAN tag insertion itself and doesn't require us
        !           209:                 * to create a special header for it. In this case, we just pass
        !           210:                 * the packet along. However, we need some way to tell the
        !           211:                 * interface where the packet came from so that it knows how
        !           212:                 * to find the VLAN tag to use, so we set the rcvif in the
        !           213:                 * mbuf header to our ifnet.
        !           214:                 *
        !           215:                 * Note: we also set the M_PROTO1 flag in the mbuf to let
        !           216:                 * the parent driver know that the rcvif pointer is really
        !           217:                 * valid. We need to do this because sometimes mbufs will
        !           218:                 * be allocated by other parts of the system that contain
        !           219:                 * garbage in the rcvif pointer. Using the M_PROTO1 flag
        !           220:                 * lets the driver perform a proper sanity check and avoid
        !           221:                 * following potentially bogus rcvif pointers off into
        !           222:                 * never-never land.
        !           223:                 */
        !           224:                if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
        !           225:                        m->m_pkthdr.rcvif = ifp;
        !           226:                        m->m_flags |= M_PROTO1;
        !           227:                } else {
        !           228:                        struct ether_vlan_header evh;
        !           229:
        !           230:                        m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
        !           231:                        evh.evl_proto = evh.evl_encap_proto;
        !           232:                        evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
        !           233:                        evh.evl_tag = htons(ifv->ifv_tag +
        !           234:                            (ifv->ifv_prio << EVL_PRIO_BITS));
        !           235:
        !           236:                        m_adj(m, ETHER_HDR_LEN);
        !           237:                        M_PREPEND(m, sizeof(evh), M_DONTWAIT);
        !           238:                        if (m == NULL) {
        !           239:                                ifp->if_oerrors++;
        !           240:                                continue;
        !           241:                        }
        !           242:
        !           243:                        m_copyback(m, 0, sizeof(evh), &evh);
        !           244:                }
        !           245:
        !           246:                /*
        !           247:                 * Send it, precisely as ether_output() would have.
        !           248:                 * We are already running at splnet.
        !           249:                 */
        !           250:                p->if_obytes += m->m_pkthdr.len;
        !           251:                if (m->m_flags & M_MCAST)
        !           252:                        p->if_omcasts++;
        !           253:                IFQ_ENQUEUE(&p->if_snd, m, NULL, error);
        !           254:                if (error) {
        !           255:                        /* mbuf is already freed */
        !           256:                        ifp->if_oerrors++;
        !           257:                        continue;
        !           258:                }
        !           259:
        !           260:                ifp->if_opackets++;
        !           261:                if ((p->if_flags & (IFF_RUNNING|IFF_OACTIVE)) == IFF_RUNNING)
        !           262:                        p->if_start(p);
        !           263:        }
        !           264:        ifp->if_flags &= ~IFF_OACTIVE;
        !           265:
        !           266:        return;
        !           267: }
        !           268:
        !           269: /*
        !           270:  * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
        !           271:  */
        !           272: int
        !           273: vlan_input(eh, m)
        !           274:        struct ether_header *eh;
        !           275:        struct mbuf *m;
        !           276: {
        !           277:        struct ifvlan *ifv;
        !           278:        u_int tag;
        !           279:        struct ifnet *ifp = m->m_pkthdr.rcvif;
        !           280:
        !           281:        if (m->m_len < EVL_ENCAPLEN &&
        !           282:            (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
        !           283:                ifp->if_ierrors++;
        !           284:                return (0);
        !           285:        }
        !           286:
        !           287:        tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)));
        !           288:
        !           289:        LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) {
        !           290:                if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag)
        !           291:                        break;
        !           292:        }
        !           293:        if (ifv == NULL)
        !           294:                return (1);
        !           295:
        !           296:        if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
        !           297:            (IFF_UP|IFF_RUNNING)) {
        !           298:                m_freem(m);
        !           299:                return (0);
        !           300:        }
        !           301:
        !           302:        /*
        !           303:         * Having found a valid vlan interface corresponding to
        !           304:         * the given source interface and vlan tag, remove the
        !           305:         * encapsulation, and run the real packet through
        !           306:         * ether_input() a second time (it had better be
        !           307:         * reentrant!).
        !           308:         */
        !           309:        m->m_pkthdr.rcvif = &ifv->ifv_if;
        !           310:        eh->ether_type = mtod(m, u_int16_t *)[1];
        !           311:        m->m_len -= EVL_ENCAPLEN;
        !           312:        m->m_data += EVL_ENCAPLEN;
        !           313:        m->m_pkthdr.len -= EVL_ENCAPLEN;
        !           314:
        !           315: #if NBPFILTER > 0
        !           316:        if (ifv->ifv_if.if_bpf)
        !           317:                bpf_mtap_hdr(ifv->ifv_if.if_bpf, (char *)eh, ETHER_HDR_LEN,
        !           318:                    m, BPF_DIRECTION_IN);
        !           319: #endif
        !           320:        ifv->ifv_if.if_ipackets++;
        !           321:        ether_input(&ifv->ifv_if, eh, m);
        !           322:
        !           323:        return (0);
        !           324: }
        !           325:
        !           326: int
        !           327: vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
        !           328: {
        !           329:        struct ifaddr *ifa1, *ifa2;
        !           330:        struct sockaddr_dl *sdl1, *sdl2;
        !           331:        int s;
        !           332:
        !           333:        if (p->if_type != IFT_ETHER)
        !           334:                return EPROTONOSUPPORT;
        !           335:        if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
        !           336:                return (0);
        !           337:        if (ifv->ifv_p)
        !           338:                return EBUSY;
        !           339:
        !           340:        ifv->ifv_p = p;
        !           341:
        !           342:        if (p->if_capabilities & IFCAP_VLAN_MTU)
        !           343:                ifv->ifv_if.if_mtu = p->if_mtu;
        !           344:        else {
        !           345:                /*
        !           346:                 * This will be incompatible with strict
        !           347:                 * 802.1Q implementations
        !           348:                 */
        !           349:                ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN;
        !           350: #ifdef DIAGNOSTIC
        !           351:                printf("%s: initialized with non-standard mtu %lu (parent %s)\n",
        !           352:                    ifv->ifv_if.if_xname, ifv->ifv_if.if_mtu,
        !           353:                    ifv->ifv_p->if_xname);
        !           354: #endif
        !           355:        }
        !           356:
        !           357:        ifv->ifv_if.if_flags = p->if_flags &
        !           358:            (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
        !           359:
        !           360:        /*
        !           361:         * Inherit the if_type from the parent.  This allows us to
        !           362:         * participate in bridges of that type.
        !           363:         */
        !           364:        ifv->ifv_if.if_type = p->if_type;
        !           365:
        !           366:        /*
        !           367:         * Inherit baudrate from the parent.  An SNMP agent would use this
        !           368:         * information.
        !           369:         */
        !           370:        ifv->ifv_if.if_baudrate = p->if_baudrate;
        !           371:
        !           372:        /*
        !           373:         * If the parent interface can do hardware-assisted
        !           374:         * VLAN encapsulation, then propagate its hardware-
        !           375:         * assisted checksumming flags.
        !           376:         *
        !           377:         * If the card cannot handle hardware tagging, it cannot
        !           378:         * possibly compute the correct checksums for tagged packets.
        !           379:         *
        !           380:         * This brings up another possibility, do cards exist which
        !           381:         * have all of these capabilities but cannot utilize them together?
        !           382:         */
        !           383:        if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
        !           384:                ifv->ifv_if.if_capabilities = p->if_capabilities &
        !           385:                    (IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4|
        !           386:                    IFCAP_CSUM_UDPv4);
        !           387:                /* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */
        !           388:
        !           389:        /*
        !           390:         * Set up our ``Ethernet address'' to reflect the underlying
        !           391:         * physical interface's.
        !           392:         */
        !           393:        ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
        !           394:        ifa2 = ifnet_addrs[p->if_index];
        !           395:        sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
        !           396:        sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
        !           397:        sdl1->sdl_type = IFT_ETHER;
        !           398:        sdl1->sdl_alen = ETHER_ADDR_LEN;
        !           399:        bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
        !           400:        bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
        !           401:
        !           402:        ifv->ifv_tag = tag;
        !           403:        s = splnet();
        !           404:        LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list);
        !           405:
        !           406:        /* Register callback for physical link state changes */
        !           407:        ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
        !           408:            vlan_vlandev_state, ifv);
        !           409:
        !           410:        /* Register callback if parent wants to unregister */
        !           411:        ifv->dh_cookie = hook_establish(p->if_detachhooks, 1,
        !           412:            vlan_ifdetach, ifv);
        !           413:
        !           414:        vlan_vlandev_state(ifv);
        !           415:        splx(s);
        !           416:
        !           417:        return 0;
        !           418: }
        !           419:
        !           420: int
        !           421: vlan_unconfig(struct ifnet *ifp)
        !           422: {
        !           423:        struct ifaddr *ifa;
        !           424:        struct sockaddr_dl *sdl;
        !           425:        struct ifvlan *ifv;
        !           426:        struct ifnet *p;
        !           427:        struct ifreq *ifr, *ifr_p;
        !           428:        int s;
        !           429:
        !           430:        ifv = ifp->if_softc;
        !           431:        p = ifv->ifv_p;
        !           432:        if (p == NULL)
        !           433:                return 0;
        !           434:
        !           435:        ifr = (struct ifreq *)&ifp->if_data;
        !           436:        ifr_p = (struct ifreq *)&ifv->ifv_p->if_data;
        !           437:
        !           438:        s = splnet();
        !           439:        LIST_REMOVE(ifv, ifv_list);
        !           440:        if (ifv->lh_cookie != NULL)
        !           441:                hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
        !           442:        /* The cookie is NULL if disestablished externally */
        !           443:        if (ifv->dh_cookie != NULL)
        !           444:                hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
        !           445:        splx(s);
        !           446:
        !           447:        /*
        !           448:         * Since the interface is being unconfigured, we need to
        !           449:         * empty the list of multicast groups that we may have joined
        !           450:         * while we were alive and remove them from the parent's list
        !           451:         * as well.
        !           452:         */
        !           453:        vlan_ether_purgemulti(ifv);
        !           454:
        !           455:        /* Disconnect from parent. */
        !           456:        ifv->ifv_p = NULL;
        !           457:        ifv->ifv_if.if_mtu = ETHERMTU;
        !           458:
        !           459:        /* Clear our MAC address. */
        !           460:        ifa = ifnet_addrs[ifv->ifv_if.if_index];
        !           461:        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
        !           462:        sdl->sdl_type = IFT_ETHER;
        !           463:        sdl->sdl_alen = ETHER_ADDR_LEN;
        !           464:        bzero(LLADDR(sdl), ETHER_ADDR_LEN);
        !           465:        bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
        !           466:
        !           467:        return 0;
        !           468: }
        !           469:
        !           470: void
        !           471: vlan_vlandev_state(void *v)
        !           472: {
        !           473:        struct ifvlan *ifv = v;
        !           474:
        !           475:        if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state)
        !           476:                return;
        !           477:
        !           478:        ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
        !           479:        ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate;
        !           480:        if_link_state_change(&ifv->ifv_if);
        !           481: }
        !           482:
        !           483: int
        !           484: vlan_set_promisc(struct ifnet *ifp)
        !           485: {
        !           486:        struct ifvlan *ifv = ifp->if_softc;
        !           487:        int error = 0;
        !           488:
        !           489:        if ((ifp->if_flags & IFF_PROMISC) != 0) {
        !           490:                if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
        !           491:                        error = ifpromisc(ifv->ifv_p, 1);
        !           492:                        if (error == 0)
        !           493:                                ifv->ifv_flags |= IFVF_PROMISC;
        !           494:                }
        !           495:        } else {
        !           496:                if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
        !           497:                        error = ifpromisc(ifv->ifv_p, 0);
        !           498:                        if (error == 0)
        !           499:                                ifv->ifv_flags &= ~IFVF_PROMISC;
        !           500:                }
        !           501:        }
        !           502:
        !           503:        return (0);
        !           504: }
        !           505:
        !           506: int
        !           507: vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
        !           508: {
        !           509:        struct proc *p = curproc;       /* XXX */
        !           510:        struct ifaddr *ifa;
        !           511:        struct ifnet *pr;
        !           512:        struct ifreq *ifr;
        !           513:        struct ifvlan *ifv;
        !           514:        struct vlanreq vlr;
        !           515:        int error = 0, p_mtu = 0, s;
        !           516:
        !           517:        ifr = (struct ifreq *)data;
        !           518:        ifa = (struct ifaddr *)data;
        !           519:        ifv = ifp->if_softc;
        !           520:
        !           521:        switch (cmd) {
        !           522:        case SIOCSIFADDR:
        !           523:                if (ifv->ifv_p != NULL) {
        !           524:                        ifp->if_flags |= IFF_UP;
        !           525:
        !           526:                        switch (ifa->ifa_addr->sa_family) {
        !           527: #ifdef INET
        !           528:                        case AF_INET:
        !           529:                                arp_ifinit(&ifv->ifv_ac, ifa);
        !           530:                                break;
        !           531: #endif
        !           532:                        default:
        !           533:                                break;
        !           534:                        }
        !           535:                } else {
        !           536:                        error = EINVAL;
        !           537:                }
        !           538:                break;
        !           539:
        !           540:        case SIOCGIFADDR:
        !           541:                {
        !           542:                        struct sockaddr *sa;
        !           543:
        !           544:                        sa = (struct sockaddr *) &ifr->ifr_data;
        !           545:                        bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
        !           546:                            (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
        !           547:                }
        !           548:                break;
        !           549:
        !           550:        case SIOCSIFMTU:
        !           551:                if (ifv->ifv_p != NULL) {
        !           552:                        if (ifv->ifv_p->if_capabilities & IFCAP_VLAN_MTU)
        !           553:                                p_mtu = ifv->ifv_p->if_mtu;
        !           554:                        else
        !           555:                                p_mtu = ifv->ifv_p->if_mtu - EVL_ENCAPLEN;
        !           556:
        !           557:                        if (ifr->ifr_mtu > p_mtu || ifr->ifr_mtu < ETHERMIN)
        !           558:                                error = EINVAL;
        !           559:                        else
        !           560:                                ifp->if_mtu = ifr->ifr_mtu;
        !           561:                } else
        !           562:                        error = EINVAL;
        !           563:
        !           564:                break;
        !           565:
        !           566:        case SIOCSETVLAN:
        !           567:                if ((error = suser(p, 0)) != 0)
        !           568:                        break;
        !           569:                if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
        !           570:                        break;
        !           571:                if (vlr.vlr_parent[0] == '\0') {
        !           572:                        s = splnet();
        !           573:                        vlan_unconfig(ifp);
        !           574:                        if (ifp->if_flags & IFF_UP)
        !           575:                                if_down(ifp);
        !           576:                        ifp->if_flags &= ~IFF_RUNNING;
        !           577:                        splx(s);
        !           578:                        break;
        !           579:                }
        !           580:                pr = ifunit(vlr.vlr_parent);
        !           581:                if (pr == NULL) {
        !           582:                        error = ENOENT;
        !           583:                        break;
        !           584:                }
        !           585:                /*
        !           586:                 * Don't let the caller set up a VLAN tag with
        !           587:                 * anything except VLID bits.
        !           588:                 */
        !           589:                if (vlr.vlr_tag & ~EVL_VLID_MASK) {
        !           590:                        error = EINVAL;
        !           591:                        break;
        !           592:                }
        !           593:                error = vlan_config(ifv, pr, vlr.vlr_tag);
        !           594:                if (error)
        !           595:                        break;
        !           596:                ifp->if_flags |= IFF_RUNNING;
        !           597:
        !           598:                /* Update promiscuous mode, if necessary. */
        !           599:                vlan_set_promisc(ifp);
        !           600:                break;
        !           601:
        !           602:        case SIOCGETVLAN:
        !           603:                bzero(&vlr, sizeof vlr);
        !           604:                if (ifv->ifv_p) {
        !           605:                        snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
        !           606:                            "%s", ifv->ifv_p->if_xname);
        !           607:                        vlr.vlr_tag = ifv->ifv_tag;
        !           608:                }
        !           609:                error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
        !           610:                break;
        !           611:        case SIOCSETVLANPRIO:
        !           612:                if ((error = suser(p, 0)) != 0)
        !           613:                        break;
        !           614:                if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
        !           615:                        break;
        !           616:                /*
        !           617:                 * Don't let the caller set up a VLAN priority
        !           618:                 * outside the range 0-7
        !           619:                 */
        !           620:                if (vlr.vlr_tag > EVL_PRIO_MAX) {
        !           621:                        error = EINVAL;
        !           622:                        break;
        !           623:                }
        !           624:                ifv->ifv_prio = vlr.vlr_tag;
        !           625:                break;
        !           626:        case SIOCGETVLANPRIO:
        !           627:                bzero(&vlr, sizeof vlr);
        !           628:                if (ifv->ifv_p)
        !           629:                        strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname,
        !           630:                             sizeof(vlr.vlr_parent));
        !           631:                vlr.vlr_tag = ifv->ifv_prio;
        !           632:                error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
        !           633:                break;
        !           634:        case SIOCSIFFLAGS:
        !           635:                /*
        !           636:                 * For promiscuous mode, we enable promiscuous mode on
        !           637:                 * the parent if we need promiscuous on the VLAN interface.
        !           638:                 */
        !           639:                if (ifv->ifv_p != NULL)
        !           640:                        error = vlan_set_promisc(ifp);
        !           641:                break;
        !           642:
        !           643:        case SIOCADDMULTI:
        !           644:                error = (ifv->ifv_p != NULL) ?
        !           645:                    vlan_ether_addmulti(ifv, ifr) : EINVAL;
        !           646:                break;
        !           647:
        !           648:        case SIOCDELMULTI:
        !           649:                error = (ifv->ifv_p != NULL) ?
        !           650:                    vlan_ether_delmulti(ifv, ifr) : EINVAL;
        !           651:                break;
        !           652:        default:
        !           653:                error = EINVAL;
        !           654:        }
        !           655:        return error;
        !           656: }
        !           657:
        !           658:
        !           659: int
        !           660: vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
        !           661: {
        !           662:        struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
        !           663:        struct vlan_mc_entry *mc;
        !           664:        u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
        !           665:        int error;
        !           666:
        !           667:        /* XXX: sa_len is too small for such comparison
        !           668:        if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
        !           669:                return (EINVAL);
        !           670:        */
        !           671:
        !           672:        error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
        !           673:        if (error != ENETRESET)
        !           674:                return (error);
        !           675:
        !           676:        /*
        !           677:         * This is new multicast address.  We have to tell parent
        !           678:         * about it.  Also, remember this multicast address so that
        !           679:         * we can delete them on unconfigure.
        !           680:         */
        !           681:        MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
        !           682:            M_DEVBUF, M_NOWAIT);
        !           683:        if (mc == NULL) {
        !           684:                error = ENOMEM;
        !           685:                goto alloc_failed;
        !           686:        }
        !           687:
        !           688:        /*
        !           689:         * As ether_addmulti() returns ENETRESET, following two
        !           690:         * statement shouldn't fail.
        !           691:         */
        !           692:        (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
        !           693:        ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm);
        !           694:        memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
        !           695:        LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
        !           696:
        !           697:        error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr);
        !           698:        if (error != 0)
        !           699:                goto ioctl_failed;
        !           700:
        !           701:        return (error);
        !           702:
        !           703:  ioctl_failed:
        !           704:        LIST_REMOVE(mc, mc_entries);
        !           705:        FREE(mc, M_DEVBUF);
        !           706:  alloc_failed:
        !           707:        (void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
        !           708:
        !           709:        return (error);
        !           710: }
        !           711:
        !           712: int
        !           713: vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
        !           714: {
        !           715:        struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
        !           716:        struct ether_multi *enm;
        !           717:        struct vlan_mc_entry *mc;
        !           718:        u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
        !           719:        int error;
        !           720:
        !           721:        /*
        !           722:         * Find a key to lookup vlan_mc_entry.  We have to do this
        !           723:         * before calling ether_delmulti for obvious reason.
        !           724:         */
        !           725:        if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
        !           726:                return (error);
        !           727:        ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm);
        !           728:        if (enm == NULL)
        !           729:                return (EINVAL);
        !           730:
        !           731:        LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries)
        !           732:                if (mc->mc_enm == enm)
        !           733:                        break;
        !           734:
        !           735:        /* We won't delete entries we didn't add */
        !           736:        if (mc == NULL)
        !           737:                return (EINVAL);
        !           738:
        !           739:        error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
        !           740:        if (error != ENETRESET)
        !           741:                return (error);
        !           742:
        !           743:        /* We no longer use this multicast address.  Tell parent so. */
        !           744:        error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
        !           745:        if (error == 0) {
        !           746:                /* And forget about this address. */
        !           747:                LIST_REMOVE(mc, mc_entries);
        !           748:                FREE(mc, M_DEVBUF);
        !           749:        } else
        !           750:                (void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
        !           751:        return (error);
        !           752: }
        !           753:
        !           754: /*
        !           755:  * Delete any multicast address we have asked to add from parent
        !           756:  * interface.  Called when the vlan is being unconfigured.
        !           757:  */
        !           758: void
        !           759: vlan_ether_purgemulti(struct ifvlan *ifv)
        !           760: {
        !           761:        struct ifnet *ifp = ifv->ifv_p;         /* Parent. */
        !           762:        struct vlan_mc_entry *mc;
        !           763:        union {
        !           764:                struct ifreq ifreq;
        !           765:                struct {
        !           766:                        char ifr_name[IFNAMSIZ];
        !           767:                        struct sockaddr_storage ifr_ss;
        !           768:                } ifreq_storage;
        !           769:        } ifreq;
        !           770:        struct ifreq *ifr = &ifreq.ifreq;
        !           771:
        !           772:        memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
        !           773:        while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
        !           774:                memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
        !           775:                (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
        !           776:                LIST_REMOVE(mc, mc_entries);
        !           777:                FREE(mc, M_DEVBUF);
        !           778:        }
        !           779: }

CVSweb