Improve support for VLAN priorities in our vlan(4) driver:

- Add new ioctls, SIOCGVLANPCP and SIOCSVLANPCP to get and set VLAN
  priority properties on a vlan(4) interface.

- Add a new ifconfig command, "vlanprio X", which allows setting a VLAN
  priority to place in the 802.1Q headers of packets transmitted on a
  vlan(4) interface.

- Add a new system privilege, PRIV_NET_SETVLANPCP, to control access to
  changing a VLAN interface's filter.  Also employ the same VIMAGE-related
  controls present on SIOCSETVLAN, for consistency, even though we might
  refine those in the future.

- In numerous places in the vlan(4) implementation, better differentiate
  "tags" and "VLAN IDs" -- tags contain three fields, the VID (VLAN
  identifer), PCP (priority), and CFI (canonical format flag), not just
  the identifier.  To this end, rename lots of arguments, variables,
  functions, etc.  VIDs, rather than tags, should be used to match VLANs,
  as arguments to hash functions, etc.

- Rename VLAN_TAG to VLAN_VID, and vlan_tag_p to vlan_vid_p.  Most of
  this change is MFCable without modification, but we might want to omit
  or modify this aspect of the change for KPI and KBI reasons.

Discussed with: rrs, yongari
Sponsored by:   ADARA Networks, Inc.
MFC after:      1 month

Index: sys/ofed/include/rdma/ib_addr.h
===================================================================
--- sys/ofed/include/rdma/ib_addr.h	(revision 229389)
+++ sys/ofed/include/rdma/ib_addr.h	(working copy)
@@ -154,11 +154,11 @@
 	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/net/if.c
===================================================================
--- sys/net/if.c	(revision 229389)
+++ sys/net/if.c	(working copy)
@@ -1870,7 +1870,7 @@
 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 229389)
+++ sys/net/if.h	(working copy)
@@ -330,6 +330,7 @@
 		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 @@
 #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 229389)
+++ sys/net/if_vlan_var.h	(working copy)
@@ -81,6 +81,9 @@
 
 /*
  * 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,9 @@
 #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 */
+
 #ifdef _KERNEL
 /*
  * Drivers that are capable of adding and removing the VLAN header
@@ -133,20 +139,20 @@
 
 #define	VLAN_TRUNKDEV(_ifp)					\
 	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_trunkdev_p)((_ifp)) : NULL
-#define	VLAN_TAG(_ifp, _tag)					\
-	(_ifp)->if_type == IFT_L2VLAN ? (*vlan_tag_p)((_ifp), (_tag)) : 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)				\
 	(_ifp)->if_type == IFT_L2VLAN ?				\
 	    (*vlan_setcookie_p)((_ifp), (_cookie)) : EINVAL
-#define	VLAN_DEVAT(_ifp, _tag)					\
-	(_ifp)->if_vlantrunk != NULL ? (*vlan_devat_p)((_ifp), (_tag)) : NULL
+#define	VLAN_DEVAT(_ifp, _vid)					\
+	(_ifp)->if_vlantrunk != NULL ? (*vlan_devat_p)((_ifp), (_vid)) : NULL
 
 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 229389)
+++ 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 @@
 #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,6 +113,15 @@
 		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 use during packet generation.  Currently, the
+	 * CFI bit is always set to 0; we may want to change this at some
+	 * point.
+	 */
+	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;
@@ -178,7 +192,7 @@
 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);
 
@@ -198,7 +212,7 @@
 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 *,
+static	struct ifnet *vlan_clone_match_ethervid(struct if_clone *,
     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);
@@ -266,9 +280,9 @@
 	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);
 
 	/*
@@ -278,7 +292,7 @@
 	 */
 	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++;
@@ -296,7 +310,7 @@
 	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--;
@@ -348,7 +362,7 @@
 	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);
@@ -362,14 +376,14 @@
 }
 
 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);
 }
@@ -393,19 +407,19 @@
 #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);
@@ -415,7 +429,7 @@
 vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
 {
 
-	trunk->vlans[ifv->ifv_tag] = NULL;
+	trunk->vlans[ifv->ifv_vid] = NULL;
 	trunk->refcnt--;
 
 	return (0);
@@ -598,6 +612,16 @@
 }
 
 /*
+ * Recalculate the cached VLAN tag to apply to outgoing packets.
+ */
+static void
+vlan_tag_recalculate(struct ifvlan *ifv)
+{
+
+	ifv->ifv_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0);
+}
+
+/*
  * Return the trunk device for a virtual interface.
  */
 static struct ifnet  *
@@ -617,17 +641,18 @@
 }
 
 /*
- * Return the 16bit vlan tag for this interface.
+ * Return the 12-bit VLAN VID for this interface, for use in external
+ * components such as Infiniband.
  */
 static int
-vlan_tag(struct ifnet *ifp, uint16_t *tagp)
+vlan_vid(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);
 }
 
@@ -663,10 +688,10 @@
 }
 
 /*
- * 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;
@@ -676,7 +701,7 @@
 		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);
@@ -716,7 +741,7 @@
 		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);
@@ -741,7 +766,7 @@
 		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;
@@ -786,7 +811,7 @@
 #endif
 
 static struct ifnet *
-vlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag)
+vlan_clone_match_ethervid(struct if_clone *ifc, const char *name, int *vid)
 {
 	const char *cp;
 	struct ifnet *ifp;
@@ -814,8 +839,8 @@
 			t = (t * 10) + (*cp - '0');
 		if (*cp != '\0')
 			continue;
-		if (tag != NULL)
-			*tag = t;
+		if (vid != NULL)
+			*vid = t;
 		break;
 	}
 	IFNET_RUNLOCK_NOSLEEP();
@@ -828,7 +853,7 @@
 {
 	const char *cp;
 
-	if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL)
+	if (vlan_clone_match_ethervid(ifc, name, NULL) != NULL)
 		return (1);
 
 	if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0)
@@ -848,7 +873,7 @@
 	int wildcard;
 	int unit;
 	int error;
-	int tag;
+	int vid;
 	int ethertag;
 	struct ifvlan *ifv;
 	struct ifnet *ifp;
@@ -866,6 +891,9 @@
 	 *   must be configured separately.
 	 * 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.
 	 */
 	if (params) {
 		error = copyin(params, &vlr, sizeof(vlr));
@@ -875,7 +903,7 @@
 		if (p == NULL)
 			return ENXIO;
 		/*
-		 * Don't let the caller set up a VLAN tag with
+		 * Don't let the caller set up a VLAN ID with
 		 * anything except VLID bits.
 		 */
 		if (vlr.vlr_tag & ~EVL_VLID_MASK)
@@ -885,18 +913,18 @@
 			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(ifc, name, &vid)) != NULL) {
 		ethertag = 1;
 		unit = -1;
 		wildcard = 0;
 
 		/*
-		 * Don't let the caller set up a VLAN tag with
+		 * Don't let the caller set up a VLAN ID with
 		 * anything except VLID bits.
 		 */
-		if (tag & ~EVL_VLID_MASK)
+		if (vid & ~EVL_VLID_MASK)
 			return (EINVAL);
 	} else {
 		ethertag = 0;
@@ -958,7 +986,7 @@
 	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 partialy failed, we need to back
@@ -1105,7 +1133,7 @@
 {
 	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
 	struct ifvlan *ifv;
-	uint16_t tag;
+	uint16_t vid;
 
 	KASSERT(trunk != NULL, ("%s: no trunk", __func__));
 
@@ -1114,7 +1142,7 @@
 		 * 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);
+		vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
 		m->m_flags &= ~M_VLANTAG;
 	} else {
 		struct ether_vlan_header *evl;
@@ -1130,7 +1158,7 @@
 				return;
 			}
 			evl = mtod(m, struct ether_vlan_header *);
-			tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+			vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
 
 			/*
 			 * Remove the 802.1q header by copying the Ethernet
@@ -1155,7 +1183,7 @@
 	}
 
 	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);
@@ -1172,14 +1200,14 @@
 }
 
 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)
+	if (vid == 0 || vid == 0xFFF)
 		return (EINVAL);
 	if (p->if_type != IFT_ETHER &&
 	    (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
@@ -1211,7 +1239,10 @@
 		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;
@@ -1289,7 +1320,7 @@
 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);
@@ -1377,7 +1408,7 @@
 	 * 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 */
@@ -1615,7 +1646,7 @@
 			break;
 		}
 		/*
-		 * Don't let the caller set up a VLAN tag with
+		 * Don't let the caller set up a VLAN ID with
 		 * anything except VLID bits.
 		 */
 		if (vlr.vlr_tag & ~EVL_VLID_MASK) {
@@ -1642,7 +1673,7 @@
 		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));
@@ -1667,6 +1698,34 @@
 			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/sys/priv.h
===================================================================
--- sys/sys/priv.h	(revision 229389)
+++ 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.
Index: sbin/ifconfig/ifconfig.8
===================================================================
--- sbin/ifconfig/ifconfig.8	(revision 229518)
+++ sbin/ifconfig/ifconfig.8	(working copy)
@@ -2375,9 +2375,9 @@
 .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,17 @@
 The
 .Ar iface
 argument is useless and hence deprecated.
+.It Cm vlanprio Ar prio
+Set the VLAN priority to
+.Ar prio .
+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.
 .El
 .Pp
 The following parameters are used to configure
Index: sbin/ifconfig/ifvlan.c
===================================================================
--- sbin/ifconfig/ifvlan.c	(revision 229389)
+++ sbin/ifconfig/ifvlan.c	(working copy)
@@ -79,10 +79,14 @@
 {
 	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(" vlanprio: %u", ifr.ifr_vlan_pcp);
+	printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
+	    "<none>" : vreq.vlr_parent);
+	printf("\n");
 }
 
 static void
@@ -93,7 +97,7 @@
 		 * 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 +124,7 @@
 }
 
 static
-DECL_CMD_FUNC(setvlantag, val, d)
+DECL_CMD_FUNC(setvlanvid, val, d)
 {
 	struct vlanreq vreq;
 	u_long ul;
@@ -150,6 +154,22 @@
 }
 
 static
+DECL_CMD_FUNC(setvlanprio, val, d)
+{
+	u_long ul;
+	char *endp;
+
+	ul = strtoul(val, &endp, 0);
+	if (*endp != '\0')
+		errx(1, "invalid value for vlanprio");
+	if (ul > 7)
+		errx(1, "value for vlanprio 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 +188,12 @@
 }
 
 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("vlanprio",				setvlanprio),
 	/* XXX For compatibility.  Should become DEF_CMD() some day. */
 	DEF_CMD_OPTARG("-vlandev",			unsetvlandev),
 	DEF_CMD("vlanmtu",	IFCAP_VLAN_MTU,		setifcap),
