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.
|