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