From owner-svn-src-head@freebsd.org Wed Oct 21 21:28:22 2020 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id D4CB842DE68; Wed, 21 Oct 2020 21:28:22 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4CGkBG5Ylkz3YkF; Wed, 21 Oct 2020 21:28:22 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id A1F29BE70; Wed, 21 Oct 2020 21:28:22 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 09LLSMfL080767; Wed, 21 Oct 2020 21:28:22 GMT (envelope-from melifaro@FreeBSD.org) Received: (from melifaro@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 09LLSK8d080756; Wed, 21 Oct 2020 21:28:20 GMT (envelope-from melifaro@FreeBSD.org) Message-Id: <202010212128.09LLSK8d080756@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: melifaro set sender to melifaro@FreeBSD.org using -f From: "Alexander V. Chernikov" Date: Wed, 21 Oct 2020 21:28:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r366917 - in head: sbin/ifconfig sys/net tests/sys/net X-SVN-Group: head X-SVN-Commit-Author: melifaro X-SVN-Commit-Paths: in head: sbin/ifconfig sys/net tests/sys/net X-SVN-Commit-Revision: 366917 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.33 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 21 Oct 2020 21:28:22 -0000 Author: melifaro Date: Wed Oct 21 21:28:20 2020 New Revision: 366917 URL: https://svnweb.freebsd.org/changeset/base/366917 Log: Add support for stacked VLANs (IEEE 802.1ad, AKA Q-in-Q). 802.1ad interfaces are created with ifconfig using the "vlanproto" parameter. Eg., the following creates a 802.1Q VLAN (id #42) over a 802.1ad S-VLAN (id #5) over a physical Ethernet interface (em0). ifconfig vlan5 create vlandev em0 vlan 5 vlanproto 802.1ad up ifconfig vlan42 create vlandev vlan5 vlan 42 inet 10.5.42.1/24 VLAN_MTU, VLAN_HWCSUM and VLAN_TSO capabilities should be properly supported. VLAN_HWTAGGING is only partially supported, as there is currently no IFCAP_VLAN_* denoting the possibility to set the VLAN EtherType to anything else than 0x8100 (802.1ad uses 0x88A8). Submitted by: Olivier Piras Sponsored by: RG Nets Differential Revision: https://reviews.freebsd.org/D26436 Modified: head/sbin/ifconfig/ifclone.c head/sbin/ifconfig/ifconfig.8 head/sbin/ifconfig/ifconfig.h head/sbin/ifconfig/ifieee80211.c head/sbin/ifconfig/ifvlan.c head/sbin/ifconfig/ifvxlan.c head/sys/net/ethernet.h head/sys/net/if_clone.c head/sys/net/if_ethersubr.c head/sys/net/if_vlan.c head/sys/net/if_vlan_var.h head/tests/sys/net/if_vlan.sh Modified: head/sbin/ifconfig/ifclone.c ============================================================================== --- head/sbin/ifconfig/ifclone.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifclone.c Wed Oct 21 21:28:20 2020 (r366917) @@ -49,6 +49,11 @@ static const char rcsid[] = #include "ifconfig.h" +typedef enum { + MT_PREFIX, + MT_FILTER, +} clone_match_type; + static void list_cloners(void) { @@ -76,7 +81,11 @@ list_cloners(void) } struct clone_defcb { - char ifprefix[IFNAMSIZ]; + union { + char ifprefix[IFNAMSIZ]; + clone_match_func *ifmatch; + }; + clone_match_type clone_mt; clone_callback_func *clone_cb; SLIST_ENTRY(clone_defcb) next; }; @@ -85,16 +94,29 @@ static SLIST_HEAD(, clone_defcb) clone_defcbh = SLIST_HEAD_INITIALIZER(clone_defcbh); void -clone_setdefcallback(const char *ifprefix, clone_callback_func *p) +clone_setdefcallback_prefix(const char *ifprefix, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1); + dcp->clone_mt = MT_PREFIX; dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } +void +clone_setdefcallback_filter(clone_match_func *filter, clone_callback_func *p) +{ + struct clone_defcb *dcp; + + dcp = malloc(sizeof(*dcp)); + dcp->ifmatch = filter; + dcp->clone_mt = MT_FILTER; + dcp->clone_cb = p; + SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); +} + /* * Do the actual clone operation. Any parameters must have been * setup by now. If a callback has been setup to do the work @@ -114,8 +136,14 @@ ifclonecreate(int s, void *arg) if (clone_cb == NULL) { /* Try to find a default callback */ SLIST_FOREACH(dcp, &clone_defcbh, next) { - if (strncmp(dcp->ifprefix, ifr.ifr_name, - strlen(dcp->ifprefix)) == 0) { + if ((dcp->clone_mt == MT_PREFIX) && + (strncmp(dcp->ifprefix, ifr.ifr_name, + strlen(dcp->ifprefix)) == 0)) { + clone_cb = dcp->clone_cb; + break; + } + if ((dcp->clone_mt == MT_FILTER) && + dcp->ifmatch(ifr.ifr_name)) { clone_cb = dcp->clone_cb; break; } Modified: head/sbin/ifconfig/ifconfig.8 ============================================================================== --- head/sbin/ifconfig/ifconfig.8 Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifconfig.8 Wed Oct 21 21:28:20 2020 (r366917) @@ -582,7 +582,7 @@ they support in their capabilities. is a synonym for enabling all available WOL mechanisms. To disable WOL use .Fl wol . -.It Cm vlanmtu , vlanhwtag, vlanhwfilter, vlanhwcsum, vlanhwtso +.It Cm vlanmtu , vlanhwtag , vlanhwfilter , vlanhwcsum , vlanhwtso If the driver offers user-configurable VLAN support, enable reception of extended frames, tag processing in hardware, frame filtering in hardware, checksum offloading, or TSO on VLAN, @@ -592,7 +592,7 @@ Note that this must be configured on a physical interf 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, @@ -2696,7 +2696,7 @@ interfaces: 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 +or 802.1ad VLAN header for packets sent from the .Xr vlan 4 interface. Note that @@ -2704,6 +2704,15 @@ Note that and .Cm vlandev must both be set at the same time. +.It Cm vlanproto Ar vlan_proto +Set the VLAN encapsulation protocol to +.Ar vlan_proto . +Supported encapsulation protocols are currently +.Dq 802.1Q +and +.Dq 802.1ad . +The default encapsulation protocol is +.Dq 802.1Q . .It Cm vlanpcp Ar priority_code_point Priority code point .Pq Dv PCP Modified: head/sbin/ifconfig/ifconfig.h ============================================================================== --- head/sbin/ifconfig/ifconfig.h Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifconfig.h Wed Oct 21 21:28:20 2020 (r366917) @@ -145,8 +145,10 @@ void printb(const char *s, unsigned value, const char void ifmaybeload(const char *name); +typedef int clone_match_func(const char *); typedef void clone_callback_func(int, struct ifreq *); -void clone_setdefcallback(const char *, clone_callback_func *); +void clone_setdefcallback_prefix(const char *, clone_callback_func *); +void clone_setdefcallback_filter(clone_match_func *, clone_callback_func *); void sfp_status(int s, struct ifreq *ifr, int verbose); Modified: head/sbin/ifconfig/ifieee80211.c ============================================================================== --- head/sbin/ifconfig/ifieee80211.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifieee80211.c Wed Oct 21 21:28:20 2020 (r366917) @@ -6069,5 +6069,5 @@ ieee80211_ctor(void) for (i = 0; i < nitems(ieee80211_cmds); i++) cmd_register(&ieee80211_cmds[i]); af_register(&af_ieee80211); - clone_setdefcallback("wlan", wlan_create); + clone_setdefcallback_prefix("wlan", wlan_create); } Modified: head/sbin/ifconfig/ifvlan.c ============================================================================== --- head/sbin/ifconfig/ifvlan.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifvlan.c Wed Oct 21 21:28:20 2020 (r366917) @@ -66,8 +66,12 @@ static const char rcsid[] = #define NOTAG ((u_short) -1) +static const char proto_8021Q[] = "802.1q"; +static const char proto_8021ad[] = "802.1ad"; + static struct vlanreq params = { .vlr_tag = NOTAG, + .vlr_proto = ETHERTYPE_VLAN, }; static int @@ -87,6 +91,17 @@ vlan_status(int s) if (getvlan(s, &ifr, &vreq) == -1) return; printf("\tvlan: %d", vreq.vlr_tag); + printf(" vlanproto: "); + switch (vreq.vlr_proto) { + case ETHERTYPE_VLAN: + printf(proto_8021Q); + break; + case ETHERTYPE_QINQ: + printf(proto_8021ad); + break; + default: + printf("0x%04x", vreq.vlr_proto); + } if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1) printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? @@ -94,9 +109,49 @@ vlan_status(int s) printf("\n"); } +static int +vlan_match_ethervid(const char *name) +{ + return (strchr(name, '.') != NULL); +} + static void +vlan_parse_ethervid(const char *name) +{ + char ifname[IFNAMSIZ]; + char *cp; + int vid; + + strlcpy(ifname, name, IFNAMSIZ); + if ((cp = strrchr(ifname, '.')) == NULL) + return; + /* + * Don't mix vlan/vlandev parameters with dot notation. + */ + if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') + errx(1, "ambiguous vlan specification"); + /* + * Derive params from interface name: "parent.vid". + */ + *cp++ = '\0'; + if ((*cp < '1') || (*cp > '9')) + errx(1, "invalid vlan tag"); + + vid = *cp++ - '0'; + while ((*cp >= '0') && (*cp <= '9')) + vid = (vid * 10) + (*cp++ - '0'); + if ((*cp != '\0') || (vid & ~0xFFF)) + errx(1, "invalid vlan tag"); + + strlcpy(params.vlr_parent, ifname, IFNAMSIZ); + params.vlr_tag = (vid & 0xFFF); +} + +static void vlan_create(int s, struct ifreq *ifr) { + vlan_parse_ethervid(ifr->ifr_name); + if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { /* * One or both parameters were specified, make sure both. @@ -159,6 +214,24 @@ DECL_CMD_FUNC(setvlandev, val, d) } static +DECL_CMD_FUNC(setvlanproto, val, d) +{ + struct vlanreq vreq; + + if (strncasecmp(proto_8021Q, val, + strlen(proto_8021Q)) == 0) { + params.vlr_proto = ETHERTYPE_VLAN; + } else if (strncasecmp(proto_8021ad, val, + strlen(proto_8021ad)) == 0) { + params.vlr_proto = ETHERTYPE_QINQ; + } else + errx(1, "invalid value for vlanproto"); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); +} + +static DECL_CMD_FUNC(setvlanpcp, val, d) { u_long ul; @@ -195,10 +268,12 @@ DECL_CMD_FUNC(unsetvlandev, val, d) static struct cmd vlan_cmds[] = { DEF_CLONE_CMD_ARG("vlan", setvlantag), DEF_CLONE_CMD_ARG("vlandev", setvlandev), + DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), DEF_CMD_ARG("vlanpcp", setvlanpcp), /* NB: non-clone cmds */ DEF_CMD_ARG("vlan", setvlantag), DEF_CMD_ARG("vlandev", setvlandev), + DEF_CMD_ARG("vlanproto", setvlanproto), /* XXX For compatibility. Should become DEF_CMD() some day. */ DEF_CMD_OPTARG("-vlandev", unsetvlandev), DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), @@ -227,5 +302,6 @@ vlan_ctor(void) cmd_register(&vlan_cmds[i]); af_register(&af_vlan); callback_register(vlan_cb, NULL); - clone_setdefcallback("vlan", vlan_create); + clone_setdefcallback_prefix("vlan", vlan_create); + clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); } Modified: head/sbin/ifconfig/ifvxlan.c ============================================================================== --- head/sbin/ifconfig/ifvxlan.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sbin/ifconfig/ifvxlan.c Wed Oct 21 21:28:20 2020 (r366917) @@ -642,5 +642,5 @@ vxlan_ctor(void) cmd_register(&vxlan_cmds[i]); af_register(&af_vxlan); callback_register(vxlan_cb, NULL); - clone_setdefcallback("vxlan", vxlan_create); + clone_setdefcallback_prefix("vxlan", vxlan_create); } Modified: head/sys/net/ethernet.h ============================================================================== --- head/sys/net/ethernet.h Wed Oct 21 20:42:29 2020 (r366916) +++ head/sys/net/ethernet.h Wed Oct 21 21:28:20 2020 (r366917) @@ -428,6 +428,7 @@ struct mbuf; struct route; struct sockaddr; struct bpf_if; +struct ether_8021q_tag; extern uint32_t ether_crc32_le(const uint8_t *, size_t); extern uint32_t ether_crc32_be(const uint8_t *, size_t); @@ -441,10 +442,16 @@ extern int ether_output_frame(struct ifnet *, struct 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 *); Modified: head/sys/net/if_clone.c ============================================================================== --- head/sys/net/if_clone.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sys/net/if_clone.c Wed Oct 21 21:28:20 2020 (r366917) @@ -571,7 +571,7 @@ if_clone_addgroup(struct ifnet *ifp, struct if_clone * /* * A utility function to extract unit numbers from interface names of - * the form name###. + * the form name###[.###]. * * Returns 0 on success and an error on failure. */ @@ -582,7 +582,9 @@ ifc_name2unit(const char *name, int *unit) int cutoff = INT_MAX / 10; int cutlim = INT_MAX % 10; - for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); + if ((cp = strrchr(name, '.')) == NULL) + cp = name; + for (; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); if (*cp == '\0') { *unit = -1; } else if (cp[0] == '0' && cp[1] != '\0') { Modified: head/sys/net/if_ethersubr.c ============================================================================== --- head/sys/net/if_ethersubr.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sys/net/if_ethersubr.c Wed Oct 21 21:28:20 2020 (r366917) @@ -441,12 +441,19 @@ bad: if (m != NULL) 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); } @@ -616,9 +623,9 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m * If the hardware did not process an 802.1Q tag, do this now, * to allow 802.1P priority frames to be passed to the main input * 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 +1310,7 @@ ether_vlan_mtap(struct bpf_if *bp, struct mbuf *m, voi } 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 +1332,7 @@ ether_vlanencap(struct mbuf *m, uint16_t tag) 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 +1361,7 @@ SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG 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; @@ -1391,7 +1398,7 @@ ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, * If PCP is set in mbuf, use it */ if ((*mp)->m_flags & M_VLANTAG) { - pcp = EVL_PRIOFTAG((*mp)->m_pkthdr.ether_vtag); + qtag->pcp = EVL_PRIOFTAG((*mp)->m_pkthdr.ether_vtag); } /* @@ -1403,14 +1410,15 @@ ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, */ 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); Modified: head/sys/net/if_vlan.c ============================================================================== --- head/sys/net/if_vlan.c Wed Oct 21 20:42:29 2020 (r366916) +++ head/sys/net/if_vlan.c Wed Oct 21 21:28:20 2020 (r366917) @@ -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 ifvlan { 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 @@ -222,9 +222,9 @@ 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 +244,7 @@ static eventhandler_tag iflladdr_tag; 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,7 +306,8 @@ static int vlan_output(struct ifnet *ifp, struct mbuf 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); @@ -760,7 +761,7 @@ vlan_pcp(struct ifnet *ifp, uint16_t *pcpp) /* * 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) @@ -811,16 +812,6 @@ vlan_devat(struct ifnet *ifp, uint16_t vid) } /* - * 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 * into this code through vlan_input_p which is defined there and @@ -867,7 +858,7 @@ vlan_modevent(module_t mod, int type, void *data) #else "hash tables with chaining" #endif - + "\n"); break; case MOD_UNLOAD: @@ -926,7 +917,7 @@ VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_OR #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 +928,7 @@ vlan_clone_match_ethervid(const char *name, int *vidp) 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) @@ -990,6 +981,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, si int unit; int error; int vid; + uint16_t proto; struct ifvlan *ifv; struct ifnet *ifp; struct ifnet *p; @@ -998,14 +990,15 @@ vlan_clone_create(struct if_clone *ifc, char *name, si 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: + * There are two ways to specify the cloned device: * o pass a parameter block with the clone request. - * o specify parameters in the text of the clone device name * o specify no parameters and get an unattached device that * must be configured separately. - * The first technique is preferred; the latter two are - * supported for backwards compatibility. + * The first technique is preferred; the latter is supported + * for backwards compatibility. * * XXXRW: Note historic use of the word "tag" here. New ioctls may be * called for. @@ -1023,10 +1016,8 @@ vlan_clone_create(struct if_clone *ifc, char *name, si return (error); } vid = vlr.vlr_tag; + proto = vlr.vlr_proto; wildcard = (unit < 0); - } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) { - unit = -1; - wildcard = 0; } else { p = NULL; error = ifc_name2unit(name, &unit); @@ -1092,7 +1083,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, si 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,8 +1108,10 @@ static int 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 +1123,7 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet 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 +1189,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) 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 +1216,19 @@ vlan_output(struct ifnet *ifp, struct mbuf *m, const s 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 +1357,8 @@ vlan_lladdr_fn(void *arg, int pending __unused) } 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 +1370,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint1 * 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 +1402,10 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint1 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 +1916,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data break; } oldmtu = ifp->if_mtu; - error = vlan_config(ifv, p, vlr.vlr_tag); + error = vlan_config(ifv, p, vlr.vlr_tag, vlr.vlr_proto); if_rele(p); /* @@ -1943,11 +1944,12 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, sizeof(vlr.vlr_parent)); vlr.vlr_tag = ifv->ifv_vid; + vlr.vlr_proto = ifv->ifv_proto; } 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 +2003,6 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data } 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; Modified: head/sys/net/if_vlan_var.h ============================================================================== --- head/sys/net/if_vlan_var.h Wed Oct 21 20:42:29 2020 (r366916) +++ head/sys/net/if_vlan_var.h Wed Oct 21 21:28:20 2020 (r366917) @@ -69,6 +69,7 @@ struct vlanreq { char vlr_parent[IFNAMSIZ]; u_short vlr_tag; + u_short vlr_proto; }; #define SIOCSETVLAN SIOCSIFGENERIC #define SIOCGETVLAN SIOCGIFGENERIC @@ -122,6 +123,15 @@ struct vlanreq { #define MTAG_8021Q 1326104895 #define MTAG_8021Q_PCP_IN 0 /* Input priority. */ #define MTAG_8021Q_PCP_OUT 1 /* Output priority. */ + +/* + * 802.1q full tag. Proto and vid are stored in host byte order. + */ +struct ether_8021q_tag { + uint16_t proto; + uint16_t vid; + uint8_t pcp; +}; #define VLAN_CAPABILITIES(_ifp) do { \ if ((_ifp)->if_vlantrunk != NULL) \ Modified: head/tests/sys/net/if_vlan.sh ============================================================================== --- head/tests/sys/net/if_vlan.sh Wed Oct 21 20:42:29 2020 (r366916) +++ head/tests/sys/net/if_vlan.sh Wed Oct 21 21:28:20 2020 (r366917) @@ -36,7 +36,185 @@ basic_cleanup() vnet_cleanup } +# Simple Q-in-Q (802.1Q over 802.1ad) + +atf_test_case "qinq_simple" "cleanup" +qinq_simple_head() +{ + atf_set descr 'Simple Q-in-Q test (802.1Q over 802.1ad)' + atf_set require.user root +} + +qinq_simple_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq0 ${epair_qinq}a + vnet_mkjail jqinq1 ${epair_qinq}b + + vlan5a=$(jexec jqinq0 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad) + vlan42a=$(jexec jqinq0 ifconfig vlan create \ + vlandev ${vlan5a} vlan 42 vlanproto 802.1q) + jexec jqinq0 ifconfig ${epair_qinq}a up + jexec jqinq0 ifconfig ${vlan5a} up + jexec jqinq0 ifconfig ${vlan42a} 10.5.42.1/24 up + + vlan5b=$(jexec jqinq1 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan42b=$(jexec jqinq1 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42 vlanproto 802.1q) + jexec jqinq1 ifconfig ${epair_qinq}b up + jexec jqinq1 ifconfig ${vlan5b} up + jexec jqinq1 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq1 ping -c 1 10.5.42.1 +} + +qinq_simple_cleanup() +{ + vnet_cleanup +} + +# Deep Q-in-Q (802.1Q over 802.1ad over 802.1ad) + +atf_test_case "qinq_deep" "cleanup" +qinq_deep_head() +{ + atf_set descr 'Deep Q-in-Q test (802.1Q over 802.1ad over 802.1ad)' + atf_set require.user root +} + +qinq_deep_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq2 ${epair_qinq}a + vnet_mkjail jqinq3 ${epair_qinq}b + + vlan5a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad) + vlan6a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${vlan5a} vlan 6 vlanproto 802.1ad) + vlan42a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${vlan6a} vlan 42 vlanproto 802.1q) + jexec jqinq2 ifconfig ${epair_qinq}a up + jexec jqinq2 ifconfig ${vlan5a} up + jexec jqinq2 ifconfig ${vlan6a} up + jexec jqinq2 ifconfig ${vlan42a} 10.6.42.1/24 up + + vlan5b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan6b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${vlan5b} vlan 6 vlanproto 802.1ad) + vlan42b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${vlan6b} vlan 42 vlanproto 802.1q) + jexec jqinq3 ifconfig ${epair_qinq}b up + jexec jqinq3 ifconfig ${vlan5b} up + jexec jqinq3 ifconfig ${vlan6b} up + jexec jqinq3 ifconfig ${vlan42b} 10.6.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq3 ping -c 1 10.6.42.1 +} + +qinq_deep_cleanup() +{ + vnet_cleanup +} + +# Legacy Q-in-Q (802.1Q over 802.1Q) + +atf_test_case "qinq_legacy" "cleanup" +qinq_legacy_head() +{ + atf_set descr 'Legacy Q-in-Q test (802.1Q over 802.1Q)' + atf_set require.user root +} + +qinq_legacy_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq4 ${epair_qinq}a + vnet_mkjail jqinq5 ${epair_qinq}b + + vlan5a=$(jexec jqinq4 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5) + vlan42a=$(jexec jqinq4 ifconfig vlan create \ + vlandev ${vlan5a} vlan 42) + jexec jqinq4 ifconfig ${epair_qinq}a up + jexec jqinq4 ifconfig ${vlan5a} up + jexec jqinq4 ifconfig ${vlan42a} 10.5.42.1/24 up + + vlan5b=$(jexec jqinq5 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5) + vlan42b=$(jexec jqinq5 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42) + jexec jqinq5 ifconfig ${epair_qinq}b up + jexec jqinq5 ifconfig ${vlan5b} up + jexec jqinq5 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq5 ping -c 1 10.5.42.1 +} + +qinq_legacy_cleanup() +{ + vnet_cleanup +} + +# Simple Q-in-Q with dot notation + +atf_test_case "qinq_dot" "cleanup" +qinq_dot_head() +{ + atf_set descr 'Simple Q-in-Q test with dot notation' + atf_set require.user root +} + +qinq_dot_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq6 ${epair_qinq}a + vnet_mkjail jqinq7 ${epair_qinq}b + + jexec jqinq6 ifconfig vlan5 create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad + jexec jqinq6 ifconfig vlan5.42 create \ + vlanproto 802.1q + jexec jqinq6 ifconfig ${epair_qinq}a up + jexec jqinq6 ifconfig vlan5 up + jexec jqinq6 ifconfig vlan5.42 10.5.42.1/24 up + + vlan5b=$(jexec jqinq7 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan42b=$(jexec jqinq7 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42 vlanproto 802.1q) + jexec jqinq7 ifconfig ${epair_qinq}b up + jexec jqinq7 ifconfig ${vlan5b} up + jexec jqinq7 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq7 ping -c 1 10.5.42.1 +} + +qinq_dot_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "basic" + atf_add_test_case "qinq_simple" + atf_add_test_case "qinq_deep" + atf_add_test_case "qinq_legacy" + atf_add_test_case "qinq_dot" }