summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net/if_vlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/net/if_vlan.c')
-rw-r--r--freebsd/sys/net/if_vlan.c401
1 files changed, 247 insertions, 154 deletions
diff --git a/freebsd/sys/net/if_vlan.c b/freebsd/sys/net/if_vlan.c
index 7d08e298..8a93565b 100644
--- a/freebsd/sys/net/if_vlan.c
+++ b/freebsd/sys/net/if_vlan.c
@@ -2,6 +2,10 @@
/*-
* Copyright 1998 Massachusetts Institute of Technology
+ * Copyright 2012 ADARA Networks, Inc.
+ *
+ * Portions of this software were developed by Robert N. M. Watson under
+ * contract to ADARA Networks, Inc.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
@@ -31,8 +35,7 @@
/*
* if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
- * Might be extended some day to also handle IEEE 802.1p priority
- * tagging. This is sort of sneaky in the implementation, since
+ * This is sort of sneaky in the implementation, since
* we need to pretend to be enough of an Ethernet implementation
* to make arp work. The way we do this is by telling everyone
* that we are an Ethernet, and then catch the packets that
@@ -47,12 +50,14 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_vlan.h>
#include <rtems/bsd/sys/param.h>
+#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <rtems/bsd/sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
-#include <sys/rwlock.h>
+#include <sys/rmlock.h>
+#include <sys/priv.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@@ -63,6 +68,7 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_clone.h>
#include <net/if_dl.h>
#include <net/if_types.h>
@@ -74,7 +80,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/if_ether.h>
#endif
-#define VLANNAME "vlan"
#define VLAN_DEF_HWIDTH 4
#define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST)
@@ -85,7 +90,7 @@ LIST_HEAD(ifvlanhead, ifvlan);
struct ifvlantrunk {
struct ifnet *parent; /* parent interface of this trunk */
- struct rwlock rw;
+ struct rmlock lock;
#ifdef VLAN_ARRAY
#define VLAN_ARRAY_SIZE (EVL_VLID_MASK + 1)
struct ifvlan *vlans[VLAN_ARRAY_SIZE]; /* static table */
@@ -105,9 +110,9 @@ struct vlan_mc_entry {
struct ifvlan {
struct ifvlantrunk *ifv_trunk;
struct ifnet *ifv_ifp;
- void *ifv_cookie;
#define TRUNK(ifv) ((ifv)->ifv_trunk)
#define PARENT(ifv) ((ifv)->ifv_trunk->parent)
+ void *ifv_cookie;
int ifv_pflags; /* special flags we have set on parent */
struct ifv_linkmib {
int ifvm_encaplen; /* encapsulation length */
@@ -115,6 +120,8 @@ struct ifvlan {
int ifvm_mintu; /* min transmission unit */
uint16_t ifvm_proto; /* encapsulation ethertype */
uint16_t ifvm_tag; /* tag to apply on packets leaving if */
+ uint16_t ifvm_vid; /* VLAN ID */
+ uint8_t ifvm_pcp; /* Priority Code Point (PCP). */
} ifv_mib;
SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
#ifndef VLAN_ARRAY
@@ -123,6 +130,8 @@ struct ifvlan {
};
#define ifv_proto ifv_mib.ifvm_proto
#define ifv_tag ifv_mib.ifvm_tag
+#define ifv_vid ifv_mib.ifvm_vid
+#define ifv_pcp ifv_mib.ifvm_pcp
#define ifv_encaplen ifv_mib.ifvm_encaplen
#define ifv_mtufudge ifv_mib.ifvm_mtufudge
#define ifv_mintu ifv_mib.ifvm_mintu
@@ -143,11 +152,22 @@ static SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0,
static SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0,
"for consistency");
-static int soft_pad = 0;
-SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0,
- "pad short frames before tagging");
+static VNET_DEFINE(int, soft_pad);
+#define V_soft_pad VNET(soft_pad)
+SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET,
+ &VNET_NAME(soft_pad), 0, "pad short frames before tagging");
+
+/*
+ * For now, make preserving PCP via an mbuf tag optional, as it increases
+ * per-packet memory allocations and frees. In the future, it would be
+ * preferable to reuse ether_vtag for this, or similar.
+ */
+static int vlan_mtag_pcp = 0;
+SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW, &vlan_mtag_pcp, 0,
+ "Retain VLAN PCP information as packets are passed up the stack");
-static MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface");
+static const char vlanname[] = "vlan";
+static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface");
static eventhandler_tag ifdetach_tag;
static eventhandler_tag iflladdr_tag;
@@ -156,7 +176,7 @@ static eventhandler_tag iflladdr_tag;
* We have a global mutex, that is used to serialize configuration
* changes and isn't used in normal packet delivery.
*
- * We also have a per-trunk rwlock, that is locked shared on packet
+ * We also have a per-trunk rmlock(9), that is locked shared on packet
* processing and exclusive when configuration is changed.
*
* The VLAN_ARRAY substitutes the dynamic hash with a static array
@@ -170,14 +190,15 @@ static struct sx ifv_lock;
#define VLAN_LOCK_ASSERT() sx_assert(&ifv_lock, SA_LOCKED)
#define VLAN_LOCK() sx_xlock(&ifv_lock)
#define VLAN_UNLOCK() sx_xunlock(&ifv_lock)
-#define TRUNK_LOCK_INIT(trunk) rw_init(&(trunk)->rw, VLANNAME)
-#define TRUNK_LOCK_DESTROY(trunk) rw_destroy(&(trunk)->rw)
-#define TRUNK_LOCK(trunk) rw_wlock(&(trunk)->rw)
-#define TRUNK_UNLOCK(trunk) rw_wunlock(&(trunk)->rw)
-#define TRUNK_LOCK_ASSERT(trunk) rw_assert(&(trunk)->rw, RA_WLOCKED)
-#define TRUNK_RLOCK(trunk) rw_rlock(&(trunk)->rw)
-#define TRUNK_RUNLOCK(trunk) rw_runlock(&(trunk)->rw)
-#define TRUNK_LOCK_RASSERT(trunk) rw_assert(&(trunk)->rw, RA_RLOCKED)
+#define TRUNK_LOCK_INIT(trunk) rm_init(&(trunk)->lock, vlanname)
+#define TRUNK_LOCK_DESTROY(trunk) rm_destroy(&(trunk)->lock)
+#define TRUNK_LOCK(trunk) rm_wlock(&(trunk)->lock)
+#define TRUNK_UNLOCK(trunk) rm_wunlock(&(trunk)->lock)
+#define TRUNK_LOCK_ASSERT(trunk) rm_assert(&(trunk)->lock, RA_WLOCKED)
+#define TRUNK_RLOCK(trunk) rm_rlock(&(trunk)->lock, &tracker)
+#define TRUNK_RUNLOCK(trunk) rm_runlock(&(trunk)->lock, &tracker)
+#define TRUNK_LOCK_RASSERT(trunk) rm_assert(&(trunk)->lock, RA_RLOCKED)
+#define TRUNK_LOCK_READER struct rm_priotracker tracker
#ifndef VLAN_ARRAY
static void vlan_inithash(struct ifvlantrunk *trunk);
@@ -186,7 +207,7 @@ static int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv);
static int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv);
static void vlan_growhash(struct ifvlantrunk *trunk, int howmuch);
static __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk,
- uint16_t tag);
+ uint16_t vid);
#endif
static void trunk_destroy(struct ifvlantrunk *trunk);
@@ -206,8 +227,7 @@ 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_ethertag(struct if_clone *,
- const char *, int *);
+static struct ifnet *vlan_clone_match_ethervid(const char *, int *);
static int vlan_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 *);
@@ -215,11 +235,10 @@ static int vlan_clone_destroy(struct if_clone *, struct ifnet *);
static void vlan_ifdetach(void *arg, struct ifnet *ifp);
static void vlan_iflladdr(void *arg, struct ifnet *ifp);
-static struct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL,
- IF_MAXUNIT, NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy);
+static struct if_clone *vlan_cloner;
#ifdef VIMAGE
-static VNET_DEFINE(struct if_clone, vlan_cloner);
+static VNET_DEFINE(struct if_clone *, vlan_cloner);
#define V_vlan_cloner VNET(vlan_cloner)
#endif
@@ -274,9 +293,9 @@ vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
b = 1 << trunk->hwidth;
- i = HASH(ifv->ifv_tag, trunk->hmask);
+ i = HASH(ifv->ifv_vid, trunk->hmask);
LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list)
- if (ifv->ifv_tag == ifv2->ifv_tag)
+ if (ifv->ifv_vid == ifv2->ifv_vid)
return (EEXIST);
/*
@@ -286,7 +305,7 @@ vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
*/
if (trunk->refcnt > (b * b) / 2) {
vlan_growhash(trunk, 1);
- i = HASH(ifv->ifv_tag, trunk->hmask);
+ i = HASH(ifv->ifv_vid, trunk->hmask);
}
LIST_INSERT_HEAD(&trunk->hash[i], ifv, ifv_list);
trunk->refcnt++;
@@ -304,7 +323,7 @@ vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__));
b = 1 << trunk->hwidth;
- i = HASH(ifv->ifv_tag, trunk->hmask);
+ i = HASH(ifv->ifv_vid, trunk->hmask);
LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list)
if (ifv2 == ifv) {
trunk->refcnt--;
@@ -356,7 +375,7 @@ vlan_growhash(struct ifvlantrunk *trunk, int howmuch)
for (i = 0; i < n; i++)
while ((ifv = LIST_FIRST(&trunk->hash[i])) != NULL) {
LIST_REMOVE(ifv, ifv_list);
- j = HASH(ifv->ifv_tag, n2 - 1);
+ j = HASH(ifv->ifv_vid, n2 - 1);
LIST_INSERT_HEAD(&hash2[j], ifv, ifv_list);
}
free(trunk->hash, M_VLAN);
@@ -370,14 +389,14 @@ vlan_growhash(struct ifvlantrunk *trunk, int howmuch)
}
static __inline struct ifvlan *
-vlan_gethash(struct ifvlantrunk *trunk, uint16_t tag)
+vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid)
{
struct ifvlan *ifv;
TRUNK_LOCK_RASSERT(trunk);
- LIST_FOREACH(ifv, &trunk->hash[HASH(tag, trunk->hmask)], ifv_list)
- if (ifv->ifv_tag == tag)
+ LIST_FOREACH(ifv, &trunk->hash[HASH(vid, trunk->hmask)], ifv_list)
+ if (ifv->ifv_vid == vid)
return (ifv);
return (NULL);
}
@@ -401,19 +420,19 @@ vlan_dumphash(struct ifvlantrunk *trunk)
#else
static __inline struct ifvlan *
-vlan_gethash(struct ifvlantrunk *trunk, uint16_t tag)
+vlan_gethash(struct ifvlantrunk *trunk, uint16_t vid)
{
- return trunk->vlans[tag];
+ return trunk->vlans[vid];
}
static __inline int
vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
{
- if (trunk->vlans[ifv->ifv_tag] != NULL)
+ if (trunk->vlans[ifv->ifv_vid] != NULL)
return EEXIST;
- trunk->vlans[ifv->ifv_tag] = ifv;
+ trunk->vlans[ifv->ifv_vid] = ifv;
trunk->refcnt++;
return (0);
@@ -423,7 +442,7 @@ static __inline int
vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
{
- trunk->vlans[ifv->ifv_tag] = NULL;
+ trunk->vlans[ifv->ifv_vid] = NULL;
trunk->refcnt--;
return (0);
@@ -461,48 +480,48 @@ trunk_destroy(struct ifvlantrunk *trunk)
* traffic that it doesn't really want, which ends up being discarded
* later by the upper protocol layers. Unfortunately, there's no way
* to avoid this: there really is only one physical interface.
- *
- * XXX: There is a possible race here if more than one thread is
- * modifying the multicast state of the vlan interface at the same time.
*/
static int
vlan_setmulti(struct ifnet *ifp)
{
struct ifnet *ifp_p;
- struct ifmultiaddr *ifma, *rifma = NULL;
+ struct ifmultiaddr *ifma;
struct ifvlan *sc;
struct vlan_mc_entry *mc;
int error;
- /*VLAN_LOCK_ASSERT();*/
-
/* Find the parent. */
sc = ifp->if_softc;
+ TRUNK_LOCK_ASSERT(TRUNK(sc));
ifp_p = PARENT(sc);
CURVNET_SET_QUIET(ifp_p->if_vnet);
/* First, remove any existing filter entries. */
while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) {
- error = if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr);
- if (error)
- return (error);
SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
+ (void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr);
free(mc, M_VLAN);
}
/* Now program new ones. */
+ IF_ADDR_WLOCK(ifp);
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT);
- if (mc == NULL)
+ if (mc == NULL) {
+ IF_ADDR_WUNLOCK(ifp);
return (ENOMEM);
+ }
bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len);
mc->mc_addr.sdl_index = ifp_p->if_index;
SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
+ }
+ IF_ADDR_WUNLOCK(ifp);
+ SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) {
error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr,
- &rifma);
+ NULL);
if (error)
return (error);
}
@@ -625,17 +644,21 @@ vlan_trunkdev(struct ifnet *ifp)
}
/*
- * Return the 16bit vlan tag for this interface.
+ * Return the 12-bit VLAN VID for this interface, for use by external
+ * components such as Infiniband.
+ *
+ * XXXRW: Note that the function name here is historical; it should be named
+ * vlan_vid().
*/
static int
-vlan_tag(struct ifnet *ifp, uint16_t *tagp)
+vlan_tag(struct ifnet *ifp, uint16_t *vidp)
{
struct ifvlan *ifv;
if (ifp->if_type != IFT_L2VLAN)
return (EINVAL);
ifv = ifp->if_softc;
- *tagp = ifv->ifv_tag;
+ *vidp = ifv->ifv_vid;
return (0);
}
@@ -671,20 +694,21 @@ vlan_setcookie(struct ifnet *ifp, void *cookie)
}
/*
- * Return the vlan device present at the specific tag.
+ * Return the vlan device present at the specific VID.
*/
static struct ifnet *
-vlan_devat(struct ifnet *ifp, uint16_t tag)
+vlan_devat(struct ifnet *ifp, uint16_t vid)
{
struct ifvlantrunk *trunk;
struct ifvlan *ifv;
+ TRUNK_LOCK_READER;
trunk = ifp->if_vlantrunk;
if (trunk == NULL)
return (NULL);
ifp = NULL;
TRUNK_RLOCK(trunk);
- ifv = vlan_gethash(trunk, tag);
+ ifv = vlan_gethash(trunk, vid);
if (ifv)
ifp = ifv->ifv_ifp;
TRUNK_RUNLOCK(trunk);
@@ -692,10 +716,20 @@ vlan_devat(struct ifnet *ifp, uint16_t tag)
}
/*
+ * 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
- * set here. Noone else in the system should be aware of this so
+ * set here. No one else in the system should be aware of this so
* we use an explicit reference here.
*/
extern void (*vlan_input_p)(struct ifnet *, struct mbuf *);
@@ -727,7 +761,8 @@ vlan_modevent(module_t mod, int type, void *data)
vlan_tag_p = vlan_tag;
vlan_devat_p = vlan_devat;
#ifndef VIMAGE
- if_clone_attach(&vlan_cloner);
+ vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match,
+ vlan_clone_create, vlan_clone_destroy);
#endif
if (bootverbose)
printf("vlan: initialized, using "
@@ -741,7 +776,7 @@ vlan_modevent(module_t mod, int type, void *data)
break;
case MOD_UNLOAD:
#ifndef VIMAGE
- if_clone_detach(&vlan_cloner);
+ if_clone_detach(vlan_cloner);
#endif
EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag);
EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag);
@@ -777,8 +812,9 @@ static void
vnet_vlan_init(const void *unused __unused)
{
+ vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match,
+ vlan_clone_create, vlan_clone_destroy);
V_vlan_cloner = vlan_cloner;
- if_clone_attach(&V_vlan_cloner);
}
VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
vnet_vlan_init, NULL);
@@ -787,46 +823,39 @@ static void
vnet_vlan_uninit(const void *unused __unused)
{
- if_clone_detach(&V_vlan_cloner);
+ if_clone_detach(V_vlan_cloner);
}
-VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST,
+VNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST,
vnet_vlan_uninit, NULL);
#endif
+/*
+ * Check for <etherif>.<vlan> style interface names.
+ */
static struct ifnet *
-vlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag)
+vlan_clone_match_ethervid(const char *name, int *vidp)
{
- const char *cp;
+ char ifname[IFNAMSIZ];
+ char *cp;
struct ifnet *ifp;
- int t;
+ int vid;
- /* Check for <etherif>.<vlan> style interface names. */
- IFNET_RLOCK_NOSLEEP();
- TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
- /*
- * We can handle non-ethernet hardware types as long as
- * they handle the tagging and headers themselves.
- */
- if (ifp->if_type != IFT_ETHER &&
- (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
- continue;
- if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0)
- continue;
- cp = name + strlen(ifp->if_xname);
- if (*cp++ != '.')
- continue;
- if (*cp == '\0')
- continue;
- t = 0;
- for(; *cp >= '0' && *cp <= '9'; cp++)
- t = (t * 10) + (*cp - '0');
- if (*cp != '\0')
- continue;
- if (tag != NULL)
- *tag = t;
- break;
- }
- IFNET_RUNLOCK_NOSLEEP();
+ strlcpy(ifname, name, IFNAMSIZ);
+ if ((cp = strchr(ifname, '.')) == NULL)
+ return (NULL);
+ *cp = '\0';
+ if ((ifp = ifunit(ifname)) == NULL)
+ return (NULL);
+ /* Parse VID. */
+ if (*++cp == '\0')
+ return (NULL);
+ vid = 0;
+ for(; *cp >= '0' && *cp <= '9'; cp++)
+ vid = (vid * 10) + (*cp - '0');
+ if (*cp != '\0')
+ return (NULL);
+ if (vidp != NULL)
+ *vidp = vid;
return (ifp);
}
@@ -836,10 +865,10 @@ vlan_clone_match(struct if_clone *ifc, const char *name)
{
const char *cp;
- if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL)
+ if (vlan_clone_match_ethervid(name, NULL) != NULL)
return (1);
- if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0)
+ if (strncmp(vlanname, name, strlen(vlanname)) != 0)
return (0);
for (cp = name + 4; *cp != '\0'; cp++) {
if (*cp < '0' || *cp > '9')
@@ -856,7 +885,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
int wildcard;
int unit;
int error;
- int tag;
+ int vid;
int ethertag;
struct ifvlan *ifv;
struct ifnet *ifp;
@@ -873,7 +902,10 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
* 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 compatibilty.
+ * supported for backwards compatibility.
+ *
+ * XXXRW: Note historic use of the word "tag" here. New ioctls may be
+ * called for.
*/
if (params) {
error = copyin(params, &vlr, sizeof(vlr));
@@ -881,31 +913,18 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
return error;
p = ifunit(vlr.vlr_parent);
if (p == NULL)
- return ENXIO;
- /*
- * Don't let the caller set up a VLAN tag with
- * anything except VLID bits.
- */
- if (vlr.vlr_tag & ~EVL_VLID_MASK)
- return (EINVAL);
+ return (ENXIO);
error = ifc_name2unit(name, &unit);
if (error != 0)
return (error);
ethertag = 1;
- tag = vlr.vlr_tag;
+ vid = vlr.vlr_tag;
wildcard = (unit < 0);
- } else if ((p = vlan_clone_match_ethertag(ifc, name, &tag)) != NULL) {
+ } else if ((p = vlan_clone_match_ethervid(name, &vid)) != NULL) {
ethertag = 1;
unit = -1;
wildcard = 0;
-
- /*
- * Don't let the caller set up a VLAN tag with
- * anything except VLID bits.
- */
- if (tag & ~EVL_VLID_MASK)
- return (EINVAL);
} else {
ethertag = 0;
@@ -937,14 +956,13 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
return (ENOSPC);
}
SLIST_INIT(&ifv->vlan_mc_listhead);
-
ifp->if_softc = ifv;
/*
* Set the name manually rather than using if_initname because
* we don't conform to the default naming convention for interfaces.
*/
strlcpy(ifp->if_xname, name, IFNAMSIZ);
- ifp->if_dname = ifc->ifc_name;
+ ifp->if_dname = vlanname;
ifp->if_dunit = unit;
/* NB: flags are not set here */
ifp->if_linkmib = &ifv->ifv_mib;
@@ -966,7 +984,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
sdl->sdl_type = IFT_L2VLAN;
if (ethertag) {
- error = vlan_config(ifv, p, tag);
+ error = vlan_config(ifv, p, vid);
if (error != 0) {
/*
* Since we've partially failed, we need to back
@@ -975,7 +993,7 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
*/
ether_ifdetach(ifp);
vlan_unconfig(ifp);
- if_free_type(ifp, IFT_ETHER);
+ if_free(ifp);
ifc_free_unit(ifc, unit);
free(ifv, M_VLAN);
@@ -997,7 +1015,7 @@ vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp)
ether_ifdetach(ifp); /* first, remove it from system-wide lists */
vlan_unconfig(ifp); /* now it can be unconfigured and freed */
- if_free_type(ifp, IFT_ETHER);
+ if_free(ifp);
free(ifv, M_VLAN);
ifc_free_unit(ifc, unit);
@@ -1020,6 +1038,8 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
{
struct ifvlan *ifv;
struct ifnet *p;
+ struct m_tag *mtag;
+ uint16_t tag;
int error, len, mcast;
ifv = ifp->if_softc;
@@ -1035,7 +1055,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
*/
if (!UP_AND_RUNNING(p)) {
m_freem(m);
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return (ENETDOWN);
}
@@ -1051,7 +1071,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
* devices that just discard such runts instead or mishandle
* them somehow.
*/
- if (soft_pad && p->if_type == IFT_ETHER) {
+ if (V_soft_pad && p->if_type == IFT_ETHER) {
static char pad[8]; /* just zeros */
int n;
@@ -1062,7 +1082,7 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
if (n > 0) {
if_printf(ifp, "cannot pad short frame\n");
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(m);
return (0);
}
@@ -1075,14 +1095,19 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
* knows how to find the VLAN tag to use, so we attach a
* packet tag that holds it.
*/
+ if (vlan_mtag_pcp && (mtag = m_tag_locate(m, MTAG_8021Q,
+ MTAG_8021Q_PCP_OUT, NULL)) != NULL)
+ tag = EVL_MAKETAG(ifv->ifv_vid, *(uint8_t *)(mtag + 1), 0);
+ else
+ tag = ifv->ifv_tag;
if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
- m->m_pkthdr.ether_vtag = ifv->ifv_tag;
+ m->m_pkthdr.ether_vtag = tag;
m->m_flags |= M_VLANTAG;
} else {
- m = ether_vlanencap(m, ifv->ifv_tag);
+ m = ether_vlanencap(m, tag);
if (m == NULL) {
if_printf(ifp, "unable to prepend VLAN header\n");
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return (0);
}
}
@@ -1091,12 +1116,12 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
* Send it, precisely as ether_output() would have.
*/
error = (p->if_transmit)(p, m);
- if (!error) {
- ifp->if_opackets++;
- ifp->if_omcasts += mcast;
- ifp->if_obytes += len;
+ if (error == 0) {
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast);
} else
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return (error);
}
@@ -1113,7 +1138,9 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
{
struct ifvlantrunk *trunk = ifp->if_vlantrunk;
struct ifvlan *ifv;
- uint16_t tag;
+ TRUNK_LOCK_READER;
+ struct m_tag *mtag;
+ uint16_t vid, tag;
KASSERT(trunk != NULL, ("%s: no trunk", __func__));
@@ -1122,7 +1149,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
* Packet is tagged, but m contains a normal
* Ethernet frame; the tag is stored out-of-band.
*/
- tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
+ tag = m->m_pkthdr.ether_vtag;
m->m_flags &= ~M_VLANTAG;
} else {
struct ether_vlan_header *evl;
@@ -1138,7 +1165,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
return;
}
evl = mtod(m, struct ether_vlan_header *);
- tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+ tag = ntohs(evl->evl_tag);
/*
* Remove the 802.1q header by copying the Ethernet
@@ -1157,43 +1184,75 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
__func__, ifp->if_xname, ifp->if_type);
#endif
m_freem(m);
- ifp->if_noproto++;
+ if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
return;
}
}
+ vid = EVL_VLANOFTAG(tag);
+
TRUNK_RLOCK(trunk);
- ifv = vlan_gethash(trunk, tag);
+ ifv = vlan_gethash(trunk, vid);
if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) {
TRUNK_RUNLOCK(trunk);
m_freem(m);
- ifp->if_noproto++;
+ if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
return;
}
TRUNK_RUNLOCK(trunk);
+ if (vlan_mtag_pcp) {
+ /*
+ * While uncommon, it is possible that we will find a 802.1q
+ * packet encapsulated inside another packet that also had an
+ * 802.1q header. For example, ethernet tunneled over IPSEC
+ * arriving over ethernet. In that case, we replace the
+ * existing 802.1q PCP m_tag value.
+ */
+ mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL);
+ if (mtag == NULL) {
+ mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_IN,
+ sizeof(uint8_t), M_NOWAIT);
+ if (mtag == NULL) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ return;
+ }
+ m_tag_prepend(m, mtag);
+ }
+ *(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag);
+ }
+
m->m_pkthdr.rcvif = ifv->ifv_ifp;
- ifv->ifv_ifp->if_ipackets++;
+ if_inc_counter(ifv->ifv_ifp, IFCOUNTER_IPACKETS, 1);
/* Pass it back through the parent's input routine. */
(*ifp->if_input)(ifv->ifv_ifp, m);
}
static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid)
{
struct ifvlantrunk *trunk;
struct ifnet *ifp;
int error = 0;
- /* VID numbers 0x0 and 0xFFF are reserved */
- if (tag == 0 || tag == 0xFFF)
- return (EINVAL);
+ /*
+ * We can handle non-ethernet hardware types as long as
+ * they handle the tagging and headers themselves.
+ */
if (p->if_type != IFT_ETHER &&
(p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
return (EPROTONOSUPPORT);
if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS)
return (EPROTONOSUPPORT);
+ /*
+ * Don't let the caller set up a VLAN VID with
+ * anything except VLID bits.
+ * VID numbers 0x0 and 0xFFF are reserved.
+ */
+ if (vid == 0 || vid == 0xFFF || (vid & ~EVL_VLID_MASK))
+ return (EINVAL);
if (ifv->ifv_trunk)
return (EBUSY);
@@ -1203,7 +1262,7 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag)
vlan_inithash(trunk);
VLAN_LOCK();
if (p->if_vlantrunk != NULL) {
- /* A race that that is very unlikely to be hit. */
+ /* A race that is very unlikely to be hit. */
vlan_freehash(trunk);
free(trunk, M_VLAN);
goto exists;
@@ -1219,7 +1278,9 @@ exists:
TRUNK_LOCK(trunk);
}
- ifv->ifv_tag = tag; /* must set this before vlan_inshash() */
+ 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;
@@ -1297,7 +1358,7 @@ exists:
done:
TRUNK_UNLOCK(trunk);
if (error == 0)
- EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_tag);
+ EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid);
VLAN_UNLOCK();
return (error);
@@ -1366,7 +1427,7 @@ vlan_unconfig_locked(struct ifnet *ifp, int departing)
* Check if we were the last.
*/
if (trunk->refcnt == 0) {
- trunk->parent->if_vlantrunk = NULL;
+ parent->if_vlantrunk = NULL;
/*
* XXXGL: If some ithread has already entered
* vlan_input() and is now blocked on the trunk
@@ -1393,7 +1454,7 @@ vlan_unconfig_locked(struct ifnet *ifp, int departing)
* to cleanup anyway.
*/
if (parent != NULL)
- EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_tag);
+ EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_vid);
}
/* Handle a reference counted flag that should be set on the parent as well */
@@ -1494,7 +1555,7 @@ vlan_capabilities(struct ifvlan *ifv)
p->if_capenable & IFCAP_VLAN_HWTAGGING) {
ifp->if_capenable = p->if_capenable & IFCAP_HWCSUM;
ifp->if_hwassist = p->if_hwassist & (CSUM_IP | CSUM_TCP |
- CSUM_UDP | CSUM_SCTP | CSUM_FRAGMENT);
+ CSUM_UDP | CSUM_SCTP);
} else {
ifp->if_capenable = 0;
ifp->if_hwassist = 0;
@@ -1562,6 +1623,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
struct ifreq *ifr;
struct ifaddr *ifa;
struct ifvlan *ifv;
+ struct ifvlantrunk *trunk;
struct vlanreq vlr;
int error = 0;
@@ -1633,6 +1695,13 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSETVLAN:
#ifdef VIMAGE
+ /*
+ * XXXRW/XXXBZ: The goal in these checks is to allow a VLAN
+ * interface to be delegated to a jail without allowing the
+ * jail to change what underlying interface/VID it is
+ * associated with. We are not entirely convinced that this
+ * is the right way to accomplish that policy goal.
+ */
if (ifp->if_vnet != ifp->if_home_vnet) {
error = EPERM;
break;
@@ -1650,14 +1719,6 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
error = ENOENT;
break;
}
- /*
- * Don't let the caller set up a VLAN tag with
- * anything except VLID bits.
- */
- if (vlr.vlr_tag & ~EVL_VLID_MASK) {
- error = EINVAL;
- break;
- }
error = vlan_config(ifv, p, vlr.vlr_tag);
if (error)
break;
@@ -1678,7 +1739,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (TRUNK(ifv) != NULL) {
strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname,
sizeof(vlr.vlr_parent));
- vlr.vlr_tag = ifv->ifv_tag;
+ vlr.vlr_tag = ifv->ifv_vid;
}
VLAN_UNLOCK();
error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
@@ -1699,8 +1760,40 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* If we don't have a parent, just remember the membership for
* when we do.
*/
- if (TRUNK(ifv) != NULL)
+ trunk = TRUNK(ifv);
+ if (trunk != NULL) {
+ TRUNK_LOCK(trunk);
error = vlan_setmulti(ifp);
+ TRUNK_UNLOCK(trunk);
+ }
+ break;
+
+ case SIOCGVLANPCP:
+#ifdef VIMAGE
+ if (ifp->if_vnet != ifp->if_home_vnet) {
+ error = EPERM;
+ break;
+ }
+#endif
+ ifr->ifr_vlan_pcp = ifv->ifv_pcp;
+ break;
+
+ case SIOCSVLANPCP:
+#ifdef VIMAGE
+ if (ifp->if_vnet != ifp->if_home_vnet) {
+ error = EPERM;
+ break;
+ }
+#endif
+ error = priv_check(curthread, PRIV_NET_SETVLANPCP);
+ if (error)
+ break;
+ if (ifr->ifr_vlan_pcp > 7) {
+ error = EINVAL;
+ break;
+ }
+ ifv->ifv_pcp = ifr->ifr_vlan_pcp;
+ vlan_tag_recalculate(ifv);
break;
default: