This patch adds a number of 802.1q-related features to the FreeBSD network
stack.  It will likely be committed as a series of commits rather than as
a single commit as presented in this patch.
                
1. Modify ifconfig(8) and vlan(4) to allow a default vlan interface priority
   to be configured using "ifconfig vlanX vlanpcp Y".  By default,
   interfaces use a priority of 0 (best effort), retaining their current
   behaviour.  A new privilege, PRIV_NET_SETVLANPCP, is added to control
   reconfiguration of VLAN priorities.

2. Modify vlan(4) to optionally preserve received vlan pcp fields from
   802.1q-encapsulated packets in m_tags.  This behaviour is controlled by
   net.link.vlan.mtag_pcp, which is disabled by default.  When enabled,
   there is additional per-packet memory allocation, which we may wish to
   revisit in the future.
 
3. Modify vlan(4) to optionally set the pcp field on a transmitted
   802.1q-encapsulated packet based on an m_tag.  Note that this is not the
   same m_tag as the received m_tag, so when a received mbuf is reused
   (due to ICMP reflection, IP forwarding, etc), the newly transmitted
   packet will not automatically inherit the received priority.  This
   behaviour is also controlled by net.link.vlan.mtag_pcp, and disabled by
   default.

4. Modify pf(4) and pfctl(8) to allow 802.1q priorities, encapsulated in
   m_tags, to be matched against and set in pf rules.  A typical rule might
   look like this:

     pass out on vlan0 proto tcp from any to any port 22 ieee8021q-setpcp ee

   Which sets the 802.1q priority on SSH packets sent via vlan0 to ee --
   excellent effort, rather than using a default set on the interface via
   ifconfig(8).  As supported by pfctl(8), it is currently possible only to
   exactly match priorities; however, pf(4) implements range comparisons,
   and it would make sense to extend pfctl(8) to parse and program such 
   comparisons, as is already done for ports.

This change is intended to be API, ABI, KPI, and KBI compliant for an as-is
MFC -- with one exception: one public symbol is renamed in the VLAN code,
and a compatibility symbol must be added before a merge to a -STABLE branch.

Sponsored by:   ADARA Networks, Inc.
Reviewed by:    bz, rrs
MFC after:      1 month

Index: sbin/ifconfig/ifconfig.8
===================================================================
--- sbin/ifconfig/ifconfig.8	(revision 230008)
+++ sbin/ifconfig/ifconfig.8	(working copy)
@@ -2375,9 +2375,9 @@ The following parameters are specific to
 .Xr vlan 4
 interfaces:
 .Bl -tag -width indent
-.It Cm vlan Ar vlan_tag
-Set the VLAN tag value to
-.Ar vlan_tag .
+.It Cm vlan Ar vid
+Set the VLAN VID value to
+.Ar vid .
 This value is a 12-bit VLAN Identifier (VID) which is used to create an 802.1Q
 VLAN header for packets sent from the
 .Xr vlan 4
@@ -2444,6 +2444,21 @@ down.
 The
 .Ar iface
 argument is useless and hence deprecated.
+.It Cm vlanpcp Ar pcp
+Set the VLAN priority to
+.Ar pcp .
+This value is a 3-bit number used to set the PCP field in an 802.1Q VLAN
+header for packets sent from the
+.Xr vlan 4
+interface.
+Interfaces default to a priority of 0 when created, selecting best-effort
+delivery.
+Values between 1 and 7 may also be used, with 1 selecting lowest
+priority, and 7 highest.
+Subsystems such as
+.Xr pf 4
+may select alternative 802.1q priorities, which will override the interface
+priority.
 .El
 .Pp
 The following parameters are used to configure
@@ -2646,6 +2661,7 @@ tried to alter an interface's configuration.
 .Xr carp 4 ,
 .Xr gif 4 ,
 .Xr netintro 4 ,
+.Xr pf 4 ,
 .Xr pfsync 4 ,
 .Xr polling 4 ,
 .Xr vlan 4 ,
Index: sbin/ifconfig/ifvlan.c
===================================================================
--- sbin/ifconfig/ifvlan.c	(revision 230008)
+++ sbin/ifconfig/ifvlan.c	(working copy)
@@ -1,7 +1,11 @@
 /*
- * Copyright (c) 1999
- *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu>
+ * Copyright (c) 2012 ADARA Networks, Inc.
+ * All rights reserved.
  *
+ * Portions of this software were developed by Robert N. M. Watson under
+ * contract to ADARA Networks, Inc.
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -79,10 +83,14 @@ vlan_status(int s)
 {
 	struct vlanreq		vreq;
 
-	if (getvlan(s, &ifr, &vreq) != -1)
-		printf("\tvlan: %d parent interface: %s\n",
-		    vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ?
-		    "<none>" : vreq.vlr_parent);
+	if (getvlan(s, &ifr, &vreq) == -1)
+		return;
+	printf("\tvlan: %d", vreq.vlr_tag);
+	if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1)
+		printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
+	printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
+	    "<none>" : vreq.vlr_parent);
+	printf("\n");
 }
 
 static void
@@ -93,7 +101,7 @@ vlan_create(int s, struct ifreq *ifr)
 		 * One or both parameters were specified, make sure both.
 		 */
 		if (params.vlr_tag == NOTAG)
-			errx(1, "must specify a tag for vlan create");
+			errx(1, "must specify a VID for vlan create");
 		if (params.vlr_parent[0] == '\0')
 			errx(1, "must specify a parent device for vlan create");
 		ifr->ifr_data = (caddr_t) &params;
@@ -120,7 +128,7 @@ vlan_set(int s, struct ifreq *ifr)
 }
 
 static
-DECL_CMD_FUNC(setvlantag, val, d)
+DECL_CMD_FUNC(setvlanvid, val, d)
 {
 	struct vlanreq vreq;
 	u_long ul;
@@ -150,6 +158,22 @@ DECL_CMD_FUNC(setvlandev, val, d)
 }
 
 static
+DECL_CMD_FUNC(setvlanpcp, val, d)
+{
+	u_long ul;
+	char *endp;
+
+	ul = strtoul(val, &endp, 0);
+	if (*endp != '\0')
+		errx(1, "invalid value for vlanpcp");
+	if (ul > 7)
+		errx(1, "value for vlanpcp out of range");
+	ifr.ifr_vlan_pcp = ul;
+	if (ioctl(s, SIOCSVLANPCP, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSVLANPCP");
+}
+
+static
 DECL_CMD_FUNC(unsetvlandev, val, d)
 {
 	struct vlanreq		vreq;
@@ -168,11 +192,12 @@ DECL_CMD_FUNC(unsetvlandev, val, d)
 }
 
 static struct cmd vlan_cmds[] = {
-	DEF_CLONE_CMD_ARG("vlan",			setvlantag),
+	DEF_CLONE_CMD_ARG("vlan",			setvlanvid),
 	DEF_CLONE_CMD_ARG("vlandev",			setvlandev),
 	/* NB: non-clone cmds */
-	DEF_CMD_ARG("vlan",				setvlantag),
+	DEF_CMD_ARG("vlan",				setvlanvid),
 	DEF_CMD_ARG("vlandev",				setvlandev),
+	DEF_CMD_ARG("vlanpcp",				setvlanpcp),
 	/* XXX For compatibility.  Should become DEF_CMD() some day. */
 	DEF_CMD_OPTARG("-vlandev",			unsetvlandev),
 	DEF_CMD("vlanmtu",	IFCAP_VLAN_MTU,		setifcap),
Index: sys/ofed/include/rdma/ib_addr.h
===================================================================
--- sys/ofed/include/rdma/ib_addr.h	(revision 230032)
+++ sys/ofed/include/rdma/ib_addr.h	(working copy)
@@ -154,11 +154,11 @@ static inline u16 rdma_vlan_dev_vlan_id(const stru
 	return dev->priv_flags & IFF_802_1Q_VLAN ?
 		vlan_dev_vlan_id(dev) : 0xffff;
 #else
-	uint16_t tag;
+	uint16_t vid;
 
-	if (VLAN_TAG(__DECONST(struct ifnet *, dev), &tag) != 0)
+	if (VLAN_VID(__DECONST(struct ifnet *, dev), &vid) != 0)
 		return 0xffff;
-	return tag;
+	return vid;
 #endif
 }
 
Index: sys/dev/cxgbe/t4_l2t.c
===================================================================
--- sys/dev/cxgbe/t4_l2t.c	(revision 230032)
+++ sys/dev/cxgbe/t4_l2t.c	(working copy)
@@ -679,7 +679,7 @@ t4_l2t_get(struct port_info *pi, struct ifnet *ifp
 		e->lle = NULL;
 		atomic_store_rel_int(&e->refcnt, 1);
 		if (ifp->if_type == IFT_L2VLAN)
-			VLAN_TAG(ifp, &e->vlan);
+			VLAN_VID(ifp, &e->vlan);
 		else
 			e->vlan = VLAN_NONE;
 		e->next = d->l2tab[hash].first;
Index: sys/net/if.c
===================================================================
--- sys/net/if.c	(revision 230032)
+++ sys/net/if.c	(working copy)
@@ -1870,7 +1870,7 @@ void	(*vlan_link_state_p)(struct ifnet *);	/* XXX:
 void	(*vlan_trunk_cap_p)(struct ifnet *);		/* XXX: private from if_vlan */
 struct ifnet *(*vlan_trunkdev_p)(struct ifnet *);
 struct	ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t);
-int	(*vlan_tag_p)(struct ifnet *, uint16_t *);
+int	(*vlan_vid_p)(struct ifnet *, uint16_t *);
 int	(*vlan_setcookie_p)(struct ifnet *, void *);
 void	*(*vlan_cookie_p)(struct ifnet *);
 
Index: sys/net/if.h
===================================================================
--- sys/net/if.h	(revision 230032)
+++ sys/net/if.h	(working copy)
@@ -330,6 +330,7 @@ struct	ifreq {
 		caddr_t	ifru_data;
 		int	ifru_cap[2];
 		u_int	ifru_fib;
+		u_char	ifru_vlan_pcp;
 	} ifr_ifru;
 #define	ifr_addr	ifr_ifru.ifru_addr	/* address */
 #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-to-p link */
@@ -347,6 +348,7 @@ struct	ifreq {
 #define	ifr_curcap	ifr_ifru.ifru_cap[1]	/* current capabilities */
 #define	ifr_index	ifr_ifru.ifru_index	/* interface index */
 #define	ifr_fib		ifr_ifru.ifru_fib	/* interface fib */
+#define	ifr_vlan_pcp	ifr_ifru.ifru_vlan_pcp	/* VLAN priority */
 };
 
 #define	_SIZEOF_ADDR_IFREQ(ifr) \
Index: sys/net/if_vlan_var.h
===================================================================
--- sys/net/if_vlan_var.h	(revision 230032)
+++ sys/net/if_vlan_var.h	(working copy)
@@ -81,6 +81,9 @@ struct	ether_vlan_header {
 
 /*
  * Configuration structure for SIOCSETVLAN and SIOCGETVLAN ioctls.
+ *
+ * XXXRW: Note that despite being named vlr_tag, the field below should really
+ * be named vlr_vid.
  */
 struct	vlanreq {
 	char	vlr_parent[IFNAMSIZ];
@@ -89,6 +92,23 @@ struct	vlanreq {
 #define	SIOCSETVLAN	SIOCSIFGENERIC
 #define	SIOCGETVLAN	SIOCGIFGENERIC
 
+#define	SIOCGVLANPCP	_IOWR('i', 152, struct ifreq)	/* Get VLAN PCP */
+#define	SIOCSVLANPCP	 _IOW('i', 153, struct ifreq)	/* Set VLAN PCP */
+
+/*
+ * Names for 802.1q priorities ("802.1p").  Notice that in this scheme,
+ * (0 < 1), allowing default 0-tagged traffic to take priority over background
+ * tagged traffic.
+ */
+#define	IEEE8021Q_PCP_BK	1	/* Background (lowest) */
+#define	IEEE8021Q_PCP_BE	0	/* Best effort (default) */
+#define	IEEE8021Q_PCP_EE	2	/* Excellent effort */
+#define	IEEE8021Q_PCP_CA	3	/* Critical applications */
+#define	IEEE8021Q_PCP_VI	4	/* Video, < 100ms latency */
+#define	IEEE8021Q_PCP_VO	5	/* Video, < 10ms latency */
+#define	IEEE8021Q_PCP_IC	6	/* Internetwork control */
+#define	IEEE8021Q_PCP_NC	7	/* Network control (highest) */
+
 #ifdef _KERNEL
 /*
  * Drivers that are capable of adding and removing the VLAN header
@@ -126,6 +146,16 @@ struct	vlanreq {
  * if_capabilities.
  */
 
+/*
+ * The 802.1q code may also tag mbufs with the PCP (priority) field for use in
+ * other layers of the stack, in which case an m_tag will be used.  This is
+ * semantically quite different from use of the ether_vtag field, which is
+ * defined only between the device driver and VLAN layer.
+ */
+#define	MTAG_8021Q		1326104895
+#define	MTAG_8021Q_PCP_IN	0		/* Input priority. */
+#define	MTAG_8021Q_PCP_OUT	1		/* Output priority. */
+
 #define	VLAN_CAPABILITIES(_ifp) do {				\
 	if ((_ifp)->if_vlantrunk != NULL) 			\
 		(*vlan_trunk_cap_p)(_ifp);			\
@@ -133,8 +163,8 @@ struct	vlanreq {
 
 #define	VLAN_TRUNKDEV(_ifp)					\
 	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_trunkdev_p)((_ifp)) : NULL
-#define	VLAN_TAG(_ifp, _vid)					\
-	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_tag_p)((_ifp), (_vid)) : EINVAL
+#define	VLAN_VID(_ifp, _vid)					\
+	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_vid_p)((_ifp), (_vid)) : EINVAL
 #define	VLAN_COOKIE(_ifp)					\
 	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_cookie_p)((_ifp)) : NULL
 #define	VLAN_SETCOOKIE(_ifp, _cookie)				\
@@ -146,7 +176,7 @@ struct	vlanreq {
 extern	void (*vlan_trunk_cap_p)(struct ifnet *);
 extern	struct ifnet *(*vlan_trunkdev_p)(struct ifnet *);
 extern	struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t);
-extern	int (*vlan_tag_p)(struct ifnet *, uint16_t *);
+extern	int (*vlan_vid_p)(struct ifnet *, uint16_t *);
 extern	int (*vlan_setcookie_p)(struct ifnet *, void *);
 extern	void *(*vlan_cookie_p)(struct ifnet *);
 
Index: sys/net/if_vlan.c
===================================================================
--- sys/net/if_vlan.c	(revision 230032)
+++ sys/net/if_vlan.c	(working copy)
@@ -1,6 +1,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
  * granted, provided that both the above copyright notice and this
@@ -50,6 +54,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/mbuf.h>
 #include <sys/module.h>
 #include <sys/rwlock.h>
+#include <sys/priv.h>
 #include <sys/queue.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
@@ -108,13 +113,19 @@ struct	ifvlan {
 		uint16_t ifvm_proto;	/* encapsulation ethertype */
 		uint16_t ifvm_tag;	/* tag to apply on packets leaving if */
 	}	ifv_mib;
+
+	/*
+	 * These fields are authoritative configuration state; ifvm_tag caches
+	 * a precomputed tag to report via the MIB.
+	 */
+	uint16_t	ifv_vid;	/* VLAN Identifier (VID). */
+	uint8_t		ifv_pcp;	/* Priority Code Point (PCP). */
 	SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
 #ifndef VLAN_ARRAY
 	LIST_ENTRY(ifvlan) ifv_list;
 #endif
 };
 #define	ifv_proto	ifv_mib.ifvm_proto
-#define	ifv_vid		ifv_mib.ifvm_tag
 #define	ifv_encaplen	ifv_mib.ifvm_encaplen
 #define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
 #define	ifv_mintu	ifv_mib.ifvm_mintu
@@ -139,6 +150,15 @@ static int soft_pad = 0;
 SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &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 eventhandler_tag ifdetach_tag;
@@ -598,6 +618,16 @@ restart:
 }
 
 /*
+ * Recalculate the cached VLAN tag exposed via the MIB.
+ */
+static void
+vlan_tag_recalculate(struct ifvlan *ifv)
+{
+
+	ifv->ifv_mib.ifvm_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0);
+}
+
+/*
  * Return the trunk device for a virtual interface.
  */
 static struct ifnet  *
@@ -619,12 +649,9 @@ vlan_trunkdev(struct ifnet *ifp)
 /*
  * 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 *vidp)
+vlan_vid(struct ifnet *ifp, uint16_t *vidp)
 {
 	struct ifvlan *ifv;
 
@@ -720,7 +747,7 @@ vlan_modevent(module_t mod, int type, void *data)
 		vlan_trunkdev_p = vlan_trunkdev;
 		vlan_cookie_p = vlan_cookie;
 		vlan_setcookie_p = vlan_setcookie;
-		vlan_tag_p = vlan_tag;
+		vlan_vid_p = vlan_vid;
 		vlan_devat_p = vlan_devat;
 #ifndef VIMAGE
 		if_clone_attach(&vlan_cloner);
@@ -745,7 +772,7 @@ vlan_modevent(module_t mod, int type, void *data)
 		vlan_link_state_p = NULL;
 		vlan_trunk_cap_p = NULL;
 		vlan_trunkdev_p = NULL;
-		vlan_tag_p = NULL;
+		vlan_vid_p = NULL;
 		vlan_cookie_p = vlan_cookie;
 		vlan_setcookie_p = vlan_setcookie;
 		vlan_devat_p = NULL;
@@ -871,8 +898,7 @@ vlan_clone_create(struct if_clone *ifc, char *name
 	 * The first technique is preferred; the latter two are
 	 * supported for backwards compatibilty.
 	 *
-	 * XXXRW: Note historic use of the word "tag" here.  New ioctls may be
-	 * called for.
+	 * XXXRW: Note historic use of the word "tag" here.
 	 */
 	if (params) {
 		error = copyin(params, &vlr, sizeof(vlr));
@@ -1019,7 +1045,9 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ifvlan *ifv;
 	struct ifnet *p;
+	struct m_tag *mtag;
 	int error, len, mcast;
+	uint16_t tag;
 
 	ifv = ifp->if_softc;
 	p = PARENT(ifv);
@@ -1073,12 +1101,21 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
 	 * tell the interface where the packet came from so that it
 	 * knows how to find the VLAN tag to use, so we attach a
 	 * packet tag that holds it.
+	 *
+	 * Only look for (and use) priority information if that is enabled;
+	 * avoid the cost of an mtag locate if not.
 	 */
+	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 = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0);
+
 	if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
-		m->m_pkthdr.ether_vtag = ifv->ifv_vid;
+		m->m_pkthdr.ether_vtag = tag;
 		m->m_flags |= M_VLANTAG;
 	} else {
-		m = ether_vlanencap(m, ifv->ifv_vid);
+		m = ether_vlanencap(m, tag);
 		if (m == NULL) {
 			if_printf(ifp, "unable to prepend VLAN header\n");
 			ifp->if_oerrors++;
@@ -1112,7 +1149,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
 	struct ifvlan *ifv;
-	uint16_t vid;
+	struct m_tag *mtag;
+	uint16_t tag, vid;
 
 	KASSERT(trunk != NULL, ("%s: no trunk", __func__));
 
@@ -1121,7 +1159,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.
 		 */
-		vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
+		tag = m->m_pkthdr.ether_vtag;
 		m->m_flags &= ~M_VLANTAG;
 	} else {
 		struct ether_vlan_header *evl;
@@ -1137,7 +1175,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
 				return;
 			}
 			evl = mtod(m, struct ether_vlan_header *);
-			vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+			tag = ntohs(evl->evl_tag);
 
 			/*
 			 * Remove the 802.1q header by copying the Ethernet
@@ -1161,6 +1199,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
 		}
 	}
 
+	vid = EVL_VLANOFTAG(tag);
+
 	TRUNK_RLOCK(trunk);
 	ifv = vlan_gethash(trunk, vid);
 	if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) {
@@ -1171,6 +1211,28 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
 	}
 	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);
+				ifp->if_ierrors++;
+				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++;
 
@@ -1219,6 +1281,9 @@ exists:
 	}
 
 	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;
@@ -1681,6 +1746,34 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
 			error = vlan_setmulti(ifp);
 		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:
 		error = EINVAL;
 		break;
Index: sys/contrib/pf/net/pf.c
===================================================================
--- sys/contrib/pf/net/pf.c	(revision 230032)
+++ sys/contrib/pf/net/pf.c	(working copy)
@@ -95,6 +95,8 @@ __FBSDID("$FreeBSD$");
 
 #include <net/if.h>
 #include <net/if_types.h>
+#include <net/ethernet.h>
+#include <net/if_vlan_var.h>
 #include <net/bpf.h>
 #include <net/route.h>
 #ifdef __FreeBSD__
@@ -2713,6 +2715,36 @@ pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_
 }
 
 int
+pf_match_ieee8021q_pcp(u_int8_t op, u_int8_t pcp1, u_int8_t pcp2,
+    struct mbuf *m)
+{
+	struct m_tag *mtag;
+	uint8_t mpcp;
+
+	/*
+	 * Packets without 802.1q headers are treated as having a PCP of 0
+	 * (best effort).
+	 */
+	mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL);
+	if (mtag != NULL)
+		mpcp = *(uint8_t *)(mtag + 1);
+	else
+		mpcp = IEEE8021Q_PCP_BE;
+
+	/*
+	 * 802.1q uses a non-traditional ordering, in which 1 < 0, allowing
+	 * default 0-tagged ("best effort") traffic to take precedence over
+	 * 1-tagged ("background") traffic.  Renumber both PCP arguments
+	 * before making a comparison so that we can use boring arithmetic
+	 * operators.
+	 */
+	pcp1 = ((pcp1 == 0) ? 1 : ((pcp1 == 1) ? 0 : pcp1));
+	pcp2 = ((pcp2 == 0) ? 1 : ((pcp2 == 1) ? 0 : pcp2));
+	mpcp = ((mpcp == 0) ? 1 : ((mpcp == 1) ? 0 : mpcp));
+	return (pf_match(op, pcp1, pcp2, mpcp));
+}
+
+int
 pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g)
 {
 	if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE)
@@ -2768,6 +2800,26 @@ pf_tag_packet(struct mbuf *m, int tag, int rtablei
 	return (0);
 }
 
+int
+pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r)
+{
+	struct m_tag *mtag;
+
+	KASSERT(r->ieee8021q_pcp.setpcp & SETPCP_VALID,
+	    ("%s with invalid setpcp", __func__));
+
+	mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL);
+	if (mtag == NULL) {
+		mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_OUT,
+		    sizeof(uint8_t), M_NOWAIT);
+		if (mtag == NULL)
+			return (ENOMEM);
+		m_tag_prepend(m, mtag);
+	}
+	*(uint8_t *)(mtag + 1) = (r->ieee8021q_pcp.setpcp & SETPCP_PCP_MASK);
+	return (0);
+}
+
 void
 pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n,
     struct pf_rule **r, struct pf_rule **a, int *match)
@@ -3578,6 +3630,10 @@ pf_test_rule(struct pf_rule **rm, struct pf_state
 		    !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
 		    pd->lookup.gid))
 			r = TAILQ_NEXT(r, entries);
+		else if (r->ieee8021q_pcp.op &&
+		    !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op,
+		    r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m))
+			r = TAILQ_NEXT(r, entries);
 		else if (r->prob &&
 #ifdef __FreeBSD__
 		    r->prob <= arc4random())
@@ -6900,6 +6956,15 @@ done:
 		pf_tag_packet(m, s ? s->tag : 0, r->rtableid);
 #endif
 
+	if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) &&
+	    pf_ieee8021q_setpcp(m, r)) {
+		action = PF_DROP;
+		REASON_SET(&reason, PFRES_MEMORY);
+		log = 1;
+		DPFPRINTF(PF_DEBUG_MISC,
+		    ("pf: failed to allocate 802.1q mtag\n"));
+	}
+
 	if (dir == PF_IN && s && s->key[PF_SK_STACK])
 #ifdef __FreeBSD__
 		pd.pf_mtag->statekey = s->key[PF_SK_STACK];
Index: sys/contrib/pf/net/pfvar.h
===================================================================
--- sys/contrib/pf/net/pfvar.h	(revision 230032)
+++ sys/contrib/pf/net/pfvar.h	(working copy)
@@ -435,6 +435,14 @@ struct pf_rule_gid {
 	u_int8_t	 op;
 };
 
+struct pf_rule_ieee8021q_pcp {
+	u_int8_t	 pcp[2];
+	u_int8_t	 op;
+#define	SETPCP_VALID	0x80	/* Set if PCP value in field is valid. */
+#define	SETPCP_PCP_MASK	0x07	/* Mask to retrieve pcp if SETPCP_VALID. */
+	u_int8_t	 setpcp;
+};
+
 struct pf_rule_addr {
 	struct pf_addr_wrap	 addr;
 	u_int16_t		 port[2];
@@ -629,7 +637,7 @@ struct pf_rule {
 	u_int32_t		 src_nodes;
 	u_int32_t		 max_src_nodes;
 	u_int32_t		 max_src_states;
-	u_int32_t		 spare1;			/* netgraph */
+	struct pf_rule_ieee8021q_pcp	ieee8021q_pcp;		/* spare1 */
 	u_int32_t		 max_src_conn;
 	struct {
 		u_int32_t		limit;
@@ -1960,6 +1968,8 @@ int	pf_match(u_int8_t, u_int32_t, u_int32_t, u_int
 int	pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t);
 int	pf_match_uid(u_int8_t, uid_t, uid_t, uid_t);
 int	pf_match_gid(u_int8_t, gid_t, gid_t, gid_t);
+int	pf_match_ieee8021q_pcp(u_int8_t, u_int8_t, u_int8_t, struct mbuf *);
+int	pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r);
 
 void	pf_normalize_init(void);
 int	pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *,
Index: sys/sys/priv.h
===================================================================
--- sys/sys/priv.h	(revision 230032)
+++ sys/sys/priv.h	(working copy)
@@ -338,6 +338,7 @@
 #define	PRIV_NET_SETIFVNET	417	/* Move interface to vnet. */
 #define	PRIV_NET_SETIFDESCR	418	/* Set interface description. */
 #define	PRIV_NET_SETIFFIB	419	/* Set interface fib. */
+#define	PRIV_NET_SETVLANPCP	420	/* Set VLAN priority. */
 
 /*
  * 802.11-related privileges.
