this is an unfortunately large reworking of vlan(4) to make tx mpsafe

it also includes the following:

- moving away from the vlan specific SIOC[SG]ETVLAN ioctls to the
SIOC[SGD]{VNETID,IFPARENT} ioctls

i did this to make the config transitions more straightforward.
with the new ioctls you either change one thing or the one thing
failed to be changed, making it more obvious what did and didnt
work.

- added compat for the legacy ioctls built out of the new ioctls

you can use an old ifconfig on top of a new kernel and it works
fine. ive been doing this on production firewalls for months now.

- added compat for the old ifconfig options.

the vlan and vlandev are mapped to vnetid and parent respectively,
allowing old configs to still work.

- made the implicit IFF_UP handling like other drivers

other drivers do IFF_UP when you configure an address on the
interface. vlan(4) did it when you configured a vlan/vlandev, but
not when you configured an interface.

- serialise changes affecting the vlan tag hashes used on input.

- restrict config changes while IFF_UP

this means you cant change the vnetid or the parent interface while
the interface is up. there are two reasons for this. firstly, moving
a vlan interface between parents is hard to do cleanly because
moving multicast group memberships is gross. secondly, moving
interfaces between parents or networks without a link state change
seems like incorrect behaviour to me.

in my mind it is best to think of config changes like this as
comparable to unplugging a cable from a switch port and moving it
to another one.

- rework multicast group membership handling

multicast group memberships could be lost before, this tries harder
to keep them.

- make transmit mpsafe

this relied on a lot of fixes on the config side to be safe, but
the changes to vlan_start are hilariously small compared to the
config changes.


it is big, but pulling it apart would be error prone and a long
process to get us to the same place this diff is already at.

i would welcome tests and eyes.

ok?

Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.316
diff -u -p -r1.316 ifconfig.c
--- sbin/ifconfig/ifconfig.c    2 Mar 2016 00:00:16 -0000       1.316
+++ sbin/ifconfig/ifconfig.c    9 Mar 2016 05:47:13 -0000
@@ -181,6 +181,7 @@ void        settunnelinst(const char *, int);
 void   settunnelttl(const char *, int);
 void   setvnetid(const char *, int);
 void   delvnetid(const char *, int);
+void   getvnetid(void);
 void   setifparent(const char *, int);
 void   delifparent(const char *, int);
 void   getifparent(void);
@@ -209,12 +210,8 @@ void       setmpwencap(const char *, int);
 void   setmpwlabel(const char *, const char *);
 void   setmpwneighbor(const char *, int);
 void   setmpwcontrolword(const char *, int);
-void   setvlantag(const char *, int);
-void   setvlandev(const char *, int);
-void   unsetvlandev(const char *, int);
 void   mpe_status(void);
 void   mpw_status(void);
-void   vlan_status(void);
 void   setinstance(const char *, int);
 int    main(int, char *[]);
 int    prefix(void *val, int);
@@ -350,9 +347,9 @@ const struct        cmd {
        { "scan",       NEXTARG0,       0,              setifscan },
        { "broadcast",  NEXTARG,        0,              setifbroadaddr },
        { "prefixlen",  NEXTARG,        0,              setifprefixlen},
-       { "vlan",       NEXTARG,        0,              setvlantag },
-       { "vlandev",    NEXTARG,        0,              setvlandev },
-       { "-vlandev",   1,              0,              unsetvlandev },
+       { "vlan",       NEXTARG,        0,              setvnetid },
+       { "vlandev",    NEXTARG,        0,              setifparent },
+       { "-vlandev",   1,              0,              delifparent },
        { "group",      NEXTARG,        0,              setifgroup },
        { "-group",     NEXTARG,        0,              unsetifgroup },
        { "autoconf",   1,              0,              setautoconf },
@@ -2850,8 +2847,6 @@ phys_status(int force)
 
        if (dstport)
                printf(":%u", ntohs(dstport));
-       if (ioctl(s, SIOCGVNETID, (caddr_t)&ifr) == 0)
-               printf(" vnetid %d", ifr.ifr_vnetid);
        if (ioctl(s, SIOCGLIFPHYTTL, (caddr_t)&ifr) == 0 && ifr.ifr_ttl > 0)
                printf(" ttl %d", ifr.ifr_ttl);
 #ifndef SMALL
@@ -2942,7 +2937,7 @@ status(int link, struct sockaddr_dl *sdl
            if_indextoname(ifrdesc.ifr_index, ifname) != NULL)
                printf("\tpatch: %s\n", ifname);
 #endif
-       vlan_status();
+       getvnetid();
        getifparent();
 #ifndef SMALL
        carp_status();
@@ -3399,6 +3394,24 @@ delvnetid(const char *ignored, int alsoi
 }
 
 void
+getvnetid(void)
+{
+       if (strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)) >=
+           sizeof(ifr.ifr_name))
+               errx(1, "vnetid: name is too long");
+
+       if (ioctl(s, SIOCGVNETID, &ifr) == -1) {
+               if (errno != EADDRNOTAVAIL)
+                       return;
+
+               printf("\tvnetid: none\n");
+               return;
+       }
+
+       printf("\tvnetid: %u\n", ifr.ifr_vnetid);
+}
+
+void
 setifparent(const char *id, int param)
 {
        struct if_parent ifp;
@@ -3628,100 +3641,6 @@ setmpwcontrolword(const char *value, int
                imrsave.imr_flags &= ~IMR_FLAG_CONTROLWORD;
 }
 #endif /* SMALL */
-
-static int __tag = 0;
-static int __have_tag = 0;
-
-void
-vlan_status(void)
-{
-       struct vlanreq vreq;
-
-       bzero((char *)&vreq, sizeof(struct vlanreq));
-       ifr.ifr_data = (caddr_t)&vreq;
-
-       if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
-               return;
-
-       if (vreq.vlr_tag || (vreq.vlr_parent[0] != '\0'))
-               printf("\tvlan: %d parent interface: %s\n",
-                   vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ?
-                   "<none>" : vreq.vlr_parent);
-}
-
-/* ARGSUSED */
-void
-setvlantag(const char *val, int d)
-{
-       u_int16_t tag;
-       struct vlanreq vreq;
-       const char *errmsg = NULL;
-
-       __tag = tag = strtonum(val, 0, 4095, &errmsg);
-       if (errmsg)
-               errx(1, "vlan tag %s: %s", val, errmsg);
-       __have_tag = 1;
-
-       bzero((char *)&vreq, sizeof(struct vlanreq));
-       ifr.ifr_data = (caddr_t)&vreq;
-
-       if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCGETVLAN");
-
-       vreq.vlr_tag = tag;
-
-       if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCSETVLAN");
-}
-
-/* ARGSUSED */
-void
-setvlandev(const char *val, int d)
-{
-       struct vlanreq   vreq;
-       int              tag;
-       size_t           skip;
-       const char      *estr;
-
-       bzero((char *)&vreq, sizeof(struct vlanreq));
-       ifr.ifr_data = (caddr_t)&vreq;
-
-       if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCGETVLAN");
-
-       (void) strlcpy(vreq.vlr_parent, val, sizeof(vreq.vlr_parent));
-
-       if (!__have_tag && vreq.vlr_tag == 0) {
-               skip = strcspn(ifr.ifr_name, "0123456789");
-               tag = strtonum(ifr.ifr_name + skip, 0, 4095, &estr);
-               if (estr != NULL)
-                       errx(1, "invalid vlan tag and device specification");
-               vreq.vlr_tag = tag;
-       } else if (__have_tag)
-               vreq.vlr_tag = __tag;
-
-       if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCSETVLAN");
-}
-
-/* ARGSUSED */
-void
-unsetvlandev(const char *val, int d)
-{
-       struct vlanreq vreq;
-
-       bzero((char *)&vreq, sizeof(struct vlanreq));
-       ifr.ifr_data = (caddr_t)&vreq;
-
-       if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCGETVLAN");
-
-       bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent));
-       vreq.vlr_tag = 0;
-
-       if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1)
-               err(1, "SIOCSETVLAN");
-}
 
 void
 settrunkport(const char *val, int d)
Index: sys/net/if_vlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.152
diff -u -p -r1.152 if_vlan.c
--- sys/net/if_vlan.c   3 Mar 2016 02:53:28 -0000       1.152
+++ sys/net/if_vlan.c   9 Mar 2016 05:47:17 -0000
@@ -78,23 +78,34 @@
 #define TAG_HASH_MASK          (TAG_HASH_SIZE - 1)
 #define TAG_HASH(tag)          (tag & TAG_HASH_MASK)
 SRPL_HEAD(, ifvlan) *vlan_tagh, *svlan_tagh;
-struct rwlock vlan_tagh_lk = RWLOCK_INITIALIZER("vlantag");
+struct rwlock vlan_cfg_lk = RWLOCK_INITIALIZER("vlantag");
 
 int    vlan_input(struct ifnet *, struct mbuf *, void *);
 void   vlan_start(struct ifnet *ifp);
 int    vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
-int    vlan_unconfig(struct ifnet *ifp, struct ifnet *newp);
-int    vlan_config(struct ifvlan *, struct ifnet *, u_int16_t);
-void   vlan_vlandev_state(void *);
+int    vlan_up(struct ifvlan *);
+int    vlan_down(struct ifvlan *);
+int    vlan_p_unconfig(struct ifnet *, struct ifnet *);
+int    vlan_p_config(struct ifvlan *, struct ifnet *);
+void   vlan_link_hook(void *);
+void   vlan_link_state(struct ifvlan *, u_char, uint64_t);
 void   vlanattach(int count);
 int    vlan_set_promisc(struct ifnet *ifp);
-int    vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
-int    vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
-void   vlan_ether_purgemulti(struct ifvlan *);
-void   vlan_ether_resetmulti(struct ifvlan *, struct ifnet *);
+int    vlan_set_vnetid(struct ifvlan *, uint16_t);
+
+int    vlan_set_compat(struct ifnet *, struct ifreq *);
+int    vlan_get_compat(struct ifnet *, struct ifreq *);
+
+int    vlan_multi_add(struct ifvlan *, struct ifreq *);
+int    vlan_multi_del(struct ifvlan *, struct ifreq *);
+void   vlan_multi_apply(struct ifvlan *, struct ifnet *, u_long);
+void   vlan_multi_free(struct ifvlan *);
+
+int    vlan_inuse(uint16_t, unsigned int, uint16_t);
+int    vlan_inuse_locked(uint16_t, unsigned int, uint16_t);
+
 int    vlan_clone_create(struct if_clone *, int);
 int    vlan_clone_destroy(struct ifnet *);
-void   vlan_ifdetach(void *);
 
 struct if_clone vlan_cloner =
     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
@@ -145,10 +156,9 @@ vlan_clone_create(struct if_clone *ifc, 
        LIST_INIT(&ifv->vlan_mc_listhead);
        ifp = &ifv->ifv_if;
        ifp->if_softc = ifv;
-       snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
-           unit);
+       snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
+           ifc->ifc_name, unit);
        /* NB: flags are not set here */
-       /* NB: mtu is not set here */
 
        /* Special handling for the IEEE 802.1ad QinQ variant */
        if (strcmp("svlan", ifc->ifc_name) == 0)
@@ -157,10 +167,12 @@ vlan_clone_create(struct if_clone *ifc, 
                ifv->ifv_type = ETHERTYPE_VLAN;
 
        refcnt_init(&ifv->ifv_refcnt);
-
+       ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
+       ifp->if_xflags = IFXF_MPSAFE;
        ifp->if_start = vlan_start;
        ifp->if_ioctl = vlan_ioctl;
-       IFQ_SET_MAXLEN(&ifp->if_snd, 1);
+       ifp->if_hardmtu = 0xffff;
+       ifp->if_link_state = LINK_STATE_DOWN;
        IFQ_SET_READY(&ifp->if_snd);
        if_attach(ifp);
        ether_ifattach(ifp);
@@ -190,22 +202,18 @@ vlan_clone_destroy(struct ifnet *ifp)
 {
        struct ifvlan   *ifv = ifp->if_softc;
 
-       vlan_unconfig(ifp, NULL);
+       if (ISSET(ifp->if_flags, IFF_RUNNING))
+               vlan_down(ifv);
+
        ether_ifdetach(ifp);
        if_detach(ifp);
        refcnt_finalize(&ifv->ifv_refcnt, "vlanrefs");
+       vlan_multi_free(ifv);
        free(ifv, M_DEVBUF, sizeof(*ifv));
 
        return (0);
 }
 
-void
-vlan_ifdetach(void *ptr)
-{
-       struct ifvlan   *ifv = ptr;
-       vlan_clone_destroy(&ifv->ifv_if);
-}
-
 static inline int
 vlan_mplstunnel(int ifidx)
 {
@@ -227,31 +235,23 @@ vlan_mplstunnel(int ifidx)
 void
 vlan_start(struct ifnet *ifp)
 {
-       struct ifvlan   *ifv;
+       struct ifvlan   *ifv = ifp->if_softc;
        struct ifnet    *p;
        struct mbuf     *m;
        uint8_t          prio;
 
-       ifv = ifp->if_softc;
-       p = ifv->ifv_p;
-
-       for (;;) {
-               IFQ_DEQUEUE(&ifp->if_snd, m);
-               if (m == NULL)
-                       break;
+       p = if_get(ifv->ifv_p);
+       if (p == NULL) {
+               IFQ_PURGE(&ifp->if_snd);
+               return;
+       }
 
+       while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
 #if NBPFILTER > 0
                if (ifp->if_bpf)
                        bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
 #endif /* NBPFILTER > 0 */
 
-               if ((p->if_flags & (IFF_UP|IFF_RUNNING)) !=
-                   (IFF_UP|IFF_RUNNING)) {
-                       ifp->if_oerrors++;
-                       m_freem(m);
-                       continue;
-               }
-
                /* IEEE 802.1p has prio 0 and 1 swapped */
                prio = m->m_pkthdr.pf.prio;
                if (prio <= 1)
@@ -314,7 +314,7 @@ vlan_inject(struct mbuf *m, uint16_t typ
  * vlan_input() returns 1 if it has consumed the packet, 0 otherwise.
  */
 int
-vlan_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
+vlan_input(struct ifnet *p, struct mbuf *m, void *cookie)
 {
        struct ifvlan                   *ifv;
        struct ether_vlan_header        *evl;
@@ -332,9 +332,9 @@ vlan_input(struct ifnet *ifp, struct mbu
                etype = ETHERTYPE_VLAN;
                tagh = vlan_tagh;
        } else if ((etype == ETHERTYPE_VLAN) || (etype == ETHERTYPE_QINQ)) {
-               if (m->m_len < EVL_ENCAPLEN &&
-                   (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
-                       ifp->if_ierrors++;
+               if (m->m_len < sizeof(*evl) &&
+                   (m = m_pullup(m, sizeof(*evl))) == NULL) {
+                       p->if_ierrors++;
                        return (1);
                }
 
@@ -356,13 +356,14 @@ vlan_input(struct ifnet *ifp, struct mbu
 
        list = &tagh[TAG_HASH(tag)];
        SRPL_FOREACH(ifv, list, &i, ifv_list) {
-               if (ifp == ifv->ifv_p && tag == ifv->ifv_tag &&
+               if (p->if_index == ifv->ifv_p &&
+                   tag == ifv->ifv_tag &&
                    etype == ifv->ifv_type)
                        break;
        }
 
        if (ifv == NULL) {
-               ifp->if_noproto++;
+               p->if_noproto++;
                goto drop;
        }
 
@@ -371,18 +372,6 @@ vlan_input(struct ifnet *ifp, struct mbu
                goto drop;
 
        /*
-        * Drop promiscuously received packets if we are not in
-        * promiscuous mode.
-        */
-       if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
-           (ifp->if_flags & IFF_PROMISC) &&
-           (ifv->ifv_if.if_flags & IFF_PROMISC) == 0) {
-               if (bcmp(&ifv->ifv_ac.ac_enaddr, eh->ether_dhost,
-                   ETHER_ADDR_LEN))
-                       goto drop;
-       }
-
-       /*
         * Having found a valid vlan interface corresponding to
         * the given source interface and vlan tag, remove the
         * encapsulation.
@@ -406,47 +395,150 @@ drop:
        return (1);
 }
 
+void
+vlan_p_detach(void *v)
+{
+       struct ifvlan *ifv = v;
+       struct ifnet *ifp = &ifv->ifv_if;
+
+       if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+               (void)vlan_down(ifv);
+               CLR(ifp->if_flags, IFF_UP);
+       }
+
+       ifv->ifv_p = 0;
+}
+
+void
+vlan_link_hook(void *v)
+{
+       struct ifvlan *ifv = v;
+       struct ifnet *p;
+
+       u_char link = LINK_STATE_DOWN;
+       uint64_t baud = 0;
+
+       p = if_get(ifv->ifv_p);
+       if (p != NULL) {
+               link = p->if_link_state;
+               baud = p->if_baudrate;
+       }
+       if_put(p);
+
+       vlan_link_state(ifv, link, baud);
+}
+
+void
+vlan_link_state(struct ifvlan *ifv, u_char link, uint64_t baud)
+{
+       if (ifv->ifv_if.if_link_state == link)
+               return;
+
+       ifv->ifv_if.if_link_state = link;
+       ifv->ifv_if.if_baudrate = baud;
+
+       if_link_state_change(&ifv->ifv_if);
+}
+
 int
-vlan_config(struct ifvlan *ifv, struct ifnet *p, u_int16_t tag)
+vlan_promisc(struct ifvlan *ifv, int promisc)
 {
-       struct sockaddr_dl      *sdl1, *sdl2;
-       SRPL_HEAD(, ifvlan)     *tagh, *list;
-       u_int                    flags;
-
-       if (p->if_type != IFT_ETHER)
-               return EPROTONOSUPPORT;
-       if (ifv->ifv_p == p && ifv->ifv_tag == tag) /* noop */
+       struct ifnet *p;
+       int error = 0;
+
+       if (ifv->ifv_promisc == promisc)
                return (0);
 
-       /* Remember existing interface flags and reset the interface */
-       flags = ifv->ifv_flags;
-       vlan_unconfig(&ifv->ifv_if, p);
-       ifv->ifv_p = p;
-       ifv->ifv_if.if_baudrate = p->if_baudrate;
-
-       if (p->if_capabilities & IFCAP_VLAN_MTU) {
-               ifv->ifv_if.if_mtu = p->if_mtu;
-               ifv->ifv_if.if_hardmtu = p->if_hardmtu;
-       } else {
-               ifv->ifv_if.if_mtu = p->if_mtu - EVL_ENCAPLEN;
-               ifv->ifv_if.if_hardmtu = p->if_hardmtu - EVL_ENCAPLEN;
+       p = if_get(ifv->ifv_p);
+       if (p != NULL) {
+               error = ifpromisc(p, promisc);
+       }
+       if_put(p);
+
+       if (error == 0)
+               ifv->ifv_promisc = promisc;
+
+       return (error);
+}
+
+int
+vlan_parent_config(struct ifvlan *ifv, struct ifnet *p)
+{
+       int error;
+
+       vlan_multi_apply(ifv, p, SIOCADDMULTI);
+
+       if (ifv->ifv_promisc) {
+               error = ifpromisc(p, 1);
+               if (error != 0)
+                       goto delmulti;
+       }
+
+       return (0);
+
+delmulti:
+       vlan_multi_apply(ifv, p, SIOCDELMULTI);
+       return (error);
+}
+
+int
+vlan_up(struct ifvlan *ifv)
+{
+       struct ifnet *ifp = &ifv->ifv_if;
+       struct ifnet *p;
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       int error = 0;
+       u_int hardmtu;
+
+       KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
+
+       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
+       list = &tagh[TAG_HASH(ifv->ifv_tag)];
+
+       if (ifv->ifv_tag == EVL_VLID_NONE)
+               return (EADDRNOTAVAIL);
+
+       p = if_get(ifv->ifv_p);
+       if (p == NULL)
+               return (ENXIO);
+
+       if (p->if_type != IFT_ETHER) {
+               error = EPROTONOSUPPORT;
+               goto put;
        }
 
-       ifv->ifv_if.if_flags = p->if_flags &
-           (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+       hardmtu = p->if_hardmtu;
+       if (!ISSET(p->if_capabilities, IFCAP_VLAN_MTU))
+               hardmtu -= EVL_ENCAPLEN;
 
-       /* Reset promisc mode on the interface and its parent */
-       if (flags & IFVF_PROMISC) {
-               ifv->ifv_if.if_flags |= IFF_PROMISC;
-               vlan_set_promisc(&ifv->ifv_if);
+       if (ifp->if_mtu > hardmtu) {
+               error = ENOBUFS;
+               goto put;
        }
 
+       error = rw_enter(&vlan_cfg_lk, RW_WRITE | RW_INTR);
+       if (error != 0)
+               goto put;
+
+       error = vlan_inuse_locked(ifv->ifv_type, ifv->ifv_p, ifv->ifv_tag);
+       if (error != 0)
+               goto leave;
+
+       error = vlan_parent_config(ifv, p);
+       if (error != 0)
+               goto leave;
+
+       /* commit */
+       ifp->if_hardmtu = hardmtu;
+       ifp->if_flags |= p->if_flags & IFF_SIMPLEX;
+
+       if_setlladdr(ifp, LLADDR(p->if_sadl));
        if (ifv->ifv_type != ETHERTYPE_VLAN) {
                /*
-                * Hardware offload only works with the default VLAN
+                * Hardware offloads only works with the default VLAN
                 * ethernet type (0x8100).
                 */
-               ifv->ifv_if.if_capabilities = 0;
+               ifp->if_capabilities = 0;
        } else if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
                /*
                 * If the parent interface can do hardware-assisted
@@ -456,251 +548,359 @@ vlan_config(struct ifvlan *ifv, struct i
                 * If the card cannot handle hardware tagging, it cannot
                 * possibly compute the correct checksums for tagged packets.
                 */
-               ifv->ifv_if.if_capabilities = p->if_capabilities &
-                   IFCAP_CSUM_MASK;
+               ifp->if_capabilities = p->if_capabilities & IFCAP_CSUM_MASK;
        }
 
-       /*
-        * Set up our ``Ethernet address'' to reflect the underlying
-        * physical interface's.
-        */
-       sdl1 = ifv->ifv_if.if_sadl;
-       sdl2 = p->if_sadl;
-       sdl1->sdl_type = IFT_ETHER;
-       sdl1->sdl_alen = ETHER_ADDR_LEN;
-       bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
-       bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
-
-       ifv->ifv_tag = tag;
-
        /* Register callback for physical link state changes */
        ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
-           vlan_vlandev_state, ifv);
+           vlan_link_hook, ifv);
 
        /* Register callback if parent wants to unregister */
        ifv->dh_cookie = hook_establish(p->if_detachhooks, 0,
-           vlan_ifdetach, ifv);
+           vlan_p_detach, ifv);
 
-       vlan_vlandev_state(ifv);
-
-       /* Change input handler of the physical interface. */
+       SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
        if_ih_insert(p, vlan_input, NULL);
 
-       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
-       list = &tagh[TAG_HASH(tag)];
+       vlan_link_state(ifv, p->if_link_state, p->if_baudrate);
 
-       rw_enter_write(&vlan_tagh_lk);
-       SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
-       rw_exit_write(&vlan_tagh_lk);
+       SET(ifp->if_flags, IFF_RUNNING);
 
-       return (0);
+leave:
+       rw_exit(&vlan_cfg_lk);
+
+put:
+       if_put(p);
+       return (error);
 }
 
 int
-vlan_unconfig(struct ifnet *ifp, struct ifnet *newp)
+vlan_down(struct ifvlan *ifv)
 {
-       struct sockaddr_dl      *sdl;
-       struct ifvlan           *ifv;
-       SRPL_HEAD(, ifvlan)     *tagh, *list;
-       struct ifnet            *p;
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       struct ifnet *ifp = &ifv->ifv_if;
+       struct ifnet *p;
+       int error;
 
-       ifv = ifp->if_softc;
-       if ((p = ifv->ifv_p) == NULL)
-               return 0;
-
-       /* Unset promisc mode on the interface and its parent */
-       if (ifv->ifv_flags & IFVF_PROMISC) {
-               ifp->if_flags &= ~IFF_PROMISC;
-               vlan_set_promisc(ifp);
-       }
+       KASSERT(ISSET(ifp->if_flags, IFF_RUNNING));
 
        tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
        list = &tagh[TAG_HASH(ifv->ifv_tag)];
 
-       rw_enter_write(&vlan_tagh_lk);
-       SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
-       rw_exit_write(&vlan_tagh_lk);
+       error = rw_enter(&vlan_cfg_lk, RW_WRITE | RW_INTR);
+       if (error != 0)
+               return (error);
 
-       /* Restore previous input handler. */
-       if_ih_remove(p, vlan_input, NULL);
+       CLR(ifp->if_flags, IFF_RUNNING);
 
-       hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
-       hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
-       /* Reset link state */
-       if (newp != NULL) {
-               ifp->if_link_state = LINK_STATE_INVALID;
-               if_link_state_change(ifp);
-       }
+       ifq_barrier(&ifp->if_snd);
 
-       /*
-        * Since the interface is being unconfigured, we need to
-        * empty the list of multicast groups that we may have joined
-        * while we were alive and remove them from the parent's list
-        * as well.
-        */
-       vlan_ether_resetmulti(ifv, newp);
+       vlan_link_state(ifv, LINK_STATE_DOWN, 0);
 
-       /* Disconnect from parent. */
-       ifv->ifv_p = NULL;
-       ifv->ifv_if.if_mtu = ETHERMTU;
-       ifv->ifv_if.if_hardmtu = ETHERMTU;
-       ifv->ifv_flags = 0;
-
-       /* Clear our MAC address. */
-       sdl = ifv->ifv_if.if_sadl;
-       sdl->sdl_type = IFT_ETHER;
-       sdl->sdl_alen = ETHER_ADDR_LEN;
-       bzero(LLADDR(sdl), ETHER_ADDR_LEN);
-       bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
+       p = if_get(ifv->ifv_p);
+       if (p != NULL)
+               if_ih_remove(p, vlan_input, NULL);
+       if_put(p);
 
-       return (0);
-}
+       SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
 
-void
-vlan_vlandev_state(void *v)
-{
-       struct ifvlan   *ifv = v;
+       hook_disestablish(p->if_detachhooks, ifv->dh_cookie);
+       hook_disestablish(p->if_linkstatehooks, ifv->lh_cookie);
 
-       if (ifv->ifv_if.if_link_state == ifv->ifv_p->if_link_state)
-               return;
+       ifp->if_capabilities = 0;
 
-       ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state;
-       ifv->ifv_if.if_baudrate = ifv->ifv_p->if_baudrate;
-       if_link_state_change(&ifv->ifv_if);
-}
+       if_setlladdr(ifp, etheranyaddr);
 
-int
-vlan_set_promisc(struct ifnet *ifp)
-{
-       struct ifvlan   *ifv = ifp->if_softc;
-       int              error = 0;
+       CLR(ifp->if_flags, IFF_SIMPLEX);
+
+       ifp->if_hardmtu = 0xffff;
+
+       rw_exit(&vlan_cfg_lk);
 
-       if ((ifp->if_flags & IFF_PROMISC) != 0) {
-               if ((ifv->ifv_flags & IFVF_PROMISC) == 0)
-                       if ((error = ifpromisc(ifv->ifv_p, 1)) == 0)
-                               ifv->ifv_flags |= IFVF_PROMISC;
-       } else {
-               if ((ifv->ifv_flags & IFVF_PROMISC) != 0)
-                       if ((error = ifpromisc(ifv->ifv_p, 0)) == 0)
-                               ifv->ifv_flags &= ~IFVF_PROMISC;
-       }
        return (0);
 }
 
 int
 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
-       struct proc     *p = curproc;   /* XXX */
-       struct ifaddr   *ifa;
-       struct ifnet    *pr;
-       struct ifreq    *ifr;
-       struct ifvlan   *ifv;
-       struct vlanreq   vlr;
-       int              error = 0, s;
-
-       ifr = (struct ifreq *)data;
-       ifa = (struct ifaddr *)data;
-       ifv = ifp->if_softc;
+       struct ifvlan *ifv = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)data;
+       struct if_parent *parent = (struct if_parent *)data;
+       struct ifnet *p;
+       uint16_t tag;
+       int error = 0;
 
        switch (cmd) {
        case SIOCSIFADDR:
-               if (ifv->ifv_p != NULL)
-                       ifp->if_flags |= IFF_UP;
-               else
-                       error = EINVAL;
+               ifp->if_flags |= IFF_UP;
+               /* FALLTHROUGH */
+
+       case SIOCSIFFLAGS:
+               error = vlan_promisc(ifv,
+                   ISSET(ifp->if_flags, IFF_PROMISC) ? 1 : 0);
+               if (error != 0)
+                       break;
+
+               if (ISSET(ifp->if_flags, IFF_UP)) {
+                       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = vlan_up(ifv);
+               } else {
+                       if (ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = vlan_down(ifv);
+               }
                break;
 
-       case SIOCSIFMTU:
-               if (ifv->ifv_p != NULL) {
-                       if (ifr->ifr_mtu < ETHERMIN ||
-                           ifr->ifr_mtu > ifv->ifv_if.if_hardmtu)
-                               error = EINVAL;
-                       else
-                               ifp->if_mtu = ifr->ifr_mtu;
-               } else
+       case SIOCSVNETID:
+               tag = ifr->ifr_vnetid;
+               if (tag == ifv->ifv_tag)
+                       break;
+
+               if (tag < EVL_VLID_MIN || tag > EVL_VLID_MAX) {
                        error = EINVAL;
+                       break;
+               }
 
+               error = vlan_set_vnetid(ifv, tag);
                break;
 
-       case SIOCSETVLAN:
-               if ((error = suser(p, 0)) != 0)
-                       break;
-               if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
+       case SIOCGVNETID:
+               if (ifv->ifv_tag == EVL_VLID_NONE)
+                       error = EADDRNOTAVAIL;
+               else
+                       ifr->ifr_vnetid = (uint32_t)ifv->ifv_tag;
+               break;
+
+       case SIOCDVNETID:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
                        break;
-               if (vlr.vlr_parent[0] == '\0') {
-                       s = splnet();
-                       vlan_unconfig(ifp, NULL);
-                       if (ifp->if_flags & IFF_UP)
-                               if_down(ifp);
-                       ifp->if_flags &= ~IFF_RUNNING;
-                       splx(s);
-                       break;
-               }
-               pr = ifunit(vlr.vlr_parent);
-               if (pr == NULL) {
-                       error = ENOENT;
+               }
+
+               ifv->ifv_tag = 0;
+               break;
+
+       case SIOCSIFPARENT:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
                        break;
                }
-               /*
-                * Don't let the caller set up a VLAN tag with
-                * anything except VLID bits.
-                */
-               if (vlr.vlr_tag & ~EVL_VLID_MASK) {
+
+               p = ifunit(parent->ifp_parent);
+               if (p == NULL) {
                        error = EINVAL;
                        break;
                }
-               error = vlan_config(ifv, pr, vlr.vlr_tag);
-               if (error)
+
+               if (ifv->ifv_p == p->if_index) {
+                       /* nop */
                        break;
-               ifp->if_flags |= IFF_RUNNING;
+               }
 
-               /* Update promiscuous mode, if necessary. */
-               vlan_set_promisc(ifp);
-               break;
-               
-       case SIOCGETVLAN:
-               bzero(&vlr, sizeof vlr);
-               if (ifv->ifv_p) {
-                       snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
-                           "%s", ifv->ifv_p->if_xname);
-                       vlr.vlr_tag = ifv->ifv_tag;
+               if (p->if_type != IFT_ETHER) {
+                       error = EPROTONOSUPPORT;
+                       break;
                }
-               error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
+
+               error = vlan_inuse(ifv->ifv_type, p->if_index, ifv->ifv_tag);
+               if (error != 0)
+                       break;
+
+               ifv->ifv_p = p->if_index;
                break;
-       case SIOCSIFFLAGS:
-               /*
-                * For promiscuous mode, we enable promiscuous mode on
-                * the parent if we need promiscuous on the VLAN interface.
-                */
-               if (ifv->ifv_p != NULL)
-                       error = vlan_set_promisc(ifp);
+
+       case SIOCGIFPARENT:
+               p = if_get(ifv->ifv_p);
+               if (p == NULL)
+                       error = EADDRNOTAVAIL;
+               else {
+                       memcpy(parent->ifp_parent, p->if_xname,
+                           sizeof(parent->ifp_parent));
+               }
+               if_put(p);
                break;
 
+       case SIOCDIFPARENT:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               ifv->ifv_p = 0;
+                break;
+
        case SIOCADDMULTI:
-               error = (ifv->ifv_p != NULL) ?
-                   vlan_ether_addmulti(ifv, ifr) : EINVAL;
+               error = vlan_multi_add(ifv, ifr);
                break;
-
        case SIOCDELMULTI:
-               error = (ifv->ifv_p != NULL) ?
-                   vlan_ether_delmulti(ifv, ifr) : EINVAL;
+               error = vlan_multi_del(ifv, ifr);
+               break;
+
+       case SIOCSETVLAN:
+               error = vlan_set_compat(ifp, ifr);
+               break;
+       case SIOCGETVLAN:
+               error = vlan_get_compat(ifp, ifr);
                break;
+
        default:
-               error = ENOTTY;
+               error = ether_ioctl(ifp, &ifv->ifv_ac, cmd, data);
+               break;
        }
-       return error;
+
+       return (error);
 }
 
+int
+vlan_set_vnetid(struct ifvlan *ifv, uint16_t tag)
+{
+       struct ifnet *ifp = &ifv->ifv_if;
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       int error;
+
+       tagh = ifv->ifv_type == ETHERTYPE_QINQ ?  svlan_tagh : vlan_tagh;
+
+       error = rw_enter(&vlan_cfg_lk, RW_WRITE | RW_INTR);
+       if (error != 0)
+               return (error);
+
+       error = vlan_inuse_locked(ifv->ifv_type, ifv->ifv_p, tag);
+       if (error != 0)
+               goto unlock;
+
+       if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+               u_char link = ifp->if_link_state;
+               uint64_t baud = ifp->if_baudrate;
+
+               if (LINK_STATE_IS_UP(link))
+                       vlan_link_state(ifv, LINK_STATE_DOWN, 0); 
+
+               list = &tagh[TAG_HASH(ifv->ifv_tag)];
+               SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
+
+               ifv->ifv_tag = tag;
+               list = &tagh[TAG_HASH(ifv->ifv_tag)];
+               SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
+
+               if (LINK_STATE_IS_UP(link))
+                       vlan_link_state(ifv, link, baud);
+       } else
+               ifv->ifv_tag = tag;
+
+unlock:
+       rw_exit(&vlan_cfg_lk);
+
+       return (error);
+}
 
 int
-vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
+vlan_set_compat(struct ifnet *ifp, struct ifreq *ifr)
 {
-       struct ifnet            *ifp = ifv->ifv_p;
+       struct vlanreq vlr;
+       struct ifreq req;
+       struct if_parent parent;
+
+       int error;
+
+       error = suser(curproc, 0);
+       if (error != 0)
+               return (error);
+
+       error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
+       if (error != 0)
+               return (error);
+
+       if (vlr.vlr_parent[0] == '\0')
+               return (vlan_ioctl(ifp, SIOCDIFPARENT, (caddr_t)ifr));
+
+       memset(&req, 0, sizeof(req));
+       memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
+       req.ifr_vnetid = vlr.vlr_tag;
+
+       error = vlan_ioctl(ifp, SIOCSVNETID, (caddr_t)&req);
+       if (error != 0)
+               return (error);
+
+       memset(&parent, 0, sizeof(parent));
+       memcpy(parent.ifp_name, ifp->if_xname, sizeof(parent.ifp_name));
+       memcpy(parent.ifp_parent, vlr.vlr_parent, sizeof(parent.ifp_parent));
+       error = vlan_ioctl(ifp, SIOCSIFPARENT, (caddr_t)&parent);
+       if (error != 0)
+               return (error);
+
+       memset(&req, 0, sizeof(req));
+       memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
+       SET(ifp->if_flags, IFF_UP);
+       return (vlan_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&req));
+}
+
+int
+vlan_get_compat(struct ifnet *ifp, struct ifreq *ifr)
+{
+       struct ifvlan *ifv = ifp->if_softc;
+       struct vlanreq vlr;
+       struct ifnet *p;
+
+       memset(&vlr, 0, sizeof(vlr));
+       p = if_get(ifv->ifv_p);
+       if (p != NULL)
+               memcpy(vlr.vlr_parent, p->if_xname, sizeof(vlr.vlr_parent));
+       if_put(p);
+
+       vlr.vlr_tag = ifv->ifv_tag;
+
+       return (copyout(&vlr, ifr->ifr_data, sizeof(vlr)));
+}
+
+/*
+ * do a quick check of up and running vlans for existing configurations.
+ *
+ * NOTE: this does allow the same config on down vlans, but vlan_up()
+ * will catch them.
+ */
+int
+vlan_inuse(uint16_t type, unsigned int ifidx, uint16_t tag)
+{
+       int error = 0;
+
+       error = rw_enter(&vlan_cfg_lk, RW_READ | RW_INTR);
+       if (error != 0)
+               return (error);
+
+       error = vlan_inuse_locked(type, ifidx, tag);
+
+       rw_exit(&vlan_cfg_lk);
+
+       return (error);
+}
+
+int
+vlan_inuse_locked(uint16_t type, unsigned int ifidx, uint16_t tag)
+{
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       struct ifvlan *ifv;
+
+       tagh = type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
+       list = &tagh[TAG_HASH(tag)];
+
+       SRPL_FOREACH_LOCKED(ifv, list, ifv_list) {
+               if (ifv->ifv_tag == tag &&
+                   ifv->ifv_type == type && /* wat */
+                   ifv->ifv_p == ifidx)
+                       return (EADDRINUSE);
+       }
+
+       return (0);
+}
+
+int
+vlan_multi_add(struct ifvlan *ifv, struct ifreq *ifr)
+{
+       struct ifnet            *p;
        struct vlan_mc_entry    *mc;
        u_int8_t                 addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
        int                      error;
 
-       error = ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
+       error = ether_addmulti(ifr, &ifv->ifv_ac);
        if (error != ENETRESET)
                return (error);
 
@@ -723,24 +923,28 @@ vlan_ether_addmulti(struct ifvlan *ifv, 
        memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
        LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
 
-       if ((error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)ifr)) != 0)
+       p = if_get(ifv->ifv_p);
+       error = p == NULL ? 0 : (*p->if_ioctl)(p, SIOCADDMULTI, (caddr_t)ifr);
+       if_put(p);
+
+       if (error != 0)
                goto ioctl_failed;
 
        return (error);
 
  ioctl_failed:
        LIST_REMOVE(mc, mc_entries);
-       free(mc, M_DEVBUF, sizeof *mc);
+       free(mc, M_DEVBUF, sizeof(*mc));
  alloc_failed:
-       (void)ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
+       (void)ether_delmulti(ifr, &ifv->ifv_ac);
 
        return (error);
 }
 
 int
-vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
+vlan_multi_del(struct ifvlan *ifv, struct ifreq *ifr)
 {
-       struct ifnet            *ifp = ifv->ifv_p;
+       struct ifnet            *p;
        struct ether_multi      *enm;
        struct vlan_mc_entry    *mc;
        u_int8_t                 addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
@@ -756,35 +960,42 @@ vlan_ether_delmulti(struct ifvlan *ifv, 
        if (enm == NULL)
                return (EINVAL);
 
-       LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries)
+       LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
                if (mc->mc_enm == enm)
                        break;
+       }
 
        /* We won't delete entries we didn't add */
        if (mc == NULL)
                return (EINVAL);
 
-       if ((error = ether_delmulti(ifr, (struct arpcom *)&ifv->ifv_ac)) != 0)
+       error = ether_delmulti(ifr, &ifv->ifv_ac);
+       if (error != ENETRESET)
+               return (error);
+
+       if (!ISSET(ifv->ifv_if.if_flags, IFF_RUNNING))
+               goto forget;
+
+       p = if_get(ifv->ifv_p);
+       error = p == NULL ? 0 : (*p->if_ioctl)(p, SIOCDELMULTI, (caddr_t)ifr);
+       if_put(p);
+
+       if (error != 0) {
+               (void)ether_addmulti(ifr, &ifv->ifv_ac);
                return (error);
+       }
 
-       /* We no longer use this multicast address.  Tell parent so. */
-       if ((error = (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr)) != 0) {
-               /* And forget about this address. */
-               LIST_REMOVE(mc, mc_entries);
-               free(mc, M_DEVBUF, sizeof *mc);
-       } else
-               (void)ether_addmulti(ifr, (struct arpcom *)&ifv->ifv_ac);
-       return (error);
+forget:
+       /* forget about this address */
+       LIST_REMOVE(mc, mc_entries);
+       free(mc, M_DEVBUF, sizeof(*mc));
+
+       return (0);
 }
 
-/*
- * Delete any multicast address we have asked to add from parent
- * interface.  Called when the vlan is being unconfigured.
- */
 void
-vlan_ether_purgemulti(struct ifvlan *ifv)
+vlan_multi_apply(struct ifvlan *ifv, struct ifnet *p, u_long cmd)
 {
-       struct ifnet            *ifp = ifv->ifv_p;
        struct vlan_mc_entry    *mc;
        union {
                struct ifreq ifreq;
@@ -795,44 +1006,21 @@ vlan_ether_purgemulti(struct ifvlan *ifv
        } ifreq;
        struct ifreq    *ifr = &ifreq.ifreq;
 
-       memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
-       while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
+       memcpy(ifr->ifr_name, p->if_xname, IFNAMSIZ);
+       LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
                memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
-               (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
-               LIST_REMOVE(mc, mc_entries);
-               free(mc, M_DEVBUF, sizeof *mc);
+
+               (void)(*p->if_ioctl)(p, cmd, (caddr_t)ifr);
        }
 }
 
 void
-vlan_ether_resetmulti(struct ifvlan *ifv, struct ifnet *p)
+vlan_multi_free(struct ifvlan *ifv)
 {
-       struct ifnet            *ifp = ifv->ifv_p;
        struct vlan_mc_entry    *mc;
-       union {
-               struct ifreq ifreq;
-               struct {
-                       char                    ifr_name[IFNAMSIZ];
-                       struct sockaddr_storage ifr_ss;
-               } ifreq_storage;
-       } ifreq;
-       struct ifreq    *ifr = &ifreq.ifreq;
 
-       if (p == NULL) {
-               vlan_ether_purgemulti(ifv);
-               return;
-       } else if (ifp == p)
-               return;
-
-       LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
-               memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
-       
-               /* Remove from the old parent */
-               memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
-               (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
-
-               /* Try to add to the new parent */
-               memcpy(ifr->ifr_name, p->if_xname, IFNAMSIZ);
-               (void)(*p->if_ioctl)(p, SIOCADDMULTI, (caddr_t)ifr);
+       while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
+               LIST_REMOVE(mc, mc_entries);
+               free(mc, M_DEVBUF, sizeof(*mc));
        }
 }
Index: sys/net/if_vlan_var.h
===================================================================
RCS file: /cvs/src/sys/net/if_vlan_var.h,v
retrieving revision 1.32
diff -u -p -r1.32 if_vlan_var.h
--- sys/net/if_vlan_var.h       3 Mar 2016 09:27:51 -0000       1.32
+++ sys/net/if_vlan_var.h       9 Mar 2016 05:47:17 -0000
@@ -42,7 +42,11 @@ struct       ether_vlan_header {
        u_int16_t evl_proto;
 };
 
-#define        EVL_VLID_MASK   0x0FFF
+#define        EVL_VLID_MASK   0xFFF
+#define        EVL_VLID_NONE   0x000
+/* 0x000 and 0xFFF are reserved */
+#define        EVL_VLID_MIN    0x001
+#define        EVL_VLID_MAX    0xFFE
 #define        EVL_VLANOFTAG(tag) ((tag) & EVL_VLID_MASK)
 #define        EVL_PRIOFTAG(tag) (((tag) >> EVL_PRIO_BITS) & 7)
 #define        EVL_ENCAPLEN    4       /* length in octets of encapsulation */
@@ -76,28 +80,24 @@ struct vlan_mc_entry {
 
 struct ifvlan {
        struct  arpcom ifv_ac;  /* make this an interface */
-       struct  ifnet *ifv_p;   /* parent interface of this vlan */
+       unsigned int ifv_p;     /* parent interface of this vlan */
        struct  ifv_linkmib {
-               int     ifvm_parent;
-               u_int16_t ifvm_proto; /* encapsulation ethertype */
                u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
                u_int16_t ifvm_prio; /* prio to apply on packet leaving if */
                u_int16_t ifvm_type; /* non-standard ethertype or 0x8100 */
        }       ifv_mib;
        LIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
        SRPL_ENTRY(ifvlan) ifv_list;
-       int ifv_flags;
+       int ifv_promisc;
        struct refcnt ifv_refcnt;
        void *lh_cookie;
        void *dh_cookie;
-       struct ifih *ifv_ifih;
 };
 
 #define        ifv_if          ifv_ac.ac_if
 #define        ifv_tag         ifv_mib.ifvm_tag
 #define        ifv_prio        ifv_mib.ifvm_prio
 #define        ifv_type        ifv_mib.ifvm_type
-#define        IFVF_PROMISC    0x01
 
 struct mbuf    *vlan_inject(struct mbuf *, uint16_t, uint16_t);
 #endif /* _KERNEL */

Reply via email to