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' ? - "" : 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 */