Skip to content

Commit 9145cb1

Browse files
author
Stefan Baranoff
committed
Add support for filtering VNTag frames
Add a "vntag" keyword similar to the existing "vlan" and "mpls" keywords to filter on VNTag frames. VNTag was proposed as 802.1Qbh but has been replaced in the standards by 802.1Qbr although some vendors are still using VNTag. Because VNTag is always followed by VLAN and the following VLAN has the next EtherType, both the VNTag and VLAN have to be present to match and both have to be skipped in processing. So, this commit also adds an optional `vlan [vlan_id]` after the `vntag` keyword to easily filter on the VLAN ID following the VNTag. Fixes #770
1 parent 732df31 commit 9145cb1

File tree

8 files changed

+161
-1
lines changed

8 files changed

+161
-1
lines changed

ethertype.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@
115115
#ifndef ETHERTYPE_8021AD
116116
#define ETHERTYPE_8021AD 0x88a8
117117
#endif
118+
#ifndef ETHERTYPE_VNTAG
119+
#define ETHERTYPE_VNTAG 0x8926
120+
#endif
118121
#ifndef ETHERTYPE_LOOPBACK
119122
#define ETHERTYPE_LOOPBACK 0x9000
120123
#endif

gencode.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10715,3 +10715,60 @@ gen_atmmulti_abbrev(compiler_state_t *cstate, int type)
1071510715
}
1071610716
return b1;
1071710717
}
10718+
10719+
/*
10720+
* support IEEE 802.1Q VLAN trunk over ethernet
10721+
*/
10722+
struct block *
10723+
gen_vntag(compiler_state_t *cstate, bpf_u_int32 vlan_num, int has_vlan_tag)
10724+
{
10725+
struct block *b0, *b1;
10726+
10727+
/*
10728+
* Catch errors reported by us and routines below us, and return NULL
10729+
* on an error.
10730+
*/
10731+
if (setjmp(cstate->top_ctx))
10732+
return (NULL);
10733+
10734+
/*
10735+
* Check for a VNTag packet, and then change the offsets to point
10736+
* to the type and data fields within the VLAN after the VNTag packet.
10737+
* VNTag always requires VLAN afterwards, and that likely won't be
10738+
* offloaded so handle it just like inline VLAN. Apply the same rules
10739+
* and restrictions as VLAN, too.
10740+
*/
10741+
if (cstate->label_stack_depth > 0)
10742+
bpf_error(cstate, "no VNTag match after MPLS");
10743+
10744+
/* Assume any of the EtherTypes that can have VLAN can also have
10745+
* VNTag as well. With VNTag not being accepted as 802.1Qbh but
10746+
* being replaced in the standads by 802.1Qbr there's not much to go on
10747+
* other than a few vendor proprietary implementations. So, assume that
10748+
* because VNTag is tied to VLAN and they're the same in this regard.
10749+
*/
10750+
switch (cstate->linktype) {
10751+
case DLT_EN10MB:
10752+
case DLT_NETANALYZER:
10753+
case DLT_NETANALYZER_TRANSPARENT:
10754+
case DLT_IEEE802_11:
10755+
case DLT_PRISM_HEADER:
10756+
case DLT_IEEE802_11_RADIO_AVS:
10757+
case DLT_IEEE802_11_RADIO:
10758+
/* Make sure we have a VNTag frame, then jump over it. */
10759+
b0 = gen_linktype(cstate, ETHERTYPE_VNTAG);
10760+
cstate->off_linkpl.constant_part += 6;
10761+
cstate->off_linktype.constant_part += 6;
10762+
/* There should always be VLAN after VNTag. */
10763+
b1 = gen_vlan_no_bpf_extensions(cstate, vlan_num, has_vlan_tag);
10764+
gen_and(b0, b1);
10765+
break;
10766+
default:
10767+
bpf_error(cstate, "no VNTag support for %s",
10768+
pcap_datalink_val_to_description_or_dlt(cstate->linktype));
10769+
/*NOTREACHED*/
10770+
}
10771+
10772+
cstate->vlan_stack_depth++;
10773+
return (b1);
10774+
}

gencode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ struct block *gen_llc_u_subtype(compiler_state_t *, bpf_u_int32);
352352

353353
struct block *gen_vlan(compiler_state_t *, bpf_u_int32, int);
354354
struct block *gen_mpls(compiler_state_t *, bpf_u_int32, int);
355+
struct block *gen_vntag(compiler_state_t *, bpf_u_int32, int);
355356

356357
struct block *gen_pppoed(compiler_state_t *);
357358
struct block *gen_pppoes(compiler_state_t *, bpf_u_int32, int);

grammar.y.in

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ DIAG_OFF_BISON_BYACC
389389
%token LSH RSH
390390
%token LEN
391391
%token IPV6 ICMPV6 AH ESP
392-
%token VLAN MPLS
392+
%token VLAN MPLS VNTAG
393393
%token PPPOED PPPOES GENEVE VXLAN
394394
%token ISO ESIS CLNP ISIS L1 L2 IIH LSP SNP CSNP PSNP
395395
%token STP
@@ -682,6 +682,8 @@ other: pqual TK_BROADCAST { CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); }
682682
| VLAN { CHECK_PTR_VAL(($$ = gen_vlan(cstate, 0, 0))); }
683683
| MPLS pnum { CHECK_PTR_VAL(($$ = gen_mpls(cstate, $2, 1))); }
684684
| MPLS { CHECK_PTR_VAL(($$ = gen_mpls(cstate, 0, 0))); }
685+
| VNTAG VLAN pnum { CHECK_PTR_VAL(($$ = gen_vntag(cstate, $3, 1))); }
686+
| VNTAG { CHECK_PTR_VAL(($$ = gen_vntag(cstate, 0, 0))); }
685687
| PPPOED { CHECK_PTR_VAL(($$ = gen_pppoed(cstate))); }
686688
| PPPOES pnum { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, $2, 1))); }
687689
| PPPOES { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, 0, 0))); }

pcap-filter.manmisc.in

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,36 @@ filters packets with an outer label of 100000 and an inner label of
10121012
.in -.5i
10131013
filters packets to or from 192.9.200.1 with an inner label of 1024 and
10141014
any outer label.
1015+
.IP "\fBvntag \fI[vlan vlan_id]\fR"
1016+
True if the packet is a VNTag packet EtherType 0x8926 followed by an
1017+
IEEE 802.1Q VLAN packet. As with VLAN, this moves the link layer offsets forward
1018+
past the VNTag and following VLAN headers.
1019+
If the optional \fIvlan vlan_id\fR is specified, this term only matches if the
1020+
packet has the specified \fIvlan_id\fR following the VNTag header. Without the
1021+
\fIvlan_id\fR specified, there is an implicit check for any VLAN after VNTag.
1022+
Note that the \fBvntag\fR keyword changes the decoding offsets for the remainder
1023+
of the expression on the assumption that the packet is a VNTag + VLAN packet.
1024+
Each use of that keyword increments the filter offsets by 10 (6 VNTag + 4 VLAN).
1025+
.IP
1026+
For example:
1027+
.in +.5i
1028+
.nf
1029+
\fBvntag\fR and \fBip src host\fR 192.0.2.0
1030+
.fi
1031+
.in -.5i
1032+
filters on VNTag followed by any VLAN followed by an IPv4 packet from 192.0.2.0
1033+
.in +.5i
1034+
.nf
1035+
\fBvntag vlan\fR 100
1036+
.fi
1037+
.in -.5i
1038+
filters on VNTag followed by VLAN 100
1039+
.in +.5i
1040+
.nf
1041+
\fBvntag && vlan \fR300 \fB&& ip\fR
1042+
.fi
1043+
.in -.5i
1044+
filters IPv4 protocol encapsulated in VLAN 300 encapsulated within any VLAN within VNTag.
10151045
.IP \fBpppoed\fP
10161046
True if the packet is a PPP-over-Ethernet Discovery packet (Ethernet
10171047
type 0x8863).

scanner.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ pppoed return PPPOED;
339339
pppoes return PPPOES;
340340
geneve return GENEVE;
341341
vxlan return VXLAN;
342+
vntag return VNTAG;
342343

343344
lane return LANE;
344345
llc return LLC;

testprogs/TESTrun

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9657,6 +9657,37 @@ my %accept_blocks = (
96579657
(020) ret #0
96589658
',
96599659
}, # dst_portrange_degenerate
9660+
vntag_eth_vlan_nullary => {
9661+
DLT => 'EN10MB',
9662+
aliases => ['vntag'],
9663+
opt => '
9664+
(000) ldh [12]
9665+
(001) jeq #0x8926 jt 2 jf 7
9666+
(002) ldh [18]
9667+
(003) jeq #0x8100 jt 6 jf 4
9668+
(004) jeq #0x88a8 jt 6 jf 5
9669+
(005) jeq #0x9100 jt 6 jf 7
9670+
(006) ret #262144
9671+
(007) ret #0
9672+
',
9673+
}, # vntag_eth_vlan_nullary
9674+
vntag_eth_vlan_unary => {
9675+
DLT => 'EN10MB',
9676+
aliases => ['vntag vlan 4095'],
9677+
opt => '
9678+
(000) ldh [12]
9679+
(001) jeq #0x8926 jt 2 jf 10
9680+
(002) ldh [18]
9681+
(003) jeq #0x8100 jt 6 jf 4
9682+
(004) jeq #0x88a8 jt 6 jf 5
9683+
(005) jeq #0x9100 jt 6 jf 10
9684+
(006) ldh [20]
9685+
(007) and #0xfff
9686+
(008) jeq #0xfff jt 9 jf 10
9687+
(009) ret #262144
9688+
(010) ret #0
9689+
',
9690+
}, # vntag_eth_vlan_unary
96609691
);
96619692

96629693
# In apply_blocks the top-level keys are test block names. Each test block
@@ -10067,6 +10098,21 @@ my %apply_blocks = (
1006710098
expr => 'outbound',
1006810099
results => [46],
1006910100
},
10101+
vntag_simple => {
10102+
savefile => 'vntag.pcap',
10103+
expr => 'vntag',
10104+
results => [65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535],
10105+
},
10106+
vntag_with_vid => {
10107+
savefile => 'vntag.pcap',
10108+
expr => 'vntag vlan 100',
10109+
results => [65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535],
10110+
},
10111+
vntag_with_wrong_vid => {
10112+
savefile => 'vntag.pcap',
10113+
expr => 'vntag vlan 200',
10114+
results => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
10115+
},
1007010116
);
1007110117

1007210118
# * DLT, expr, netmask and skip: same as in accept_blocks above
@@ -10658,6 +10704,26 @@ my %reject_tests = (
1065810704
expr => "ip6 gateway eth-ipv4-ipv6.host123.libpcap.test",
1065910705
errstr => 'illegal modifier of \'gateway\'',
1066010706
},
10707+
vntag_not_supported => {
10708+
DLT => 'RAW',
10709+
expr => 'vntag',
10710+
errstr => 'no VNTag support',
10711+
},
10712+
vntag_vlan_too_big => {
10713+
DLT => 'EN10MB',
10714+
expr => 'vntag vlan 4096',
10715+
errstr => 'VLAN tag 4096 greater than maximum 4095',
10716+
},
10717+
vntag_vlan_no_tag => {
10718+
DLT => 'EN10MB',
10719+
expr => 'vntag vlan',
10720+
errstr => 'syntax error',
10721+
},
10722+
vntag_vlan_not_immediate => {
10723+
DLT => 'EN10MB',
10724+
expr => 'vntag vlan (eth[20:2] & 0xfff)',
10725+
errstr => 'syntax error',
10726+
},
1066110727
);
1066210728

1066310729
sub accept_test_label {

tests/filter/vntag.pcap

1.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)