Projet

Général

Profil

Télécharger (22,7 ko) Statistiques
| Branche: | Révision:

univnautes-tools / patches / stable / 10 / pf_802.1p.diff @ 4ab3b90b

1
diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c
2
index cefcbbc..6761ab2 100644
3
--- a/sbin/ifconfig/ifvlan.c
4
+++ b/sbin/ifconfig/ifvlan.c
5
@@ -1,6 +1,10 @@
6
 /*
7
- * Copyright (c) 1999
8
- *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
9
+ * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu>
10
+ * Copyright (c) 2012 ADARA Networks, Inc.
11
+ * All rights reserved.
12
+  *
13
+ * Portions of this software were developed by Robert N. M. Watson under
14
+ * contract to ADARA Networks, Inc.
15
  *
16
  * Redistribution and use in source and binary forms, with or without
17
  * modification, are permitted provided that the following conditions
18
@@ -79,10 +83,14 @@ vlan_status(int s)
19
 {
20
 	struct vlanreq		vreq;
21
 
22
-	if (getvlan(s, &ifr, &vreq) != -1)
23
-		printf("\tvlan: %d parent interface: %s\n",
24
-		    vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ?
25
-		    "<none>" : vreq.vlr_parent);
26
+	if (getvlan(s, &ifr, &vreq) == -1)
27
+		return;
28
+	printf("\tvlan: %d", vreq.vlr_tag);
29
+	if (ioctl(s, SIOCGVLANPCP, (caddr_t)&ifr) != -1)
30
+		printf(" vlanpcp: %u", ifr.ifr_vlan_pcp);
31
+	printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ?
32
+	    "<none>" : vreq.vlr_parent);
33
+	printf("\n");
34
 }
35
 
36
 static void
37
@@ -150,6 +158,22 @@ DECL_CMD_FUNC(setvlandev, val, d)
38
 }
39
 
40
 static
41
+DECL_CMD_FUNC(setvlanpcp, val, d)
42
+{
43
+	u_long ul;
44
+	char *endp;
45
+
46
+	ul = strtoul(val, &endp, 0);
47
+	if (*endp != '\0')
48
+		errx(1, "invalid value for vlanpcp");
49
+	if (ul > 7)
50
+		errx(1, "value for vlanpcp out of range");
51
+	ifr.ifr_vlan_pcp = ul;
52
+	if (ioctl(s, SIOCSVLANPCP, (caddr_t)&ifr) == -1)
53
+		err(1, "SIOCSVLANPCP");
54
+}
55
+
56
+static
57
 DECL_CMD_FUNC(unsetvlandev, val, d)
58
 {
59
 	struct vlanreq		vreq;
60
@@ -170,6 +194,7 @@ DECL_CMD_FUNC(unsetvlandev, val, d)
61
 static struct cmd vlan_cmds[] = {
62
 	DEF_CLONE_CMD_ARG("vlan",			setvlantag),
63
 	DEF_CLONE_CMD_ARG("vlandev",			setvlandev),
64
+	DEF_CMD_ARG("vlanpcp",				setvlanpcp),
65
 	/* NB: non-clone cmds */
66
 	DEF_CMD_ARG("vlan",				setvlantag),
67
 	DEF_CMD_ARG("vlandev",				setvlandev),
68
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
69
index bc9a8f7..da1468b 100644
70
--- a/sbin/pfctl/parse.y
71
+++ b/sbin/pfctl/parse.y
72
@@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
73
 #include <sys/sysctl.h>
74
 #endif
75
 #include <net/if.h>
76
+#include <net/ethernet.h>
77
+#include <net/if_vlan_var.h>
78
 #include <netinet/in.h>
79
 #include <netinet/in_systm.h>
80
 #include <netinet/ip.h>
81
@@ -241,6 +243,11 @@ struct filter_opts {
82
 	char			*tag;
83
 	char			*match_tag;
84
 	u_int8_t		 match_tag_not;
85
+	struct {
86
+		uint8_t          pcp[2];
87
+		uint8_t          op;
88
+		uint8_t          setpcp;
89
+	} ieee8021q_pcp;
90
 	u_int32_t                dnpipe;
91
 	u_int32_t                pdnpipe;
92
 	u_int32_t                free_flags;
93
@@ -463,6 +470,7 @@ int	parseport(char *, struct range *r, int);
94
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
95
 %token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
96
 %token	TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
97
+%token	IEEE8021QPCP IEEE8021QSETPCP
98
 %token	DIVERTTO DIVERTREPLY
99
 %token	<v.string>		STRING
100
 %token	<v.number>		NUMBER
101
@@ -886,6 +894,11 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
102
 				YYERROR;
103
 			}
104
 
105
+			r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0];
106
+			r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1];
107
+			r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op;
108
+			r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp;
109
+
110
 			if ($9.match_tag)
111
 				if (strlcpy(r.match_tagname, $9.match_tag,
112
 				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
113
@@ -1962,6 +1975,11 @@ pfrule		: action dir logquick interface route af proto fromto
114
 			r.prob = $9.prob;
115
 			r.rtableid = $9.rtableid;
116
 
117
+			r.ieee8021q_pcp.pcp[0] = $9.ieee8021q_pcp.pcp[0];
118
+			r.ieee8021q_pcp.pcp[1] = $9.ieee8021q_pcp.pcp[1];
119
+			r.ieee8021q_pcp.op = $9.ieee8021q_pcp.op;
120
+			r.ieee8021q_pcp.setpcp = $9.ieee8021q_pcp.setpcp;
121
+
122
 			r.af = $6;
123
 			if ($9.tag)
124
 				if (strlcpy(r.tagname, $9.tag,
125
@@ -2498,6 +2516,98 @@ filter_opt	: USER uids {
126
 			if (filter_opts.prob == 0)
127
 				filter_opts.prob = 1;
128
 		}
129
+		| IEEE8021QPCP STRING {
130
+			u_int pcp;
131
+
132
+			/*
133
+			* XXXRW: More complete set of operations, similar to
134
+			* ports.
135
+			*/
136
+			if (!strcmp($2, "be"))
137
+				pcp = IEEE8021Q_PCP_BE;
138
+			else if (!strcmp($2, "bk"))
139
+				pcp = IEEE8021Q_PCP_BK;
140
+			else if (!strcmp($2, "ee"))
141
+				pcp = IEEE8021Q_PCP_EE;
142
+			else if (!strcmp($2, "ca"))
143
+				pcp = IEEE8021Q_PCP_CA;
144
+			else if (!strcmp($2, "vi"))
145
+				pcp = IEEE8021Q_PCP_VI;
146
+			else if (!strcmp($2, "vo"))
147
+				pcp = IEEE8021Q_PCP_VO;
148
+			else if (!strcmp($2, "ic"))
149
+				pcp = IEEE8021Q_PCP_IC;
150
+			else if (!strcmp($2, "nc"))
151
+				pcp = IEEE8021Q_PCP_NC;
152
+			else
153
+				pcp = 8;               /* flag bad argument */
154
+			if (pcp > 7) {
155
+				yyerror("invalid ieee8021q_pcp value %s", $2);
156
+				free($2);
157
+				YYERROR;
158
+			}
159
+			free($2);
160
+			filter_opts.ieee8021q_pcp.pcp[0] = pcp;
161
+			filter_opts.ieee8021q_pcp.pcp[1] = 0;
162
+			filter_opts.ieee8021q_pcp.op = PF_OP_EQ;
163
+		}
164
+		| IEEE8021QPCP number {
165
+			u_int pcp;
166
+
167
+			pcp = $2;
168
+			if (pcp > 7) {
169
+				yyerror("invalid ieee8021q_pcp value %u", pcp);
170
+				YYERROR;
171
+			}
172
+			filter_opts.ieee8021q_pcp.pcp[0] = pcp;
173
+			filter_opts.ieee8021q_pcp.pcp[1] = 0;
174
+			filter_opts.ieee8021q_pcp.op = PF_OP_EQ;
175
+		}
176
+		| IEEE8021QSETPCP STRING {
177
+			u_int pcp;
178
+
179
+			/*
180
+			* XXXRW: More complete set of operations, similar to
181
+			* ports.
182
+			*/
183
+			if (!strcmp($2, "be"))
184
+				pcp = IEEE8021Q_PCP_BE;
185
+			else if (!strcmp($2, "bk"))
186
+				pcp = IEEE8021Q_PCP_BK;
187
+			else if (!strcmp($2, "ee"))
188
+				pcp = IEEE8021Q_PCP_EE;
189
+			else if (!strcmp($2, "ca"))
190
+				pcp = IEEE8021Q_PCP_CA;
191
+			else if (!strcmp($2, "vi"))
192
+				pcp = IEEE8021Q_PCP_VI;
193
+			else if (!strcmp($2, "vo"))
194
+				pcp = IEEE8021Q_PCP_VO;
195
+			else if (!strcmp($2, "ic"))
196
+				pcp = IEEE8021Q_PCP_IC;
197
+			else if (!strcmp($2, "nc"))
198
+				pcp = IEEE8021Q_PCP_NC;
199
+			else
200
+				pcp = 8;               /* flag bad argument */
201
+			if (pcp > 7) {
202
+				yyerror("invalid ieee8021q_setpcp value %s",
203
+					$2);
204
+				free($2);
205
+				YYERROR;
206
+			}
207
+			free($2);
208
+			filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID;
209
+		}
210
+		| IEEE8021QSETPCP number {
211
+			u_int pcp;
212
+
213
+			pcp = $2;
214
+			if (pcp > 7) {
215
+				yyerror("invalid ieee8021q_setpcp value %u",
216
+					pcp);
217
+				YYERROR;
218
+			}
219
+			filter_opts.ieee8021q_pcp.setpcp = pcp | SETPCP_VALID;
220
+		}
221
 		| RTABLE NUMBER				{
222
 			if ($2 < 0 || $2 > rt_tableid_max()) {
223
 				yyerror("invalid rtable id");
224
@@ -5474,6 +5584,8 @@ lookup(char *s)
225
 		{ "hostid",		HOSTID},
226
 		{ "icmp-type",		ICMPTYPE},
227
 		{ "icmp6-type",		ICMP6TYPE},
228
+		{ "ieee8021q-pcp",	IEEE8021QPCP},
229
+		{ "ieee8021q-setpcp",	IEEE8021QSETPCP},
230
 		{ "if-bound",		IFBOUND},
231
 		{ "in",			IN},
232
 		{ "include",		INCLUDE},
233
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
234
index afc2b82..2612b11 100644
235
--- a/sbin/pfctl/pfctl_parser.c
236
+++ b/sbin/pfctl/pfctl_parser.c
237
@@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$");
238
 #include <sys/param.h>
239
 #include <sys/proc.h>
240
 #include <net/if.h>
241
+#include <net/ethernet.h>
242
+#include <net/if_vlan_var.h>
243
 #include <netinet/in.h>
244
 #include <netinet/in_systm.h>
245
 #include <netinet/ip.h>
246
@@ -65,6 +67,8 @@ __FBSDID("$FreeBSD$");
247
 void		 print_op (u_int8_t, const char *, const char *);
248
 void		 print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int);
249
 void		 print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned);
250
+void		 print_ieee8021q_pcp (u_int8_t, uint8_t, uint8_t);
251
+void		 print_ieee8021q_setpcp (u_int8_t);
252
 void		 print_flags (u_int8_t);
253
 void		 print_fromto(struct pf_rule_addr *, pf_osfp_t,
254
 		    struct pf_rule_addr *, u_int8_t, u_int8_t, int, int);
255
@@ -353,6 +357,47 @@ print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax)
256
 		print_op(op, a1, a2);
257
 }
258
 
259
+static const char *
260
+ieee8021q_pcp_name(u_int8_t pcp)
261
+{
262
+	const char *s;
263
+
264
+	if (pcp == IEEE8021Q_PCP_BE)
265
+		s = "be";
266
+	else if (pcp == IEEE8021Q_PCP_BK)
267
+		s = "bk";
268
+	else if (pcp == IEEE8021Q_PCP_EE)
269
+		s = "ee";
270
+	else if (pcp == IEEE8021Q_PCP_CA)
271
+		s = "ca";
272
+	else if (pcp == IEEE8021Q_PCP_VI)
273
+		s = "vi";
274
+	else if (pcp == IEEE8021Q_PCP_VO)
275
+		s = "vo";
276
+	else if (pcp == IEEE8021Q_PCP_IC)
277
+		s = "ic";
278
+	else if (pcp == IEEE8021Q_PCP_NC)
279
+		s = "nc";
280
+	else
281
+		s = "??";
282
+	return (s);
283
+}
284
+
285
+ void
286
+print_ieee8021q_pcp(u_int8_t op, u_int8_t pcp0, u_int8_t pcp1)
287
+{
288
+
289
+	printf(" ieee8021q-pcp");
290
+	print_op(op, ieee8021q_pcp_name(pcp0), ieee8021q_pcp_name(pcp1));
291
+}
292
+
293
+void
294
+print_ieee8021q_setpcp(u_int8_t pcp)
295
+{
296
+
297
+	printf(" ieee8021q-setpcp %s", ieee8021q_pcp_name(pcp));
298
+}
299
+
300
 void
301
 print_flags(u_int8_t f)
302
 {
303
@@ -1024,6 +1069,13 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric)
304
 	}
305
 	if (r->rtableid != -1)
306
 		printf(" rtable %u", r->rtableid);
307
+	if (r->ieee8021q_pcp.op != 0)
308
+		print_ieee8021q_pcp(r->ieee8021q_pcp.op,
309
+			r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1]);
310
+	if (r->ieee8021q_pcp.setpcp & SETPCP_VALID)
311
+		print_ieee8021q_setpcp(r->ieee8021q_pcp.setpcp &
312
+			SETPCP_PCP_MASK);
313
+
314
 	if (r->divert.port) {
315
 #ifdef __FreeBSD__
316
 		printf(" divert-to %u", ntohs(r->divert.port));
317
diff --git a/sys/net/if.h b/sys/net/if.h
318
index 2a4aad6..243e9e6 100644
319
--- a/sys/net/if.h
320
+++ b/sys/net/if.h
321
@@ -386,6 +386,7 @@ struct	ifreq {
322
 		caddr_t	ifru_data;
323
 		int	ifru_cap[2];
324
 		u_int	ifru_fib;
325
+		u_char	ifru_vlan_pcp;
326
 	} ifr_ifru;
327
 #define	ifr_addr	ifr_ifru.ifru_addr	/* address */
328
 #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-to-p link */
329
@@ -403,6 +404,7 @@ struct	ifreq {
330
 #define	ifr_curcap	ifr_ifru.ifru_cap[1]	/* current capabilities */
331
 #define	ifr_index	ifr_ifru.ifru_index	/* interface index */
332
 #define	ifr_fib		ifr_ifru.ifru_fib	/* interface fib */
333
+#define	ifr_vlan_pcp	ifr_ifru.ifru_vlan_pcp	/* VLAN priority */
334
 };
335
 
336
 #define	_SIZEOF_ADDR_IFREQ(ifr) \
337
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
338
index b7f1197..916ca8c 100644
339
--- a/sys/net/if_vlan.c
340
+++ b/sys/net/if_vlan.c
341
@@ -1,5 +1,9 @@
342
 /*-
343
  * Copyright 1998 Massachusetts Institute of Technology
344
+ * Copyright 2012 ADARA Networks, Inc.
345
+ *
346
+ * Portions of this software were developed by Robert N. M. Watson under
347
+ * contract to ADARA Networks, Inc.
348
  *
349
  * Permission to use, copy, modify, and distribute this software and
350
  * its documentation for any purpose and without fee is hereby
351
@@ -51,6 +55,7 @@ __FBSDID("$FreeBSD$");
352
 #include <sys/mbuf.h>
353
 #include <sys/module.h>
354
 #include <sys/rwlock.h>
355
+#include <sys/priv.h>
356
 #include <sys/queue.h>
357
 #include <sys/socket.h>
358
 #include <sys/sockio.h>
359
@@ -112,6 +117,7 @@ struct	ifvlan {
360
 		int	ifvm_mintu;	/* min transmission unit */
361
 		uint16_t ifvm_proto;	/* encapsulation ethertype */
362
 		uint16_t ifvm_tag;	/* tag to apply on packets leaving if */
363
+		uint8_t	ifvm_pcp;	/* Priority Code Point (PCP). */
364
 	}	ifv_mib;
365
 	SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
366
 #ifndef VLAN_ARRAY
367
@@ -120,6 +126,7 @@ struct	ifvlan {
368
 };
369
 #define	ifv_proto	ifv_mib.ifvm_proto
370
 #define	ifv_vid		ifv_mib.ifvm_tag
371
+#define	ifv_pcp		ifv_mib.ifvm_pcp
372
 #define	ifv_encaplen	ifv_mib.ifvm_encaplen
373
 #define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
374
 #define	ifv_mintu	ifv_mib.ifvm_mintu
375
@@ -144,6 +151,15 @@ static int soft_pad = 0;
376
 SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0,
377
 	   "pad short frames before tagging");
378
 
379
+/*
380
+ * For now, make preserving PCP via an mbuf tag optional, as it increases
381
+ * per-packet memory allocations and frees.  In the future, it would be
382
+ * preferable to reuse ether_vtag for this, or similar.
383
+ */
384
+static int vlan_mtag_pcp = 0;
385
+SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW, &vlan_mtag_pcp, 0,
386
+	"Retain VLAN PCP information as packets are passed up the stack");
387
+
388
 static const char vlanname[] = "vlan";
389
 static MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface");
390
 
391
@@ -693,6 +709,16 @@ vlan_devat(struct ifnet *ifp, uint16_t vid)
392
 }
393
 
394
 /*
395
+ * Recalculate the cached VLAN tag exposed via the MIB.
396
+ */
397
+static void
398
+vlan_tag_recalculate(struct ifvlan *ifv)
399
+{
400
+
401
+	ifv->ifv_mib.ifvm_tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0);
402
+}
403
+
404
+/*
405
  * VLAN support can be loaded as a module.  The only place in the
406
  * system that's intimately aware of this is ether_input.  We hook
407
  * into this code through vlan_input_p which is defined there and
408
@@ -1026,6 +1052,8 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
409
 {
410
 	struct ifvlan *ifv;
411
 	struct ifnet *p;
412
+	struct m_tag *mtag;
413
+	uint16_t tag;
414
 	int error, len, mcast;
415
 
416
 	ifv = ifp->if_softc;
417
@@ -1081,11 +1109,16 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m)
418
 	 * knows how to find the VLAN tag to use, so we attach a
419
 	 * packet tag that holds it.
420
 	 */
421
+	if (vlan_mtag_pcp && (mtag = m_tag_locate(m, MTAG_8021Q,
422
+	    MTAG_8021Q_PCP_OUT, NULL)) != NULL)
423
+		tag = EVL_MAKETAG(ifv->ifv_vid, *(uint8_t *)(mtag + 1), 0);
424
+	else
425
+		tag = EVL_MAKETAG(ifv->ifv_vid, ifv->ifv_pcp, 0);
426
 	if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
427
-		m->m_pkthdr.ether_vtag = ifv->ifv_vid;
428
+		m->m_pkthdr.ether_vtag = tag;
429
 		m->m_flags |= M_VLANTAG;
430
 	} else {
431
-		m = ether_vlanencap(m, ifv->ifv_vid);
432
+		m = ether_vlanencap(m, tag);
433
 		if (m == NULL) {
434
 			if_printf(ifp, "unable to prepend VLAN header\n");
435
 			ifp->if_oerrors++;
436
@@ -1119,7 +1152,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
437
 {
438
 	struct ifvlantrunk *trunk = ifp->if_vlantrunk;
439
 	struct ifvlan *ifv;
440
-	uint16_t vid;
441
+	struct m_tag *mtag;
442
+	uint16_t vid, tag;
443
 
444
 	KASSERT(trunk != NULL, ("%s: no trunk", __func__));
445
 
446
@@ -1128,7 +1162,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
447
 		 * Packet is tagged, but m contains a normal
448
 		 * Ethernet frame; the tag is stored out-of-band.
449
 		 */
450
-		vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
451
+		tag = m->m_pkthdr.ether_vtag;
452
 		m->m_flags &= ~M_VLANTAG;
453
 	} else {
454
 		struct ether_vlan_header *evl;
455
@@ -1144,7 +1178,7 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
456
 				return;
457
 			}
458
 			evl = mtod(m, struct ether_vlan_header *);
459
-			vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
460
+			tag = ntohs(evl->evl_tag);
461
 
462
 			/*
463
 			 * Remove the 802.1q header by copying the Ethernet
464
@@ -1168,6 +1202,8 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
465
 		}
466
 	}
467
 
468
+	vid = EVL_VLANOFTAG(tag);
469
+
470
 	TRUNK_RLOCK(trunk);
471
 	ifv = vlan_gethash(trunk, vid);
472
 	if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) {
473
@@ -1178,6 +1214,28 @@ vlan_input(struct ifnet *ifp, struct mbuf *m)
474
 	}
475
 	TRUNK_RUNLOCK(trunk);
476
 
477
+	if (vlan_mtag_pcp) {
478
+		/*
479
+		 * While uncommon, it is possible that we will find a 802.1q
480
+		 * packet encapsulated inside another packet that also had an
481
+		 * 802.1q header.  For example, ethernet tunneled over IPSEC
482
+		 * arriving over ethernet.  In that case, we replace the
483
+		 * existing 802.1q PCP m_tag value.
484
+		 */
485
+		mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL);
486
+		if (mtag == NULL) {
487
+			mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_IN,
488
+			    sizeof(uint8_t), M_NOWAIT);
489
+			if (mtag == NULL) {
490
+				m_freem(m);
491
+				ifp->if_ierrors++;
492
+				return;
493
+			}
494
+			m_tag_prepend(m, mtag);
495
+		}
496
+		*(uint8_t *)(mtag + 1) = EVL_PRIOFTAG(tag);
497
+	}
498
+
499
 	m->m_pkthdr.rcvif = ifv->ifv_ifp;
500
 	ifv->ifv_ifp->if_ipackets++;
501
 
502
@@ -1226,6 +1284,8 @@ exists:
503
 	}
504
 
505
 	ifv->ifv_vid = vid;	/* must set this before vlan_inshash() */
506
+	ifv->ifv_pcp = 0;       /* Default: best effort delivery. */
507
+	vlan_tag_recalculate(ifv);
508
 	error = vlan_inshash(trunk, ifv);
509
 	if (error)
510
 		goto done;
511
@@ -1712,6 +1772,34 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
512
 			error = vlan_setmulti(ifp);
513
 		break;
514
 
515
+	case SIOCGVLANPCP:
516
+#ifdef VIMAGE
517
+		if (ifp->if_vnet != ifp->if_home_vnet) {
518
+			error = EPERM;
519
+			break;
520
+		}
521
+#endif
522
+		ifr->ifr_vlan_pcp = ifv->ifv_pcp;
523
+		break;
524
+
525
+	case SIOCSVLANPCP:
526
+#ifdef VIMAGE
527
+		if (ifp->if_vnet != ifp->if_home_vnet) {
528
+			error = EPERM;
529
+			break;
530
+		}
531
+#endif
532
+		error = priv_check(curthread, PRIV_NET_SETVLANPCP);
533
+		if (error)
534
+			break;
535
+		if (ifr->ifr_vlan_pcp > 7) {
536
+			error = EINVAL;
537
+			break;
538
+		}
539
+		ifv->ifv_pcp = ifr->ifr_vlan_pcp;
540
+		vlan_tag_recalculate(ifv);
541
+		break;
542
+
543
 	default:
544
 		error = EINVAL;
545
 		break;
546
diff --git a/sys/net/if_vlan_var.h b/sys/net/if_vlan_var.h
547
index 4eb3b09..b1950e1 100644
548
--- a/sys/net/if_vlan_var.h
549
+++ b/sys/net/if_vlan_var.h
550
@@ -89,6 +89,23 @@ struct	vlanreq {
551
 #define	SIOCSETVLAN	SIOCSIFGENERIC
552
 #define	SIOCGETVLAN	SIOCGIFGENERIC
553
 
554
+#define	SIOCGVLANPCP	_IOWR('i', 152, struct ifreq)	/* Get VLAN PCP */
555
+#define	SIOCSVLANPCP	 _IOW('i', 153, struct ifreq)	/* Set VLAN PCP */
556
+
557
+/*
558
+ * Names for 802.1q priorities ("802.1p").  Notice that in this scheme,
559
+ * (0 < 1), allowing default 0-tagged traffic to take priority over background
560
+ * tagged traffic.
561
+ */
562
+#define	IEEE8021Q_PCP_BK	1	/* Background (lowest) */
563
+#define	IEEE8021Q_PCP_BE	0	/* Best effort (default) */
564
+#define	IEEE8021Q_PCP_EE	2	/* Excellent effort */
565
+#define	IEEE8021Q_PCP_CA	3	/* Critical applications */
566
+#define	IEEE8021Q_PCP_VI	4	/* Video, < 100ms latency */
567
+#define	IEEE8021Q_PCP_VO	5	/* Video, < 10ms latency */
568
+#define	IEEE8021Q_PCP_IC	6	/* Internetwork control */
569
+#define	IEEE8021Q_PCP_NC	7	/* Network control (highest) */
570
+
571
 #ifdef _KERNEL
572
 /*
573
  * Drivers that are capable of adding and removing the VLAN header
574
@@ -126,6 +143,16 @@ struct	vlanreq {
575
  * if_capabilities.
576
  */
577
 
578
+/*
579
+ * The 802.1q code may also tag mbufs with the PCP (priority) field for use in
580
+ * other layers of the stack, in which case an m_tag will be used.  This is
581
+ * semantically quite different from use of the ether_vtag field, which is
582
+ * defined only between the device driver and VLAN layer.
583
+ */
584
+#define	MTAG_8021Q		1326104895
585
+#define	MTAG_8021Q_PCP_IN	0		/* Input priority. */
586
+#define	MTAG_8021Q_PCP_OUT	1		/* Output priority. */
587
+
588
 #define	VLAN_CAPABILITIES(_ifp) do {				\
589
 	if ((_ifp)->if_vlantrunk != NULL) 			\
590
 		(*vlan_trunk_cap_p)(_ifp);			\
591
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
592
index 6061763..8ecb0d5 100644
593
--- a/sys/net/pfvar.h
594
+++ b/sys/net/pfvar.h
595
@@ -326,6 +326,14 @@ struct pf_rule_gid {
596
 	u_int8_t	 op;
597
 };
598
 
599
+struct pf_rule_ieee8021q_pcp {
600
+	u_int8_t	 pcp[2];
601
+	u_int8_t	 op;
602
+#define	SETPCP_VALID	0x80	/* Set if PCP value in field is valid. */
603
+#define	SETPCP_PCP_MASK	0x07	/* Mask to retrieve pcp if SETPCP_VALID. */
604
+	u_int8_t	 setpcp;
605
+};
606
+
607
 struct pf_rule_addr {
608
 	struct pf_addr_wrap	 addr;
609
 	u_int16_t		 port[2];
610
@@ -616,6 +624,8 @@ struct pf_rule {
611
 		u_int16_t		port;
612
 	}			divert;
613
 
614
+	struct pf_rule_ieee8021q_pcp    ieee8021q_pcp;
615
+
616
 	uint64_t		 u_states_cur;
617
 	uint64_t		 u_states_tot;
618
 	uint64_t		 u_src_nodes;
619
@@ -1674,6 +1684,8 @@ int	pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *,
620
 int	pf_match_addr_range(struct pf_addr *, struct pf_addr *,
621
 	    struct pf_addr *, sa_family_t);
622
 int	pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t);
623
+int	pf_match_ieee8021q_pcp(u_int8_t, u_int8_t, u_int8_t, struct mbuf *);
624
+int	pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r);
625
 
626
 void	pf_normalize_init(void);
627
 void	pf_normalize_cleanup(void);
628
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
629
index 5d62b39..580f380 100644
630
--- a/sys/netpfil/pf/pf.c
631
+++ b/sys/netpfil/pf/pf.c
632
@@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
633
 
634
 #include <net/if.h>
635
 #include <net/if_types.h>
636
+#include <net/ethernet.h>
637
+#include <net/if_vlan_var.h>
638
 #include <net/route.h>
639
 #include <net/radix_mpath.h>
640
 #include <net/vnet.h>
641
@@ -2513,6 +2515,26 @@ pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af,
642
 	pf_send(pfse);
643
 }
644
 
645
+int
646
+pf_ieee8021q_setpcp(struct mbuf *m, struct pf_rule *r)
647
+{
648
+	struct m_tag *mtag;
649
+
650
+	KASSERT(r->ieee8021q_pcp.setpcp & SETPCP_VALID,
651
+	    ("%s with invalid setpcp", __func__));
652
+
653
+	mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL);
654
+	if (mtag == NULL) {
655
+		mtag = m_tag_alloc(MTAG_8021Q, MTAG_8021Q_PCP_OUT,
656
+		    sizeof(uint8_t), M_NOWAIT);
657
+		if (mtag == NULL)
658
+			return (ENOMEM);
659
+		m_tag_prepend(m, mtag);
660
+	}
661
+	*(uint8_t *)(mtag + 1) = (r->ieee8021q_pcp.setpcp & SETPCP_PCP_MASK);
662
+	return (0);
663
+}
664
+
665
 static void
666
 pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af,
667
     struct pf_rule *r)
668
@@ -2686,6 +2708,36 @@ pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p)
669
 	return (pf_match(op, a1, a2, p));
670
 }
671
 
672
+int
673
+pf_match_ieee8021q_pcp(u_int8_t op, u_int8_t pcp1, u_int8_t pcp2,
674
+    struct mbuf *m)
675
+{
676
+	struct m_tag *mtag;
677
+	uint8_t mpcp;
678
+
679
+	/*
680
+	* Packets without 802.1q headers are treated as having a PCP of 0
681
+	* (best effort).
682
+	*/
683
+	mtag = m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_IN, NULL);
684
+	if (mtag != NULL)
685
+		mpcp = *(uint8_t *)(mtag + 1);
686
+	else
687
+		mpcp = IEEE8021Q_PCP_BE;
688
+
689
+	/*
690
+	* 802.1q uses a non-traditional ordering, in which 1 < 0, allowing
691
+	* default 0-tagged ("best effort") traffic to take precedence over
692
+	* 1-tagged ("background") traffic.  Renumber both PCP arguments
693
+	* before making a comparison so that we can use boring arithmetic
694
+	* operators.
695
+	*/
696
+	pcp1 = ((pcp1 == 0) ? 1 : ((pcp1 == 1) ? 0 : pcp1));
697
+	pcp2 = ((pcp2 == 0) ? 1 : ((pcp2 == 1) ? 0 : pcp2));
698
+	mpcp = ((mpcp == 0) ? 1 : ((mpcp == 1) ? 0 : mpcp));
699
+	return (pf_match(op, pcp1, pcp2, mpcp));
700
+}
701
+
702
 static int
703
 pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u)
704
 {
705
@@ -3444,6 +3496,10 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
706
 		    !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1],
707
 		    pd->lookup.gid))
708
 			r = TAILQ_NEXT(r, entries);
709
+		else if (r->ieee8021q_pcp.op &&
710
+		    !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op,
711
+		    r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m))
712
+			r = TAILQ_NEXT(r, entries);
713
 		else if (r->prob &&
714
 		    r->prob <= arc4random())
715
 			r = TAILQ_NEXT(r, entries);
716
@@ -3902,6 +3958,10 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif,
717
 		    pd->proto == IPPROTO_ICMPV6) &&
718
 		    (r->type || r->code))
719
 			r = TAILQ_NEXT(r, entries);
720
+                else if (r->ieee8021q_pcp.op &&
721
+                    !pf_match_ieee8021q_pcp(r->ieee8021q_pcp.op,
722
+                    r->ieee8021q_pcp.pcp[0], r->ieee8021q_pcp.pcp[1], m))
723
+                        r = TAILQ_NEXT(r, entries);
724
 		else if (r->prob && r->prob <=
725
 		    (arc4random() % (UINT_MAX - 1) + 1))
726
 			r = TAILQ_NEXT(r, entries);
727
@@ -6708,6 +6768,24 @@ done:
728
 	if (r->rtableid >= 0)
729
 		M_SETFIB(m, r->rtableid);
730
 
731
+	if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) &&
732
+	    pf_ieee8021q_setpcp(m, r)) {
733
+		action = PF_DROP;
734
+		REASON_SET(&reason, PFRES_MEMORY);
735
+		log = 1;
736
+		DPFPRINTF(PF_DEBUG_MISC,
737
+		    ("pf: failed to allocate 802.1q mtag\n"));
738
+	}
739
+
740
+	if ((r->ieee8021q_pcp.setpcp & SETPCP_VALID) &&
741
+	    pf_ieee8021q_setpcp(m, r)) {
742
+		action = PF_DROP;
743
+		REASON_SET(&reason, PFRES_MEMORY);
744
+		log = 1;
745
+		DPFPRINTF(PF_DEBUG_MISC,
746
+		    ("pf: failed to allocate 802.1q mtag\n"));
747
+	}
748
+
749
 #ifdef ALTQ
750
 	if (s && s->qid) {
751
                 pd.act.pqid = s->pqid;
752
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
753
index 18d17af..65f151d 100644
754
--- a/sys/sys/priv.h
755
+++ b/sys/sys/priv.h
756
@@ -339,6 +339,7 @@
757
 #define	PRIV_NET_SETIFVNET	417	/* Move interface to vnet. */
758
 #define	PRIV_NET_SETIFDESCR	418	/* Set interface description. */
759
 #define	PRIV_NET_SETIFFIB	419	/* Set interface fib. */
760
+#define	PRIV_NET_SETVLANPCP     420     /* Set VLAN priority. */
761
 
762
 /*
763
  * 802.11-related privileges.
(43-43/67)