Index: sbin/ifconfig/ifconfig.8 =================================================================== --- sbin/ifconfig/ifconfig.8 +++ sbin/ifconfig/ifconfig.8 @@ -592,7 +592,7 @@ not on a .Xr vlan 4 interface itself. -.It Fl vlanmtu , vlanhwtag, vlanhwfilter, vlanhwtso +.It Fl vlanmtu , vlanhwtag , vlanhwfilter , vlanhwtso If the driver offers user-configurable VLAN support, disable reception of extended frames, tag processing in hardware, frame filtering in hardware, or TSO on VLAN, @@ -2690,14 +2690,18 @@ .Pp The following parameters are specific to .Xr vlan 4 +and +.Xr svlan 4 interfaces: .Bl -tag -width indent .It Cm vlan Ar vlan_tag Set the VLAN tag value to .Ar vlan_tag . -This value is a 12-bit VLAN Identifier (VID) which is used to create an 802.1Q -VLAN header for packets sent from the +This value is a 12-bit VLAN Identifier (VID) which is used to create an +802.1Q or 802.1ad VLAN header for packets sent from the .Xr vlan 4 +or +.Xr svlan 4 interface. Note that .Cm vlan @@ -2732,29 +2736,48 @@ .Ar iface with a .Xr vlan 4 +or +.Xr svlan 4 interface. -Packets transmitted through the +.Pp +Packets transmitted through a .Xr vlan 4 interface will be diverted to the specified physical interface .Ar iface with 802.1Q VLAN encapsulation. +Packets transmitted through a +.Xr svlan 4 +interface will be +diverted to the specified physical interface +.Ar iface +with 802.1ad VLAN encapsulation. +.Pp Packets with 802.1Q encapsulation received by the parent interface with the correct VLAN Identifier will be diverted to the associated .Xr vlan 4 pseudo-interface. -The +Packets with 802.1ad encapsulation received +by the parent interface with the correct VLAN Identifier will be diverted to +the associated +.Xr svlan 4 +pseudo-interface. +.Pp .Xr vlan 4 -interface is assigned a +and +.Xr svlan 4 +interfaces are assigned a copy of the parent interface's flags and the parent's Ethernet address. The .Cm vlandev and .Cm vlan must both be set at the same time. -If the +If a .Xr vlan 4 +or +.Xr svlan 4 interface already has a physical interface associated with it, this command will fail. To @@ -2775,9 +2798,13 @@ .It Fl vlandev Op Ar iface If the driver is a .Xr vlan 4 +or +.Xr svlan 4 pseudo device, disassociate the parent interface from it. This breaks the link between the .Xr vlan 4 +or +.Xr svlan 4 interface and its parent, clears its VLAN Identifier, flags and its link address and shuts the interface down. Index: sbin/ifconfig/ifvlan.c =================================================================== --- sbin/ifconfig/ifvlan.c +++ sbin/ifconfig/ifvlan.c @@ -228,4 +228,5 @@ af_register(&af_vlan); callback_register(vlan_cb, NULL); clone_setdefcallback("vlan", vlan_create); + clone_setdefcallback("svlan", vlan_create); } Index: share/man/man4/Makefile =================================================================== --- share/man/man4/Makefile +++ share/man/man4/Makefile @@ -739,7 +739,7 @@ MLINKS+=tun.4 if_tun.4 MLINKS+=ure.4 if_ure.4 MLINKS+=vge.4 if_vge.4 -MLINKS+=vlan.4 if_vlan.4 +MLINKS+=vlan.4 svlan.4 if_vlan.4 if_svlan.4 MLINKS+=vxlan.4 if_vxlan.4 MLINKS+=${_vmx.4} ${_if_vmx.4} MLINKS+=vr.4 if_vr.4 Index: share/man/man4/vlan.4 =================================================================== --- share/man/man4/vlan.4 +++ share/man/man4/vlan.4 @@ -29,8 +29,9 @@ .Dt VLAN 4 .Os .Sh NAME -.Nm vlan -.Nd "IEEE 802.1Q VLAN network interface" +.Nm vlan , +.Nm svlan +.Nd "IEEE 802.1Q/802.1ad VLAN network interfaces" .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your @@ -49,13 +50,17 @@ The .Nm driver demultiplexes frames tagged according to -the IEEE 802.1Q standard into logical +the IEEE 802.1Q and IEEE 802.1ad standards into logical .Nm -network interfaces, which allows routing/bridging between -multiple VLANs through a single switch trunk port. +and +.Nm svlan +network interfaces, respectively, allowing routing/bridging +between multiple VLANs through a single switch trunk port. .Pp Each -.Nm +.Nm vlan +or +.Nm svlan interface is created at runtime using interface cloning. This is most easily done with the @@ -67,12 +72,16 @@ .Xr rc.conf 5 . .Pp To function, a -.Nm +.Nm vlan +or +.Nm svlan interface must be assigned a parent interface and numeric VLAN tag using .Xr ifconfig 8 . A single parent can be assigned to multiple -.Nm +.Nm vlan +or +.Nm svlan interfaces provided they have different tags. The parent interface is likely to be an Ethernet card connected to a properly configured switch port. @@ -152,10 +161,10 @@ However, some lack the capability of transmitting and receiving long frames. Assigning such an interface as the parent to -.Nm -will result in a reduced MTU on the corresponding -.Nm -interfaces. +.Nm vlan +or +.Nm svlan +will result in a reduced MTU on the child interfaces. In the modern Internet, this is likely to cause .Xr tcp 4 connectivity problems due to massive, inadequate Index: sys/net/ethernet.h =================================================================== --- sys/net/ethernet.h +++ sys/net/ethernet.h @@ -89,6 +89,15 @@ uint16_t evl_proto; } __packed; +/* + * 802.1q full tag. + */ +struct ether_8021q_tag { + uint16_t proto; + uint16_t vid; + uint8_t pcp; +}; + #define EVL_VLID_MASK 0x0FFF #define EVL_PRI_MASK 0xE000 #define EVL_VLANOFTAG(tag) ((tag) & EVL_VLID_MASK) @@ -441,11 +450,14 @@ extern char *ether_sprintf(const u_int8_t *); void ether_vlan_mtap(struct bpf_if *, struct mbuf *, void *, u_int); -struct mbuf *ether_vlanencap(struct mbuf *, uint16_t); -bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, - uint16_t vid, uint8_t pcp); +struct mbuf *ether_vlanencap_proto(struct mbuf *, uint16_t, uint16_t); +bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, + struct ifnet *p, struct ether_8021q_tag *); void ether_gen_addr(struct ifnet *ifp, struct ether_addr *hwaddr); +static __inline struct mbuf *ether_vlanencap(struct mbuf *m, uint16_t tag) +{ return ether_vlanencap_proto(m, tag, ETHERTYPE_VLAN); } + /* new ethernet interface attached event */ typedef void (*ether_ifattach_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ether_ifattach_event, ether_ifattach_event_handler_t); Index: sys/net/if_ethersubr.c =================================================================== --- sys/net/if_ethersubr.c +++ sys/net/if_ethersubr.c @@ -441,11 +441,18 @@ static bool ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp) { + struct ether_8021q_tag qtag; struct ether_header *eh; eh = mtod(*mp, struct ether_header *); if (ntohs(eh->ether_type) == ETHERTYPE_VLAN || - ether_8021q_frame(mp, ifp, ifp, 0, pcp)) + ntohs(eh->ether_type) == ETHERTYPE_QINQ) + return (true); + + qtag.vid = 0; + qtag.pcp = pcp; + qtag.proto = ETHERTYPE_VLAN; + if (ether_8021q_frame(mp, ifp, ifp, &qtag)) return (true); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (false); @@ -618,7 +625,8 @@ * path correctly. * TODO: Deal with Q-in-Q frames, but not arbitrary nesting levels. */ - if ((m->m_flags & M_VLANTAG) == 0 && etype == ETHERTYPE_VLAN) { + if ((m->m_flags & M_VLANTAG) == 0 && + ((etype == ETHERTYPE_VLAN) || (etype == ETHERTYPE_QINQ))) { struct ether_vlan_header *evl; if (m->m_len < sizeof(*evl) && @@ -1303,7 +1311,7 @@ } struct mbuf * -ether_vlanencap(struct mbuf *m, uint16_t tag) +ether_vlanencap_proto(struct mbuf *m, uint16_t tag, uint16_t proto) { struct ether_vlan_header *evl; @@ -1325,7 +1333,7 @@ evl = mtod(m, struct ether_vlan_header *); bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); - evl->evl_encap_proto = htons(ETHERTYPE_VLAN); + evl->evl_encap_proto = htons(proto); evl->evl_tag = htons(tag); return (m); } @@ -1354,7 +1362,7 @@ bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, - uint16_t vid, uint8_t pcp) + struct ether_8021q_tag *qtag) { struct m_tag *mtag; int n; @@ -1396,14 +1404,15 @@ */ if (vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL)) != NULL) - tag = EVL_MAKETAG(vid, *(uint8_t *)(mtag + 1), 0); + tag = EVL_MAKETAG(qtag->vid, *(uint8_t *)(mtag + 1), 0); else - tag = EVL_MAKETAG(vid, pcp, 0); - if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { + tag = EVL_MAKETAG(qtag->vid, qtag->pcp, 0); + if ((p->if_capenable & IFCAP_VLAN_HWTAGGING) && + (qtag->proto == ETHERTYPE_VLAN)) { (*mp)->m_pkthdr.ether_vtag = tag; (*mp)->m_flags |= M_VLANTAG; } else { - *mp = ether_vlanencap(*mp, tag); + *mp = ether_vlanencap_proto(*mp, tag, qtag->proto); if (*mp == NULL) { if_printf(ife, "unable to prepend 802.1Q header"); return (false); Index: sys/net/if_vlan.c =================================================================== --- sys/net/if_vlan.c +++ sys/net/if_vlan.c @@ -17,7 +17,7 @@ * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. - * + * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF @@ -185,17 +185,17 @@ struct ifvlantrunk *ifv_trunk; struct ifnet *ifv_ifp; #define TRUNK(ifv) ((ifv)->ifv_trunk) -#define PARENT(ifv) ((ifv)->ifv_trunk->parent) +#define PARENT(ifv) (TRUNK(ifv)->parent) void *ifv_cookie; int ifv_pflags; /* special flags we have set on parent */ int ifv_capenable; int ifv_encaplen; /* encapsulation length */ int ifv_mtufudge; /* MTU fudged by this much */ int ifv_mintu; /* min transmission unit */ - uint16_t ifv_proto; /* encapsulation ethertype */ - uint16_t ifv_tag; /* tag to apply on packets leaving if */ - uint16_t ifv_vid; /* VLAN ID */ - uint8_t ifv_pcp; /* Priority Code Point (PCP). */ + struct ether_8021q_tag ifv_qtag; +#define ifv_proto ifv_qtag.proto +#define ifv_vid ifv_qtag.vid +#define ifv_pcp ifv_qtag.pcp struct task lladdr_task; CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; #ifndef VLAN_ARRAY @@ -216,15 +216,16 @@ extern int vlan_mtag_pcp; static const char vlanname[] = "vlan"; +static const char svlanname[] = "svlan"; static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); static eventhandler_tag ifdetach_tag; static eventhandler_tag iflladdr_tag; /* - * if_vlan uses two module-level synchronizations primitives to allow concurrent - * modification of vlan interfaces and (mostly) allow for vlans to be destroyed - * while they are being used for tx/rx. To accomplish this in a way that has + * if_vlan uses two module-level synchronizations primitives to allow concurrent + * modification of vlan interfaces and (mostly) allow for vlans to be destroyed + * while they are being used for tx/rx. To accomplish this in a way that has * acceptable performance and cooperation with other parts of the network stack * there is a non-sleepable epoch(9) and an sx(9). * @@ -244,7 +245,7 @@ static struct sx _VLAN_SX_ID; #define VLAN_LOCKING_INIT() \ - sx_init(&_VLAN_SX_ID, "vlan_sx") + sx_init_flags(&_VLAN_SX_ID, "vlan_sx", SX_RECURSE) #define VLAN_LOCKING_DESTROY() \ sx_destroy(&_VLAN_SX_ID) @@ -306,13 +307,15 @@ const struct sockaddr *dst, struct route *ro); static void vlan_unconfig(struct ifnet *ifp); static void vlan_unconfig_locked(struct ifnet *ifp, int departing); -static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag); +static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag, + uint16_t proto); static void vlan_link_state(struct ifnet *ifp); static void vlan_capabilities(struct ifvlan *ifv); static void vlan_trunk_capabilities(struct ifnet *ifp); static struct ifnet *vlan_clone_match_ethervid(const char *, int *); static int vlan_clone_match(struct if_clone *, const char *); +static int svlan_clone_match(struct if_clone *, const char *); static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); static int vlan_clone_destroy(struct if_clone *, struct ifnet *); @@ -322,10 +325,13 @@ static void vlan_lladdr_fn(void *arg, int pending); static struct if_clone *vlan_cloner; +static struct if_clone *svlan_cloner; #ifdef VIMAGE VNET_DEFINE_STATIC(struct if_clone *, vlan_cloner); #define V_vlan_cloner VNET(vlan_cloner) +VNET_DEFINE_STATIC(struct if_clone *, svlan_cloner); +#define V_svlan_cloner VNET(svlan_cloner) #endif static void @@ -760,7 +766,7 @@ /* * Return a driver specific cookie for this interface. Synchronization - * with setcookie must be provided by the driver. + * with setcookie must be provided by the driver. */ static void * vlan_cookie(struct ifnet *ifp) @@ -810,16 +816,6 @@ return (ifp); } -/* - * Recalculate the cached VLAN tag exposed via the MIB. - */ -static void -vlan_tag_recalculate(struct ifvlan *ifv) -{ - - ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0); -} - /* * VLAN support can be loaded as a module. The only place in the * system that's intimately aware of this is ether_input. We hook @@ -859,6 +855,8 @@ #ifndef VIMAGE vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, vlan_clone_create, vlan_clone_destroy); + svlan_cloner = if_clone_advanced(svlanname, 0, svlan_clone_match, + vlan_clone_create, vlan_clone_destroy); #endif if (bootverbose) printf("vlan: initialized, using " @@ -867,11 +865,12 @@ #else "hash tables with chaining" #endif - + "\n"); break; case MOD_UNLOAD: #ifndef VIMAGE + if_clone_detach(svlan_cloner); if_clone_detach(vlan_cloner); #endif EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); @@ -911,6 +910,10 @@ vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, vlan_clone_create, vlan_clone_destroy); V_vlan_cloner = vlan_cloner; + + svlan_cloner = if_clone_advanced(svlanname, 0, svlan_clone_match, + vlan_clone_create, vlan_clone_destroy); + V_svlan_cloner = svlan_cloner; } VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_vlan_init, NULL); @@ -918,7 +921,7 @@ static void vnet_vlan_uninit(const void *unused __unused) { - + if_clone_detach(V_svlan_cloner); if_clone_detach(V_vlan_cloner); } VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY, @@ -926,7 +929,7 @@ #endif /* - * Check for . style interface names. + * Check for .[. ...] style interface names. */ static struct ifnet * vlan_clone_match_ethervid(const char *name, int *vidp) @@ -937,7 +940,7 @@ int vid; strlcpy(ifname, name, IFNAMSIZ); - if ((cp = strchr(ifname, '.')) == NULL) + if ((cp = strrchr(ifname, '.')) == NULL) return (NULL); *cp = '\0'; if ((ifp = ifunit_ref(ifname)) == NULL) @@ -982,6 +985,21 @@ return (1); } +static int +svlan_clone_match(struct if_clone *ifc, const char *name) +{ + const char *cp; + + if (strncmp(svlanname, name, strlen(svlanname)) != 0) + return (0); + for (cp = name + 5; *cp != '\0'; cp++) { + if (*cp < '0' || *cp > '9') + return (0); + } + + return (1); +} + static int vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) { @@ -990,6 +1008,7 @@ int unit; int error; int vid; + uint16_t proto; struct ifvlan *ifv; struct ifnet *ifp; struct ifnet *p; @@ -998,6 +1017,8 @@ struct vlanreq vlr; static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ + proto = ETHERTYPE_VLAN; + /* * There are 3 (ugh) ways to specify the cloned device: * o pass a parameter block with the clone request. @@ -1024,8 +1045,10 @@ } vid = vlr.vlr_tag; wildcard = (unit < 0); + if (strncmp(svlanname, name, strlen(svlanname)) == 0) + proto = ETHERTYPE_QINQ; } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { - unit = -1; + unit = vid; wildcard = 0; } else { p = NULL; @@ -1068,7 +1091,7 @@ * we don't conform to the default naming convention for interfaces. */ strlcpy(ifp->if_xname, name, IFNAMSIZ); - ifp->if_dname = vlanname; + ifp->if_dname = (proto == ETHERTYPE_QINQ) ? svlanname : vlanname; ifp->if_dunit = unit; ifp->if_init = vlan_init; @@ -1092,7 +1115,7 @@ sdl->sdl_type = IFT_L2VLAN; if (p != NULL) { - error = vlan_config(ifv, p, vid); + error = vlan_config(ifv, p, vid, proto); if_rele(p); if (error != 0) { /* @@ -1117,7 +1140,9 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) { struct ifvlan *ifv = ifp->if_softc; - int unit = ifp->if_dunit; + + if (ifp->if_vlantrunk) + return (EBUSY); ether_ifdetach(ifp); /* first, remove it from system-wide lists */ vlan_unconfig(ifp); /* now it can be unconfigured and freed */ @@ -1130,7 +1155,7 @@ NET_EPOCH_WAIT(); if_free(ifp); free(ifv, M_VLAN); - ifc_free_unit(ifc, unit); + ifc_free_unit(ifc, ifp->if_dunit); return (0); } @@ -1196,7 +1221,7 @@ return (ENETDOWN); } - if (!ether_8021q_frame(&m, ifp, p, ifv->ifv_vid, ifv->ifv_pcp)) { + if (!ether_8021q_frame(&m, ifp, p, &ifv->ifv_qtag)) { if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (0); } @@ -1223,12 +1248,19 @@ NET_EPOCH_ASSERT(); + /* + * Find the first non-VLAN parent interface. + */ ifv = ifp->if_softc; - if (TRUNK(ifv) == NULL) { - m_freem(m); - return (ENETDOWN); - } - p = PARENT(ifv); + do { + if (TRUNK(ifv) == NULL) { + m_freem(m); + return (ENETDOWN); + } + p = PARENT(ifv); + ifv = p->if_softc; + } while (p->if_type == IFT_L2VLAN); + return p->if_output(ifp, m, dst, ro); } @@ -1357,7 +1389,8 @@ } static int -vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) +vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, + uint16_t proto) { struct epoch_tracker et; struct ifvlantrunk *trunk; @@ -1369,6 +1402,7 @@ * they handle the tagging and headers themselves. */ if (p->if_type != IFT_ETHER && + p->if_type != IFT_L2VLAN && (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) return (EPROTONOSUPPORT); if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) @@ -1400,11 +1434,10 @@ ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ ifv->ifv_pcp = 0; /* Default: best effort delivery. */ - vlan_tag_recalculate(ifv); error = vlan_inshash(trunk, ifv); if (error) goto done; - ifv->ifv_proto = ETHERTYPE_VLAN; + ifv->ifv_proto = proto; ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; ifv->ifv_mintu = ETHERMIN; ifv->ifv_pflags = 0; @@ -1915,7 +1948,7 @@ break; } oldmtu = ifp->if_mtu; - error = vlan_config(ifv, p, vlr.vlr_tag); + error = vlan_config(ifv, p, vlr.vlr_tag, ifv->ifv_proto); if_rele(p); /* @@ -1947,7 +1980,7 @@ VLAN_SUNLOCK(); error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr)); break; - + case SIOCSIFFLAGS: /* * We should propagate selected flags to the parent, @@ -2001,7 +2034,6 @@ } ifv->ifv_pcp = ifr->ifr_vlan_pcp; ifp->if_pcp = ifv->ifv_pcp; - vlan_tag_recalculate(ifv); /* broadcast event about PCP change */ EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP); break;