vlan config is now done via the vnet and parent ioctls. the idea
is config can be set up while the vlan interface is down, and then
"committed' when it is brought up and running.

vlan up does checks of the vlan state against the target parent,
applies any config necessary to the parent, and then configures the
vlan interface to handle packets.

vlan down just goes in reverse.

a side effect of this is we can mark the driver as mpsafe.

tests? ok?

Index: if_vlan.c
===================================================================
RCS file: /cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.158
diff -u -p -r1.158 if_vlan.c
--- if_vlan.c   29 Mar 2016 13:55:42 -0000      1.158
+++ if_vlan.c   4 Apr 2016 13:00:38 -0000
@@ -80,23 +80,36 @@
 SRPL_HEAD(, ifvlan) *vlan_tagh, *svlan_tagh;
 struct rwlock vlan_tagh_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 *);
 void   vlanattach(int count);
-int    vlan_set_promisc(struct ifnet *ifp);
 int    vlan_clone_create(struct if_clone *, int);
 int    vlan_clone_destroy(struct ifnet *);
-void   vlan_ifdetach(void *);
+
+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_up(struct ifvlan *);
+int    vlan_parent_up(struct ifvlan *, struct ifnet *);
+int    vlan_down(struct ifvlan *);
+
+int    vlan_promisc(struct ifvlan *, int);
+
+void   vlan_ifp0_detach_hook(void *);
+void   vlan_link_hook(void *);
+void   vlan_link_state(struct ifvlan *, u_char, u_int64_t);
+
+int    vlan_set_vnetid(struct ifvlan *, uint16_t);
+int    vlan_inuse(uint16_t, unsigned int, uint16_t);
+int    vlan_inuse_locked(uint16_t, unsigned int, uint16_t);
 
 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_set_compat(struct ifnet *, struct ifreq *);
+int    vlan_get_compat(struct ifnet *, struct ifreq *);
+
 struct if_clone vlan_cloner =
     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
 struct if_clone svlan_cloner =
@@ -159,9 +172,12 @@ vlan_clone_create(struct if_clone *ifc, 
 
        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 = ~0;
+       ifp->if_link_state = LINK_STATE_DOWN;
        IFQ_SET_READY(&ifp->if_snd);
        if_attach(ifp);
        ether_ifattach(ifp);
@@ -191,7 +207,9 @@ 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");
@@ -201,13 +219,6 @@ vlan_clone_destroy(struct ifnet *ifp)
        return (0);
 }
 
-void
-vlan_ifdetach(void *ptr)
-{
-       struct ifvlan   *ifv = ptr;
-       vlan_clone_destroy(&ifv->ifv_if);
-}
-
 static inline int
 vlan_mplstunnel(int ifidx)
 {
@@ -399,47 +410,82 @@ drop:
 }
 
 int
-vlan_config(struct ifvlan *ifv, struct ifnet *ifp0, u_int16_t tag)
+vlan_parent_up(struct ifvlan *ifv, struct ifnet *ifp0)
 {
-       struct sockaddr_dl      *sdl1, *sdl2;
-       SRPL_HEAD(, ifvlan)     *tagh, *list;
-       u_int                    flags;
-
-       if (ifp0->if_type != IFT_ETHER)
-               return EPROTONOSUPPORT;
-       if (ifp0->if_index == ifv->ifv_p && ifv->ifv_tag == tag) /* noop */
-               return (0);
+       int error;
 
-       /* Remember existing interface flags and reset the interface */
-       flags = ifv->ifv_flags;
-       vlan_unconfig(&ifv->ifv_if, ifp0);
-       ifv->ifv_p = ifp0->if_index;
-       ifv->ifv_if.if_baudrate = ifp0->if_baudrate;
-
-       if (ifp0->if_capabilities & IFCAP_VLAN_MTU) {
-               ifv->ifv_if.if_mtu = ifp0->if_mtu;
-               ifv->ifv_if.if_hardmtu = ifp0->if_hardmtu;
-       } else {
-               ifv->ifv_if.if_mtu = ifp0->if_mtu - EVL_ENCAPLEN;
-               ifv->ifv_if.if_hardmtu = ifp0->if_hardmtu - EVL_ENCAPLEN;
+        /* Register callback for physical link state changes */
+       ifv->lh_cookie = hook_establish(ifp0->if_linkstatehooks, 1,
+           vlan_link_hook, ifv);
+
+       /* Register callback if parent wants to unregister */
+       ifv->dh_cookie = hook_establish(ifp0->if_detachhooks, 0,
+           vlan_ifp0_detach_hook, ifv);
+
+       vlan_multi_apply(ifv, ifp0, SIOCADDMULTI);
+
+       if (ISSET(ifv->ifv_flags, IFVF_PROMISC)) {
+               error = ifpromisc(ifp0, 1);
+               if (error != 0)
+                       goto delmulti;
        }
 
-       ifv->ifv_if.if_flags = ifp0->if_flags &
-           (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+       if_ih_insert(ifp0, vlan_input, NULL);
 
-       /* 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);
+       return (0);
+
+delmulti:
+       vlan_multi_apply(ifv, ifp0, SIOCDELMULTI);
+       hook_disestablish(ifp0->if_detachhooks, ifv->dh_cookie);
+       hook_disestablish(ifp0->if_linkstatehooks, ifv->lh_cookie);
+       return (error);
+}
+
+int
+vlan_up(struct ifvlan *ifv)
+{
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       struct ifnet *ifp = &ifv->ifv_if;
+       struct ifnet *ifp0;
+       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)];
+
+       ifp0 = if_get(ifv->ifv_p);
+       if (ifp0 == NULL)
+               return (ENXIO);
+
+       /* check vlan will work on top of the parent */
+       if (ifp0->if_type != IFT_ETHER) {
+               error = EPROTONOSUPPORT;
+               goto put;
        }
 
+       hardmtu = ifp0->if_hardmtu;
+       if (!ISSET(ifp0->if_capabilities, IFCAP_VLAN_MTU))
+               hardmtu -= EVL_ENCAPLEN;
+
+       if (ifp->if_mtu > hardmtu) {
+               error = ENOBUFS;
+               goto put;
+       }
+
+       /* parent is fine, let's prepare the ifv to handle packets */
+       ifp->if_hardmtu = hardmtu;
+       SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
+       if_setlladdr(ifp, LLADDR(ifp0->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;
-       } else if (ifp0->if_capabilities & IFCAP_VLAN_HWTAGGING) {
+               ifp->if_capabilities = 0;
+       } else if (ISSET(ifp0->if_capabilities, IFCAP_VLAN_HWTAGGING)) {
                /*
                 * If the parent interface can do hardware-assisted
                 * VLAN encapsulation, then propagate its hardware-
@@ -448,266 +494,424 @@ 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 = ifp0->if_capabilities &
-                   IFCAP_CSUM_MASK;
+               ifp->if_capabilities = ifp0->if_capabilities & IFCAP_CSUM_MASK;
        }
 
-       /*
-        * Set up our ``Ethernet address'' to reflect the underlying
-        * physical interface's.
-        */
-       sdl1 = ifv->ifv_if.if_sadl;
-       sdl2 = ifp0->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;
+       /* commit the ifv */
+       error = rw_enter(&vlan_tagh_lk, RW_WRITE | RW_INTR);
+       if (error != 0)
+               goto scrub;
 
-       /* Register callback for physical link state changes */
-       ifv->lh_cookie = hook_establish(ifp0->if_linkstatehooks, 1,
-           vlan_vlandev_state, ifv);
-
-       /* Register callback if parent wants to unregister */
-       ifv->dh_cookie = hook_establish(ifp0->if_detachhooks, 0,
-           vlan_ifdetach, ifv);
+       error = vlan_inuse_locked(ifv->ifv_type, ifv->ifv_p, ifv->ifv_tag);
+       if (error != 0)
+               goto leave;
 
-       vlan_multi_apply(ifv, ifp0, SIOCADDMULTI);
+       SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
+       rw_exit(&vlan_tagh_lk);
 
-       vlan_vlandev_state(ifv);
+       /* configure the parent to handle packets for this vlan */
+       error = vlan_parent_up(ifv, ifp0);
+       if (error != 0)
+               goto remove;
+
+       /* we're running now */
+       SET(ifp->if_flags, IFF_RUNNING);
+       vlan_link_state(ifv, ifp0->if_link_state, ifp0->if_baudrate);
 
-       /* Change input handler of the physical interface. */
-       if_ih_insert(ifp0, vlan_input, NULL);
+       if_put(ifp0);
 
-       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
-       list = &tagh[TAG_HASH(tag)];
+       return (0);
 
-       rw_enter_write(&vlan_tagh_lk);
-       SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
-       rw_exit_write(&vlan_tagh_lk);
+remove:
+       rw_enter(&vlan_tagh_lk, RW_WRITE);
+       SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
+leave:
+       rw_exit(&vlan_tagh_lk);
+scrub:
+       ifp->if_capabilities = 0;
+       if_setlladdr(ifp, etheranyaddr);
+       CLR(ifp->if_flags, IFF_SIMPLEX);
+       ifp->if_hardmtu = ~0;
+put:
+       if_put(ifp0);
 
-       return (0);
+       return (error);
 }
 
 int
-vlan_unconfig(struct ifnet *ifp, struct ifnet *newifp0)
+vlan_down(struct ifvlan *ifv)
 {
-       struct sockaddr_dl      *sdl;
-       struct ifvlan           *ifv;
-       SRPL_HEAD(, ifvlan)     *tagh, *list;
-       struct ifnet            *ifp0;
-
-       ifv = ifp->if_softc;
-       ifp0 = if_get(ifv->ifv_p);
-       if (ifp0 == NULL)
-               goto disconnect;
-
-       /* Unset promisc mode on the interface and its parent */
-       if (ifv->ifv_flags & IFVF_PROMISC) {
-               ifp->if_flags &= ~IFF_PROMISC;
-               vlan_set_promisc(ifp);
-       }
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       struct ifnet *ifp = &ifv->ifv_if;
+       struct ifnet *ifp0;
 
        tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
        list = &tagh[TAG_HASH(ifv->ifv_tag)];
 
+       KASSERT(ISSET(ifp->if_flags, IFF_RUNNING));
+
+       vlan_link_state(ifv, LINK_STATE_DOWN, 0);
+       CLR(ifp->if_flags, IFF_RUNNING);
+
+       ifq_barrier(&ifp->if_snd);
+
+       ifp0 = if_get(ifv->ifv_p);
+       if (ifp0 != NULL) {
+               if_ih_remove(ifp0, vlan_input, NULL);
+               if (ISSET(ifv->ifv_flags, IFVF_PROMISC))
+                       ifpromisc(ifp0, 0);
+               vlan_multi_apply(ifv, ifp0, SIOCDELMULTI);
+               hook_disestablish(ifp0->if_detachhooks, ifv->dh_cookie);
+               hook_disestablish(ifp0->if_linkstatehooks, ifv->lh_cookie);
+       }
+       if_put(ifp0);
+
        rw_enter_write(&vlan_tagh_lk);
        SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
        rw_exit_write(&vlan_tagh_lk);
 
-       /* Restore previous input handler. */
-       if_ih_remove(ifp0, vlan_input, NULL);
-
-       hook_disestablish(ifp0->if_linkstatehooks, ifv->lh_cookie);
-       hook_disestablish(ifp0->if_detachhooks, ifv->dh_cookie);
-       /* Reset link state */
-       if (newifp0 != NULL) {
-               ifp->if_link_state = LINK_STATE_INVALID;
-               if_link_state_change(ifp);
-       }
+       ifp->if_capabilities = 0;
+       if_setlladdr(ifp, etheranyaddr);
+       CLR(ifp->if_flags, IFF_SIMPLEX);
+       ifp->if_hardmtu = ~0;
 
-       /*
-        * 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_multi_apply(ifv, ifp0, SIOCDELMULTI);
+       return (0);
+}
 
-disconnect:
-       /* Disconnect from parent. */
-       ifv->ifv_p = 0;
-       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);
+void
+vlan_ifp0_detach_hook(void *v)
+{
+       struct ifvlan *ifv = v;
+       struct ifnet *ifp = &ifv->ifv_if;
 
-       if_put(ifp0);
+       if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+               vlan_down(ifv);
+               CLR(ifp->if_flags, IFF_UP);
+       }
 
-       return (0);
+       ifv->ifv_p = 0;
 }
 
 void
-vlan_vlandev_state(void *v)
+vlan_link_hook(void *v)
 {
-       struct ifvlan   *ifv = v;
-       struct ifnet    *ifp0;
-       int              link_state = LINK_STATE_DOWN;
-       uint64_t         baudrate = 0;
+       struct ifvlan *ifv = v;
+       struct ifnet *ifp0;
+
+       u_char link = LINK_STATE_DOWN;
+       uint64_t baud = 0;
 
        ifp0 = if_get(ifv->ifv_p);
        if (ifp0 != NULL) {
-               link_state = ifp0->if_link_state;
-               baudrate = ifp0->if_baudrate;
+               link = ifp0->if_link_state;
+               baud = ifp0->if_baudrate;
        }
        if_put(ifp0);
 
-       if (ifv->ifv_if.if_link_state == link_state)
+       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_state;
-       ifv->ifv_if.if_baudrate = baudrate;
+       ifv->ifv_if.if_link_state = link;
+       ifv->ifv_if.if_baudrate = baud;
+
        if_link_state_change(&ifv->ifv_if);
 }
 
 int
-vlan_set_promisc(struct ifnet *ifp)
+vlan_promisc(struct ifvlan *ifv, int promisc)
 {
-       struct ifvlan   *ifv = ifp->if_softc;
-       struct ifnet    *ifp0;
-       int              error = 0;
+       struct ifnet *ifp0;
+       int error = 0;
 
-       ifp0 = if_get(ifv->ifv_p);
-       if (ifp0 == NULL)
-               goto leave;
+       if ((ISSET(ifv->ifv_flags, IFVF_PROMISC) ? 1 : 0) == promisc)
+               return (0);
 
-       if ((ifp->if_flags & IFF_PROMISC) != 0) {
-               if ((ifv->ifv_flags & IFVF_PROMISC) == 0)
-                       if ((error = ifpromisc(ifp0, 1)) == 0)
-                               ifv->ifv_flags |= IFVF_PROMISC;
-       } else {
-               if ((ifv->ifv_flags & IFVF_PROMISC) != 0)
-                       if ((error = ifpromisc(ifp0, 0)) == 0)
-                               ifv->ifv_flags &= ~IFVF_PROMISC;
+       ifp0 = if_get(ifv->ifv_p);
+       if (ifp0 != NULL) {
+               error = ifpromisc(ifp0, promisc);
        }
-
-leave:
        if_put(ifp0);
 
-       return (0);
+       if (error == 0) {
+               CLR(ifv->ifv_flags, IFVF_PROMISC);
+               SET(ifv->ifv_flags, promisc ? IFVF_PROMISC : 0);
+       }
+
+       return (error);
 }
 
 int
 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
-       struct proc     *p = curproc;   /* XXX */
-       struct ifaddr   *ifa;
-       struct ifnet    *ifp0;
-       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 *ifp0;
+       uint16_t tag;
+       int error = 0;
 
        switch (cmd) {
        case SIOCSIFADDR:
-               if (ifv->ifv_p != 0)
-                       ifp->if_flags |= IFF_UP;
-               else
-                       error = EINVAL;
-               break;
+               ifp->if_flags |= IFF_UP;
+               /* FALLTHROUGH */
 
-       case SIOCSIFMTU:
-               if (ifv->ifv_p != 0) {
-                       if (ifr->ifr_mtu < ETHERMIN ||
-                           ifr->ifr_mtu > ifv->ifv_if.if_hardmtu)
-                               error = EINVAL;
-                       else
-                               ifp->if_mtu = ifr->ifr_mtu;
-               } else
-                       error = EINVAL;
+       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 SIOCSETVLAN:
-               if ((error = suser(p, 0)) != 0)
+       case SIOCSVNETID:
+               tag = ifr->ifr_vnetid;
+               if (tag == ifv->ifv_tag)
                        break;
-               if ((error = copyin(ifr->ifr_data, &vlr, sizeof vlr)))
+
+               if (tag < EVL_VLID_MIN || tag > EVL_VLID_MAX) {
+                       error = EINVAL;
                        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);
+               }
+
+               error = vlan_set_vnetid(ifv, tag);
+               break;
+
+       case SIOCGVNETID:
+               if (ifv->ifv_tag == EVL_VLID_NULL)
+                       error = EADDRNOTAVAIL;
+               else
+                       ifr->ifr_vnetid = (uint32_t)ifv->ifv_tag;
+               break;
+
+       case SIOCDVNETID:
+               error = vlan_set_vnetid(ifv, 0);
+               break;
+
+       case SIOCSIFPARENT:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
                        break;
                }
-               ifp0 = ifunit(vlr.vlr_parent);
+
+               ifp0 = ifunit(parent->ifp_parent);
                if (ifp0 == NULL) {
-                       error = ENOENT;
+                       error = EINVAL;
                        break;
                }
-               /*
-                * Don't let the caller set up a VLAN tag with
-                * anything except VLID bits.
-                */
-               if (vlr.vlr_tag & ~EVL_VLID_MASK) {
-                       error = EINVAL;
+
+               if (ifv->ifv_p == ifp0->if_index) {
+                       /* nop */
                        break;
                }
-               error = vlan_config(ifv, ifp0, vlr.vlr_tag);
-               if (error)
+
+               if (ifp0->if_type != IFT_ETHER) {
+                       error = EPROTONOSUPPORT;
+                       break;
+               }
+
+               error = vlan_inuse(ifv->ifv_type, ifp0->if_index, ifv->ifv_tag);
+               if (error != 0)
                        break;
-               ifp->if_flags |= IFF_RUNNING;
 
-               /* Update promiscuous mode, if necessary. */
-               vlan_set_promisc(ifp);
+               ifv->ifv_p = ifp0->if_index;
                break;
-               
-       case SIOCGETVLAN:
-               bzero(&vlr, sizeof vlr);
+
+       case SIOCGIFPARENT:
                ifp0 = if_get(ifv->ifv_p);
-               if (ifp0) {
-                       snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
-                           "%s", ifp0->if_xname);
-                       vlr.vlr_tag = ifv->ifv_tag;
+               if (ifp0 == NULL)
+                       error = EADDRNOTAVAIL;
+               else {
+                       memcpy(parent->ifp_parent, ifp0->if_xname,
+                           sizeof(parent->ifp_parent));
                }
                if_put(ifp0);
-               error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
                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 != 0)
-                       error = vlan_set_promisc(ifp);
+
+       case SIOCDIFPARENT:
+               if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+                       error = EBUSY;
+                       break;
+               }
+
+               ifv->ifv_p = 0;
                break;
 
        case SIOCADDMULTI:
                error = vlan_multi_add(ifv, ifr);
                break;
-
        case SIOCDELMULTI:
                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;
 }
 
+int
+vlan_set_vnetid(struct ifvlan *ifv, uint16_t tag)
+{
+       struct ifnet *ifp = &ifv->ifv_if;
+       SRPL_HEAD(, ifvlan) *tagh, *list;
+       u_char link = ifp->if_link_state;
+       uint64_t baud = ifp->if_baudrate;
+       int error;
+
+       tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
+
+       if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
+               vlan_link_state(ifv, LINK_STATE_DOWN, 0);
+
+       error = rw_enter(&vlan_tagh_lk, RW_WRITE);
+       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)) {
+               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);
+       } else
+               ifv->ifv_tag = tag;
+
+unlock:
+       rw_exit(&vlan_tagh_lk);
+
+       if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
+               vlan_link_state(ifv, link, baud);
+
+       return (error);
+}
+
+int
+vlan_set_compat(struct ifnet *ifp, struct ifreq *ifr)
+{
+       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_tagh_lk, RW_READ | RW_INTR);
+       if (error != 0)
+               return (error);
+
+       error = vlan_inuse_locked(type, ifidx, tag);
+
+       rw_exit(&vlan_tagh_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)

Reply via email to