On 09/03/16(Wed) 16:26, David Gwynne wrote:
> this is an unfortunately large reworking of vlan(4) to make tx mpsafe

This is great but unfortunately hard to review.

> it also includes the following:
> 
> - moving away from the vlan specific SIOC[SG]ETVLAN ioctls to the
> SIOC[SGD]{VNETID,IFPARENT} ioctls
> [...] 
> - added compat for the legacy ioctls built out of the new ioctls
> [...] 
> - added compat for the old ifconfig options.
> [...] 
> - 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.

I don't like the way it is done.  If you insist for putting a rwlock
then I'd rather see it in the common ioctl path in net/if.c.  This
way you don't need to back your locking for every driver.

I bet that we're going to change our mind about how we're serializing
ioctl(2) code execution in some months?  years?.  So I'd rather keep
the code easy to grok and modify.

> - restrict config changes while IFF_UP

Makes sense.

> - rework multicast group membership handling

This should be kept in sync with the other pseudo-drivers.

> - 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.

If the goal you want to reach is to commit a mpsafe vlan(4) I agree.

But if what you want is peer review, which implies teaching others
about your changes, then I disagree.  Splitting your diff is like
doing the effort yourself to make it easier for others to understand.

Asking for ok on a big diff is like asking me (because nobody else
will look at that) to wrap my head around your 1,3K lines diff.  Would
you do that if I was asking you?  Maybe.  I won't.

> i would welcome tests and eyes.

As I already said you insisted for the parent interface to be name
"ifp0" coherently in carp(4) I think you should do the same here.

> 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