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
This commit is contained in:
parent
37d411338e
commit
c7cffd65c5
@ -49,6 +49,11 @@ static const char rcsid[] =
|
|||||||
|
|
||||||
#include "ifconfig.h"
|
#include "ifconfig.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MT_PREFIX,
|
||||||
|
MT_FILTER,
|
||||||
|
} clone_match_type;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
list_cloners(void)
|
list_cloners(void)
|
||||||
{
|
{
|
||||||
@ -76,7 +81,11 @@ list_cloners(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct clone_defcb {
|
struct clone_defcb {
|
||||||
char ifprefix[IFNAMSIZ];
|
union {
|
||||||
|
char ifprefix[IFNAMSIZ];
|
||||||
|
clone_match_func *ifmatch;
|
||||||
|
};
|
||||||
|
clone_match_type clone_mt;
|
||||||
clone_callback_func *clone_cb;
|
clone_callback_func *clone_cb;
|
||||||
SLIST_ENTRY(clone_defcb) next;
|
SLIST_ENTRY(clone_defcb) next;
|
||||||
};
|
};
|
||||||
@ -85,12 +94,25 @@ static SLIST_HEAD(, clone_defcb) clone_defcbh =
|
|||||||
SLIST_HEAD_INITIALIZER(clone_defcbh);
|
SLIST_HEAD_INITIALIZER(clone_defcbh);
|
||||||
|
|
||||||
void
|
void
|
||||||
clone_setdefcallback(const char *ifprefix, clone_callback_func *p)
|
clone_setdefcallback_prefix(const char *ifprefix, clone_callback_func *p)
|
||||||
{
|
{
|
||||||
struct clone_defcb *dcp;
|
struct clone_defcb *dcp;
|
||||||
|
|
||||||
dcp = malloc(sizeof(*dcp));
|
dcp = malloc(sizeof(*dcp));
|
||||||
strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1);
|
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;
|
dcp->clone_cb = p;
|
||||||
SLIST_INSERT_HEAD(&clone_defcbh, dcp, next);
|
SLIST_INSERT_HEAD(&clone_defcbh, dcp, next);
|
||||||
}
|
}
|
||||||
@ -114,8 +136,14 @@ ifclonecreate(int s, void *arg)
|
|||||||
if (clone_cb == NULL) {
|
if (clone_cb == NULL) {
|
||||||
/* Try to find a default callback */
|
/* Try to find a default callback */
|
||||||
SLIST_FOREACH(dcp, &clone_defcbh, next) {
|
SLIST_FOREACH(dcp, &clone_defcbh, next) {
|
||||||
if (strncmp(dcp->ifprefix, ifr.ifr_name,
|
if ((dcp->clone_mt == MT_PREFIX) &&
|
||||||
strlen(dcp->ifprefix)) == 0) {
|
(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;
|
clone_cb = dcp->clone_cb;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -582,7 +582,7 @@ they support in their capabilities.
|
|||||||
is a synonym for enabling all available WOL mechanisms.
|
is a synonym for enabling all available WOL mechanisms.
|
||||||
To disable WOL use
|
To disable WOL use
|
||||||
.Fl wol .
|
.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
|
If the driver offers user-configurable VLAN support, enable
|
||||||
reception of extended frames, tag processing in hardware,
|
reception of extended frames, tag processing in hardware,
|
||||||
frame filtering in hardware, checksum offloading, or TSO on VLAN,
|
frame filtering in hardware, checksum offloading, or TSO on VLAN,
|
||||||
@ -592,7 +592,7 @@ Note that this must be configured on a physical interface associated with
|
|||||||
not on a
|
not on a
|
||||||
.Xr vlan 4
|
.Xr vlan 4
|
||||||
interface itself.
|
interface itself.
|
||||||
.It Fl vlanmtu , vlanhwtag , vlanhwfilter , vlanhwtso
|
.It Fl vlanmtu , vlanhwtag, vlanhwfilter, vlanhwtso
|
||||||
If the driver offers user-configurable VLAN support, disable
|
If the driver offers user-configurable VLAN support, disable
|
||||||
reception of extended frames, tag processing in hardware,
|
reception of extended frames, tag processing in hardware,
|
||||||
frame filtering in hardware, or TSO on VLAN,
|
frame filtering in hardware, or TSO on VLAN,
|
||||||
@ -2696,7 +2696,7 @@ interfaces:
|
|||||||
Set the VLAN tag value to
|
Set the VLAN tag value to
|
||||||
.Ar vlan_tag .
|
.Ar vlan_tag .
|
||||||
This value is a 12-bit VLAN Identifier (VID) which is used to create an 802.1Q
|
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
|
.Xr vlan 4
|
||||||
interface.
|
interface.
|
||||||
Note that
|
Note that
|
||||||
@ -2704,6 +2704,15 @@ Note that
|
|||||||
and
|
and
|
||||||
.Cm vlandev
|
.Cm vlandev
|
||||||
must both be set at the same time.
|
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
|
.It Cm vlanpcp Ar priority_code_point
|
||||||
Priority code point
|
Priority code point
|
||||||
.Pq Dv PCP
|
.Pq Dv PCP
|
||||||
|
@ -145,8 +145,10 @@ void printb(const char *s, unsigned value, const char *bits);
|
|||||||
|
|
||||||
void ifmaybeload(const char *name);
|
void ifmaybeload(const char *name);
|
||||||
|
|
||||||
|
typedef int clone_match_func(const char *);
|
||||||
typedef void clone_callback_func(int, struct ifreq *);
|
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);
|
void sfp_status(int s, struct ifreq *ifr, int verbose);
|
||||||
|
|
||||||
|
@ -6069,5 +6069,5 @@ ieee80211_ctor(void)
|
|||||||
for (i = 0; i < nitems(ieee80211_cmds); i++)
|
for (i = 0; i < nitems(ieee80211_cmds); i++)
|
||||||
cmd_register(&ieee80211_cmds[i]);
|
cmd_register(&ieee80211_cmds[i]);
|
||||||
af_register(&af_ieee80211);
|
af_register(&af_ieee80211);
|
||||||
clone_setdefcallback("wlan", wlan_create);
|
clone_setdefcallback_prefix("wlan", wlan_create);
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,12 @@ static const char rcsid[] =
|
|||||||
|
|
||||||
#define NOTAG ((u_short) -1)
|
#define NOTAG ((u_short) -1)
|
||||||
|
|
||||||
|
static const char proto_8021Q[] = "802.1q";
|
||||||
|
static const char proto_8021ad[] = "802.1ad";
|
||||||
|
|
||||||
static struct vlanreq params = {
|
static struct vlanreq params = {
|
||||||
.vlr_tag = NOTAG,
|
.vlr_tag = NOTAG,
|
||||||
|
.vlr_proto = ETHERTYPE_VLAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -87,6 +91,17 @@ vlan_status(int s)
|
|||||||
if (getvlan(s, &ifr, &vreq) == -1)
|
if (getvlan(s, &ifr, &vreq) == -1)
|
||||||
return;
|
return;
|
||||||
printf("\tvlan: %d", vreq.vlr_tag);
|
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)
|
if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1)
|
||||||
printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
|
printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
|
||||||
printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
|
printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
|
||||||
@ -94,9 +109,49 @@ vlan_status(int s)
|
|||||||
printf("\n");
|
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
|
static void
|
||||||
vlan_create(int s, struct ifreq *ifr)
|
vlan_create(int s, struct ifreq *ifr)
|
||||||
{
|
{
|
||||||
|
vlan_parse_ethervid(ifr->ifr_name);
|
||||||
|
|
||||||
if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') {
|
if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') {
|
||||||
/*
|
/*
|
||||||
* One or both parameters were specified, make sure both.
|
* One or both parameters were specified, make sure both.
|
||||||
@ -158,6 +213,24 @@ DECL_CMD_FUNC(setvlandev, val, d)
|
|||||||
vlan_set(s, &ifr);
|
vlan_set(s, &ifr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static
|
||||||
DECL_CMD_FUNC(setvlanpcp, val, d)
|
DECL_CMD_FUNC(setvlanpcp, val, d)
|
||||||
{
|
{
|
||||||
@ -195,10 +268,12 @@ DECL_CMD_FUNC(unsetvlandev, val, d)
|
|||||||
static struct cmd vlan_cmds[] = {
|
static struct cmd vlan_cmds[] = {
|
||||||
DEF_CLONE_CMD_ARG("vlan", setvlantag),
|
DEF_CLONE_CMD_ARG("vlan", setvlantag),
|
||||||
DEF_CLONE_CMD_ARG("vlandev", setvlandev),
|
DEF_CLONE_CMD_ARG("vlandev", setvlandev),
|
||||||
|
DEF_CLONE_CMD_ARG("vlanproto", setvlanproto),
|
||||||
DEF_CMD_ARG("vlanpcp", setvlanpcp),
|
DEF_CMD_ARG("vlanpcp", setvlanpcp),
|
||||||
/* NB: non-clone cmds */
|
/* NB: non-clone cmds */
|
||||||
DEF_CMD_ARG("vlan", setvlantag),
|
DEF_CMD_ARG("vlan", setvlantag),
|
||||||
DEF_CMD_ARG("vlandev", setvlandev),
|
DEF_CMD_ARG("vlandev", setvlandev),
|
||||||
|
DEF_CMD_ARG("vlanproto", setvlanproto),
|
||||||
/* XXX For compatibility. Should become DEF_CMD() some day. */
|
/* XXX For compatibility. Should become DEF_CMD() some day. */
|
||||||
DEF_CMD_OPTARG("-vlandev", unsetvlandev),
|
DEF_CMD_OPTARG("-vlandev", unsetvlandev),
|
||||||
DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap),
|
DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap),
|
||||||
@ -227,5 +302,6 @@ vlan_ctor(void)
|
|||||||
cmd_register(&vlan_cmds[i]);
|
cmd_register(&vlan_cmds[i]);
|
||||||
af_register(&af_vlan);
|
af_register(&af_vlan);
|
||||||
callback_register(vlan_cb, NULL);
|
callback_register(vlan_cb, NULL);
|
||||||
clone_setdefcallback("vlan", vlan_create);
|
clone_setdefcallback_prefix("vlan", vlan_create);
|
||||||
|
clone_setdefcallback_filter(vlan_match_ethervid, vlan_create);
|
||||||
}
|
}
|
||||||
|
@ -642,5 +642,5 @@ vxlan_ctor(void)
|
|||||||
cmd_register(&vxlan_cmds[i]);
|
cmd_register(&vxlan_cmds[i]);
|
||||||
af_register(&af_vxlan);
|
af_register(&af_vxlan);
|
||||||
callback_register(vxlan_cb, NULL);
|
callback_register(vxlan_cb, NULL);
|
||||||
clone_setdefcallback("vxlan", vxlan_create);
|
clone_setdefcallback_prefix("vxlan", vxlan_create);
|
||||||
}
|
}
|
||||||
|
@ -428,6 +428,7 @@ struct mbuf;
|
|||||||
struct route;
|
struct route;
|
||||||
struct sockaddr;
|
struct sockaddr;
|
||||||
struct bpf_if;
|
struct bpf_if;
|
||||||
|
struct ether_8021q_tag;
|
||||||
|
|
||||||
extern uint32_t ether_crc32_le(const uint8_t *, size_t);
|
extern uint32_t ether_crc32_le(const uint8_t *, size_t);
|
||||||
extern uint32_t ether_crc32_be(const uint8_t *, size_t);
|
extern uint32_t ether_crc32_be(const uint8_t *, size_t);
|
||||||
@ -441,11 +442,17 @@ extern int ether_output_frame(struct ifnet *, struct mbuf *);
|
|||||||
extern char *ether_sprintf(const u_int8_t *);
|
extern char *ether_sprintf(const u_int8_t *);
|
||||||
void ether_vlan_mtap(struct bpf_if *, struct mbuf *,
|
void ether_vlan_mtap(struct bpf_if *, struct mbuf *,
|
||||||
void *, u_int);
|
void *, u_int);
|
||||||
struct mbuf *ether_vlanencap(struct mbuf *, uint16_t);
|
struct mbuf *ether_vlanencap_proto(struct mbuf *, uint16_t, uint16_t);
|
||||||
bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p,
|
bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife,
|
||||||
uint16_t vid, uint8_t pcp);
|
struct ifnet *p, struct ether_8021q_tag *);
|
||||||
void ether_gen_addr(struct ifnet *ifp, struct ether_addr *hwaddr);
|
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 */
|
/* new ethernet interface attached event */
|
||||||
typedef void (*ether_ifattach_event_handler_t)(void *, struct ifnet *);
|
typedef void (*ether_ifattach_event_handler_t)(void *, struct ifnet *);
|
||||||
EVENTHANDLER_DECLARE(ether_ifattach_event, ether_ifattach_event_handler_t);
|
EVENTHANDLER_DECLARE(ether_ifattach_event, ether_ifattach_event_handler_t);
|
||||||
|
@ -571,7 +571,7 @@ if_clone_addgroup(struct ifnet *ifp, struct if_clone *ifc)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* A utility function to extract unit numbers from interface names of
|
* 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.
|
* 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 cutoff = INT_MAX / 10;
|
||||||
int cutlim = 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') {
|
if (*cp == '\0') {
|
||||||
*unit = -1;
|
*unit = -1;
|
||||||
} else if (cp[0] == '0' && cp[1] != '\0') {
|
} else if (cp[0] == '0' && cp[1] != '\0') {
|
||||||
|
@ -441,11 +441,18 @@ bad: if (m != NULL)
|
|||||||
static bool
|
static bool
|
||||||
ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp)
|
ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp)
|
||||||
{
|
{
|
||||||
|
struct ether_8021q_tag qtag;
|
||||||
struct ether_header *eh;
|
struct ether_header *eh;
|
||||||
|
|
||||||
eh = mtod(*mp, struct ether_header *);
|
eh = mtod(*mp, struct ether_header *);
|
||||||
if (ntohs(eh->ether_type) == ETHERTYPE_VLAN ||
|
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);
|
return (true);
|
||||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
||||||
return (false);
|
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,
|
* 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
|
* to allow 802.1P priority frames to be passed to the main input
|
||||||
* path correctly.
|
* 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;
|
struct ether_vlan_header *evl;
|
||||||
|
|
||||||
if (m->m_len < sizeof(*evl) &&
|
if (m->m_len < sizeof(*evl) &&
|
||||||
@ -1303,7 +1310,7 @@ ether_vlan_mtap(struct bpf_if *bp, struct mbuf *m, void *data, u_int dlen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct mbuf *
|
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;
|
struct ether_vlan_header *evl;
|
||||||
|
|
||||||
@ -1325,7 +1332,7 @@ ether_vlanencap(struct mbuf *m, uint16_t tag)
|
|||||||
evl = mtod(m, struct ether_vlan_header *);
|
evl = mtod(m, struct ether_vlan_header *);
|
||||||
bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
|
bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
|
||||||
(char *)evl, ETHER_HDR_LEN - ETHER_TYPE_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);
|
evl->evl_tag = htons(tag);
|
||||||
return (m);
|
return (m);
|
||||||
}
|
}
|
||||||
@ -1354,7 +1361,7 @@ SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW,
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p,
|
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;
|
struct m_tag *mtag;
|
||||||
int n;
|
int n;
|
||||||
@ -1391,7 +1398,7 @@ ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p,
|
|||||||
* If PCP is set in mbuf, use it
|
* If PCP is set in mbuf, use it
|
||||||
*/
|
*/
|
||||||
if ((*mp)->m_flags & M_VLANTAG) {
|
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, struct ifnet *p,
|
|||||||
*/
|
*/
|
||||||
if (vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q,
|
if (vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q,
|
||||||
MTAG_8021Q_PCP_OUT, NULL)) != NULL)
|
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
|
else
|
||||||
tag = EVL_MAKETAG(vid, pcp, 0);
|
tag = EVL_MAKETAG(qtag->vid, qtag->pcp, 0);
|
||||||
if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
|
if ((p->if_capenable & IFCAP_VLAN_HWTAGGING) &&
|
||||||
|
(qtag->proto == ETHERTYPE_VLAN)) {
|
||||||
(*mp)->m_pkthdr.ether_vtag = tag;
|
(*mp)->m_pkthdr.ether_vtag = tag;
|
||||||
(*mp)->m_flags |= M_VLANTAG;
|
(*mp)->m_flags |= M_VLANTAG;
|
||||||
} else {
|
} else {
|
||||||
*mp = ether_vlanencap(*mp, tag);
|
*mp = ether_vlanencap_proto(*mp, tag, qtag->proto);
|
||||||
if (*mp == NULL) {
|
if (*mp == NULL) {
|
||||||
if_printf(ife, "unable to prepend 802.1Q header");
|
if_printf(ife, "unable to prepend 802.1Q header");
|
||||||
return (false);
|
return (false);
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* no representations about the suitability of this software for any
|
* no representations about the suitability of this software for any
|
||||||
* purpose. It is provided "as is" without express or implied
|
* purpose. It is provided "as is" without express or implied
|
||||||
* warranty.
|
* warranty.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
@ -185,17 +185,17 @@ struct ifvlan {
|
|||||||
struct ifvlantrunk *ifv_trunk;
|
struct ifvlantrunk *ifv_trunk;
|
||||||
struct ifnet *ifv_ifp;
|
struct ifnet *ifv_ifp;
|
||||||
#define TRUNK(ifv) ((ifv)->ifv_trunk)
|
#define TRUNK(ifv) ((ifv)->ifv_trunk)
|
||||||
#define PARENT(ifv) ((ifv)->ifv_trunk->parent)
|
#define PARENT(ifv) (TRUNK(ifv)->parent)
|
||||||
void *ifv_cookie;
|
void *ifv_cookie;
|
||||||
int ifv_pflags; /* special flags we have set on parent */
|
int ifv_pflags; /* special flags we have set on parent */
|
||||||
int ifv_capenable;
|
int ifv_capenable;
|
||||||
int ifv_encaplen; /* encapsulation length */
|
int ifv_encaplen; /* encapsulation length */
|
||||||
int ifv_mtufudge; /* MTU fudged by this much */
|
int ifv_mtufudge; /* MTU fudged by this much */
|
||||||
int ifv_mintu; /* min transmission unit */
|
int ifv_mintu; /* min transmission unit */
|
||||||
uint16_t ifv_proto; /* encapsulation ethertype */
|
struct ether_8021q_tag ifv_qtag;
|
||||||
uint16_t ifv_tag; /* tag to apply on packets leaving if */
|
#define ifv_proto ifv_qtag.proto
|
||||||
uint16_t ifv_vid; /* VLAN ID */
|
#define ifv_vid ifv_qtag.vid
|
||||||
uint8_t ifv_pcp; /* Priority Code Point (PCP). */
|
#define ifv_pcp ifv_qtag.pcp
|
||||||
struct task lladdr_task;
|
struct task lladdr_task;
|
||||||
CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
|
CK_SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
|
||||||
#ifndef VLAN_ARRAY
|
#ifndef VLAN_ARRAY
|
||||||
@ -222,9 +222,9 @@ static eventhandler_tag ifdetach_tag;
|
|||||||
static eventhandler_tag iflladdr_tag;
|
static eventhandler_tag iflladdr_tag;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if_vlan uses two module-level synchronizations primitives to allow concurrent
|
* if_vlan uses two module-level synchronizations primitives to allow concurrent
|
||||||
* modification of vlan interfaces and (mostly) allow for vlans to be destroyed
|
* 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
|
* 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
|
* acceptable performance and cooperation with other parts of the network stack
|
||||||
* there is a non-sleepable epoch(9) and an sx(9).
|
* 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;
|
static struct sx _VLAN_SX_ID;
|
||||||
|
|
||||||
#define VLAN_LOCKING_INIT() \
|
#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() \
|
#define VLAN_LOCKING_DESTROY() \
|
||||||
sx_destroy(&_VLAN_SX_ID)
|
sx_destroy(&_VLAN_SX_ID)
|
||||||
@ -306,7 +306,8 @@ static int vlan_output(struct ifnet *ifp, struct mbuf *m,
|
|||||||
const struct sockaddr *dst, struct route *ro);
|
const struct sockaddr *dst, struct route *ro);
|
||||||
static void vlan_unconfig(struct ifnet *ifp);
|
static void vlan_unconfig(struct ifnet *ifp);
|
||||||
static void vlan_unconfig_locked(struct ifnet *ifp, int departing);
|
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_link_state(struct ifnet *ifp);
|
||||||
static void vlan_capabilities(struct ifvlan *ifv);
|
static void vlan_capabilities(struct ifvlan *ifv);
|
||||||
static void vlan_trunk_capabilities(struct ifnet *ifp);
|
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
|
* 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 *
|
static void *
|
||||||
vlan_cookie(struct ifnet *ifp)
|
vlan_cookie(struct ifnet *ifp)
|
||||||
@ -810,16 +811,6 @@ vlan_devat(struct ifnet *ifp, uint16_t vid)
|
|||||||
return (ifp);
|
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
|
* 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
|
* system that's intimately aware of this is ether_input. We hook
|
||||||
@ -867,7 +858,7 @@ vlan_modevent(module_t mod, int type, void *data)
|
|||||||
#else
|
#else
|
||||||
"hash tables with chaining"
|
"hash tables with chaining"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
"\n");
|
"\n");
|
||||||
break;
|
break;
|
||||||
case MOD_UNLOAD:
|
case MOD_UNLOAD:
|
||||||
@ -926,7 +917,7 @@ VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_ANY,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for <etherif>.<vlan> style interface names.
|
* Check for <etherif>.<vlan>[.<vlan> ...] style interface names.
|
||||||
*/
|
*/
|
||||||
static struct ifnet *
|
static struct ifnet *
|
||||||
vlan_clone_match_ethervid(const char *name, int *vidp)
|
vlan_clone_match_ethervid(const char *name, int *vidp)
|
||||||
@ -937,7 +928,7 @@ vlan_clone_match_ethervid(const char *name, int *vidp)
|
|||||||
int vid;
|
int vid;
|
||||||
|
|
||||||
strlcpy(ifname, name, IFNAMSIZ);
|
strlcpy(ifname, name, IFNAMSIZ);
|
||||||
if ((cp = strchr(ifname, '.')) == NULL)
|
if ((cp = strrchr(ifname, '.')) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
if ((ifp = ifunit_ref(ifname)) == NULL)
|
if ((ifp = ifunit_ref(ifname)) == NULL)
|
||||||
@ -990,6 +981,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
|
|||||||
int unit;
|
int unit;
|
||||||
int error;
|
int error;
|
||||||
int vid;
|
int vid;
|
||||||
|
uint16_t proto;
|
||||||
struct ifvlan *ifv;
|
struct ifvlan *ifv;
|
||||||
struct ifnet *ifp;
|
struct ifnet *ifp;
|
||||||
struct ifnet *p;
|
struct ifnet *p;
|
||||||
@ -998,14 +990,15 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
|
|||||||
struct vlanreq vlr;
|
struct vlanreq vlr;
|
||||||
static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
|
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 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
|
* o specify no parameters and get an unattached device that
|
||||||
* must be configured separately.
|
* must be configured separately.
|
||||||
* The first technique is preferred; the latter two are
|
* The first technique is preferred; the latter is supported
|
||||||
* supported for backwards compatibility.
|
* for backwards compatibility.
|
||||||
*
|
*
|
||||||
* XXXRW: Note historic use of the word "tag" here. New ioctls may be
|
* XXXRW: Note historic use of the word "tag" here. New ioctls may be
|
||||||
* called for.
|
* called for.
|
||||||
@ -1023,10 +1016,8 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
vid = vlr.vlr_tag;
|
vid = vlr.vlr_tag;
|
||||||
|
proto = vlr.vlr_proto;
|
||||||
wildcard = (unit < 0);
|
wildcard = (unit < 0);
|
||||||
} else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) {
|
|
||||||
unit = -1;
|
|
||||||
wildcard = 0;
|
|
||||||
} else {
|
} else {
|
||||||
p = NULL;
|
p = NULL;
|
||||||
error = ifc_name2unit(name, &unit);
|
error = ifc_name2unit(name, &unit);
|
||||||
@ -1092,7 +1083,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
|
|||||||
sdl->sdl_type = IFT_L2VLAN;
|
sdl->sdl_type = IFT_L2VLAN;
|
||||||
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
error = vlan_config(ifv, p, vid);
|
error = vlan_config(ifv, p, vid, proto);
|
||||||
if_rele(p);
|
if_rele(p);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
/*
|
/*
|
||||||
@ -1117,7 +1108,9 @@ static int
|
|||||||
vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
|
vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
|
||||||
{
|
{
|
||||||
struct ifvlan *ifv = ifp->if_softc;
|
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 */
|
ether_ifdetach(ifp); /* first, remove it from system-wide lists */
|
||||||
vlan_unconfig(ifp); /* now it can be unconfigured and freed */
|
vlan_unconfig(ifp); /* now it can be unconfigured and freed */
|
||||||
@ -1130,7 +1123,7 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
|
|||||||
NET_EPOCH_WAIT();
|
NET_EPOCH_WAIT();
|
||||||
if_free(ifp);
|
if_free(ifp);
|
||||||
free(ifv, M_VLAN);
|
free(ifv, M_VLAN);
|
||||||
ifc_free_unit(ifc, unit);
|
ifc_free_unit(ifc, ifp->if_dunit);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -1196,7 +1189,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
|
|||||||
return (ENETDOWN);
|
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);
|
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -1223,12 +1216,19 @@ vlan_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
|
|||||||
|
|
||||||
NET_EPOCH_ASSERT();
|
NET_EPOCH_ASSERT();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the first non-VLAN parent interface.
|
||||||
|
*/
|
||||||
ifv = ifp->if_softc;
|
ifv = ifp->if_softc;
|
||||||
if (TRUNK(ifv) == NULL) {
|
do {
|
||||||
m_freem(m);
|
if (TRUNK(ifv) == NULL) {
|
||||||
return (ENETDOWN);
|
m_freem(m);
|
||||||
}
|
return (ENETDOWN);
|
||||||
p = PARENT(ifv);
|
}
|
||||||
|
p = PARENT(ifv);
|
||||||
|
ifv = p->if_softc;
|
||||||
|
} while (p->if_type == IFT_L2VLAN);
|
||||||
|
|
||||||
return p->if_output(ifp, m, dst, ro);
|
return p->if_output(ifp, m, dst, ro);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1357,7 +1357,8 @@ vlan_lladdr_fn(void *arg, int pending __unused)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 epoch_tracker et;
|
||||||
struct ifvlantrunk *trunk;
|
struct ifvlantrunk *trunk;
|
||||||
@ -1369,6 +1370,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid)
|
|||||||
* they handle the tagging and headers themselves.
|
* they handle the tagging and headers themselves.
|
||||||
*/
|
*/
|
||||||
if (p->if_type != IFT_ETHER &&
|
if (p->if_type != IFT_ETHER &&
|
||||||
|
p->if_type != IFT_L2VLAN &&
|
||||||
(p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
|
(p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
|
||||||
return (EPROTONOSUPPORT);
|
return (EPROTONOSUPPORT);
|
||||||
if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS)
|
if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS)
|
||||||
@ -1400,11 +1402,10 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid)
|
|||||||
|
|
||||||
ifv->ifv_vid = vid; /* must set this before vlan_inshash() */
|
ifv->ifv_vid = vid; /* must set this before vlan_inshash() */
|
||||||
ifv->ifv_pcp = 0; /* Default: best effort delivery. */
|
ifv->ifv_pcp = 0; /* Default: best effort delivery. */
|
||||||
vlan_tag_recalculate(ifv);
|
|
||||||
error = vlan_inshash(trunk, ifv);
|
error = vlan_inshash(trunk, ifv);
|
||||||
if (error)
|
if (error)
|
||||||
goto done;
|
goto done;
|
||||||
ifv->ifv_proto = ETHERTYPE_VLAN;
|
ifv->ifv_proto = proto;
|
||||||
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
|
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
|
||||||
ifv->ifv_mintu = ETHERMIN;
|
ifv->ifv_mintu = ETHERMIN;
|
||||||
ifv->ifv_pflags = 0;
|
ifv->ifv_pflags = 0;
|
||||||
@ -1915,7 +1916,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
oldmtu = ifp->if_mtu;
|
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);
|
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,
|
strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname,
|
||||||
sizeof(vlr.vlr_parent));
|
sizeof(vlr.vlr_parent));
|
||||||
vlr.vlr_tag = ifv->ifv_vid;
|
vlr.vlr_tag = ifv->ifv_vid;
|
||||||
|
vlr.vlr_proto = ifv->ifv_proto;
|
||||||
}
|
}
|
||||||
VLAN_SUNLOCK();
|
VLAN_SUNLOCK();
|
||||||
error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr));
|
error = copyout(&vlr, ifr_data_get_ptr(ifr), sizeof(vlr));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIOCSIFFLAGS:
|
case SIOCSIFFLAGS:
|
||||||
/*
|
/*
|
||||||
* We should propagate selected flags to the parent,
|
* 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;
|
ifv->ifv_pcp = ifr->ifr_vlan_pcp;
|
||||||
ifp->if_pcp = ifv->ifv_pcp;
|
ifp->if_pcp = ifv->ifv_pcp;
|
||||||
vlan_tag_recalculate(ifv);
|
|
||||||
/* broadcast event about PCP change */
|
/* broadcast event about PCP change */
|
||||||
EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP);
|
EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP);
|
||||||
break;
|
break;
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
struct vlanreq {
|
struct vlanreq {
|
||||||
char vlr_parent[IFNAMSIZ];
|
char vlr_parent[IFNAMSIZ];
|
||||||
u_short vlr_tag;
|
u_short vlr_tag;
|
||||||
|
u_short vlr_proto;
|
||||||
};
|
};
|
||||||
#define SIOCSETVLAN SIOCSIFGENERIC
|
#define SIOCSETVLAN SIOCSIFGENERIC
|
||||||
#define SIOCGETVLAN SIOCGIFGENERIC
|
#define SIOCGETVLAN SIOCGIFGENERIC
|
||||||
@ -123,6 +124,15 @@ struct vlanreq {
|
|||||||
#define MTAG_8021Q_PCP_IN 0 /* Input priority. */
|
#define MTAG_8021Q_PCP_IN 0 /* Input priority. */
|
||||||
#define MTAG_8021Q_PCP_OUT 1 /* Output 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 { \
|
#define VLAN_CAPABILITIES(_ifp) do { \
|
||||||
if ((_ifp)->if_vlantrunk != NULL) \
|
if ((_ifp)->if_vlantrunk != NULL) \
|
||||||
(*vlan_trunk_cap_p)(_ifp); \
|
(*vlan_trunk_cap_p)(_ifp); \
|
||||||
|
@ -36,7 +36,185 @@ basic_cleanup()
|
|||||||
vnet_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_init_test_cases()
|
||||||
{
|
{
|
||||||
atf_add_test_case "basic"
|
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"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user