[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

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