From 29a7f4ef0d5af3f961b472e35c9a3ad65933cf41 Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Sat, 1 Mar 2025 14:48:57 +0000 Subject: [PATCH] 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. Skips the 6 bytes of the VNTag header similar to the 4 byte skip of VLAN. They both inject themselves between Ethernet dest addr and EtherType. --- CHANGES | 1 + ethertype.h | 3 ++ gencode.c | 73 ++++++++++++++++++++++++++ gencode.h | 1 + grammar.y.in | 3 +- pcap-filter.manmisc.in | 24 ++++++++- scanner.l | 1 + testprogs/TESTrun | 48 +++++++++++++++++ tests/filter/vntag-novntag-mixed.pcap | Bin 0 -> 3312 bytes 9 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 tests/filter/vntag-novntag-mixed.pcap diff --git a/CHANGES b/CHANGES index 2518354f49..25bc566b22 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,7 @@ DayOfTheWeek, Month DD, YYYY / The Tcpdump Group Require a live capture for all Linux BPF extensions. Have "outbound" mean Tx only for DLT_SLIP. Filter Linux SocketCAN frames in userland if necessary. + Add support for filtering VNTag frames (pull request #1480) rpcap: Support user names and passwords in rpcap:// and rpcaps:// URLs. Add a -t flag to rpcapd to specify the data channel port; from diff --git a/ethertype.h b/ethertype.h index e34e07b98d..82158b8145 100644 --- a/ethertype.h +++ b/ethertype.h @@ -115,6 +115,9 @@ #ifndef ETHERTYPE_8021AD #define ETHERTYPE_8021AD 0x88a8 #endif +#ifndef ETHERTYPE_VNTAG +#define ETHERTYPE_VNTAG 0x8926 +#endif #ifndef ETHERTYPE_LOOPBACK #define ETHERTYPE_LOOPBACK 0x9000 #endif diff --git a/gencode.c b/gencode.c index a9e431649e..af1a937dc6 100644 --- a/gencode.c +++ b/gencode.c @@ -10710,3 +10710,76 @@ gen_atmmulti_abbrev(compiler_state_t *cstate, int type) } return b1; } + +/* + * support VNTag (proposed as IEEE 802.1Qbh but never accepted) over Ethernet + * VNTag headers look like this on their own: + * 0 1 2 3 4 5 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EtherType |D|P| vif_list_id/dvif_id |L|R|ver| svif_id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * In context that looks like this: + * 0 1 2 3 4 5 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | VNTag | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EtherType | Payload... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct block * +gen_vntag(compiler_state_t *cstate) +{ + struct block *b0; + + /* + * Catch errors reported by us and routines below us, and return NULL + * on an error. + */ + if (setjmp(cstate->top_ctx)) + return (NULL); + + /* + * Check for a VNTag packet, and then change the offsets to point + * to the type and data fields within the VLAN after the VNTag packet. + * VNTag always requires VLAN afterwards, and that likely won't be + * offloaded so handle it just like inline VLAN. Apply the same rules + * and restrictions as VLAN, too. + */ + if (cstate->label_stack_depth > 0) + bpf_error(cstate, "no VNTag match after MPLS"); + + /* Assume any of the EtherTypes that can have VLAN can also have + * VNTag as well. With VNTag not being accepted as 802.1Qbh but + * being replaced in the standads by 802.1Qbr there's not much to go on + * other than a few vendor proprietary implementations. + */ + switch (cstate->linktype) { + case DLT_EN10MB: + case DLT_NETANALYZER: + case DLT_NETANALYZER_TRANSPARENT: + case DLT_IEEE802_11: + case DLT_PRISM_HEADER: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_IEEE802_11_RADIO: + /* Make sure we have a VNTag frame, then jump over it. */ + b0 = gen_linktype(cstate, ETHERTYPE_VNTAG); + cstate->off_linkpl.constant_part += 6; + cstate->off_linktype.constant_part += 6; + break; + default: + bpf_error(cstate, "no VNTag support for %s", + pcap_datalink_val_to_description_or_dlt(cstate->linktype)); + /*NOTREACHED*/ + } + + return (b0); +} diff --git a/gencode.h b/gencode.h index 22752ed1aa..608daa24d3 100644 --- a/gencode.h +++ b/gencode.h @@ -352,6 +352,7 @@ struct block *gen_llc_u_subtype(compiler_state_t *, bpf_u_int32); struct block *gen_vlan(compiler_state_t *, bpf_u_int32, int); struct block *gen_mpls(compiler_state_t *, bpf_u_int32, int); +struct block *gen_vntag(compiler_state_t *); struct block *gen_pppoed(compiler_state_t *); struct block *gen_pppoes(compiler_state_t *, bpf_u_int32, int); diff --git a/grammar.y.in b/grammar.y.in index 4df433960e..0c0e3b5ecd 100644 --- a/grammar.y.in +++ b/grammar.y.in @@ -389,7 +389,7 @@ DIAG_OFF_BISON_BYACC %token LSH RSH %token LEN %token IPV6 ICMPV6 AH ESP -%token VLAN MPLS +%token VLAN MPLS VNTAG %token PPPOED PPPOES GENEVE VXLAN %token ISO ESIS CLNP ISIS L1 L2 IIH LSP SNP CSNP PSNP %token STP @@ -682,6 +682,7 @@ other: pqual TK_BROADCAST { CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); } | VLAN { CHECK_PTR_VAL(($$ = gen_vlan(cstate, 0, 0))); } | MPLS pnum { CHECK_PTR_VAL(($$ = gen_mpls(cstate, $2, 1))); } | MPLS { CHECK_PTR_VAL(($$ = gen_mpls(cstate, 0, 0))); } + | VNTAG { CHECK_PTR_VAL(($$ = gen_vntag(cstate))); } | PPPOED { CHECK_PTR_VAL(($$ = gen_pppoed(cstate))); } | PPPOES pnum { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, $2, 1))); } | PPPOES { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, 0, 0))); } diff --git a/pcap-filter.manmisc.in b/pcap-filter.manmisc.in index 7864cf03ab..eaa21c7b69 100644 --- a/pcap-filter.manmisc.in +++ b/pcap-filter.manmisc.in @@ -1011,6 +1011,24 @@ filters packets with an outer label of 100000 and an inner label of .in -.5i filters packets to or from 192.9.200.1 with an inner label of 1024 and any outer label. +.IP "\fBvntag\fR" +True if the packet is a VNTag packet (identified by EtherType 0x8926). +Each use of that keyword increments the filter link header and payload offsets by 6. +.IP +For example: +.in +.5i +.nf +\fBvntag\fR +.fi +.in -.5i +filters on VNTag as the next EtherType and skips the VNTag header +.in +.5i +.nf +\fBvntag\fR and \fBvlan\fR and \fBip src host\fR 192.0.2.0 +.fi +.in -.5i +filters on VNTag followed by any VLAN followed by an IPv4 packet from 192.0.2.0 +skipping over the VNTag header, then VLAN header, to allow finding the IPv4 .IP \fBpppoed\fP True if the packet is a PPP-over-Ethernet Discovery packet (Ethernet type 0x8863). @@ -1636,8 +1654,10 @@ The keyword became available in libpcap 1.10.0. .PP The -.B vxlan -keyword became available in libpcap 1.11.0. +.BR vxlan +and +.B vntag +keywords became available in libpcap 1.11.0. .SH SEE ALSO .BR pcap (3PCAP) .SH BUGS diff --git a/scanner.l b/scanner.l index 0fa8f40724..07a842b158 100644 --- a/scanner.l +++ b/scanner.l @@ -339,6 +339,7 @@ pppoed return PPPOED; pppoes return PPPOES; geneve return GENEVE; vxlan return VXLAN; +vntag return VNTAG; lane return LANE; llc return LLC; diff --git a/testprogs/TESTrun b/testprogs/TESTrun index 33d9f42915..47e1a9fa52 100755 --- a/testprogs/TESTrun +++ b/testprogs/TESTrun @@ -9657,6 +9657,34 @@ my %accept_blocks = ( (020) ret #0 ', }, # dst_portrange_degenerate + vntag_eth => { + DLT => 'EN10MB', + aliases => ['vntag'], + opt => ' + (000) ldh [12] + (001) jeq #0x8926 jt 2 jf 3 + (002) ret #262144 + (003) ret #0 + ', + }, # vntag_eth + vntag_eth_vlan_vntag_vlan => { + DLT => 'EN10MB', + aliases => ['vlan and vntag and vlan'], + opt => ' + (000) ldh [12] + (001) jeq #0x8100 jt 4 jf 2 + (002) jeq #0x88a8 jt 4 jf 3 + (003) jeq #0x9100 jt 4 jf 11 + (004) ldh [16] + (005) jeq #0x8926 jt 6 jf 11 + (006) ldh [22] + (007) jeq #0x8100 jt 10 jf 8 + (008) jeq #0x88a8 jt 10 jf 9 + (009) jeq #0x9100 jt 10 jf 11 + (010) ret #262144 + (011) ret #0 + ', + }, # vntag_eth ); # In apply_blocks the top-level keys are test block names. Each test block @@ -10067,6 +10095,21 @@ my %apply_blocks = ( expr => 'outbound', results => [46], }, + vntag => { + savefile => 'vntag-novntag-mixed.pcap', + expr => 'vntag', + results => [262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, + vntag_vlan_ip => { + savefile => 'vntag-novntag-mixed.pcap', + expr => 'vntag and vlan 100 and ip', + results => [262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 262144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, + vntag_vlan_dest_ip => { + savefile => 'vntag-novntag-mixed.pcap', + expr => 'vntag and vlan 100 and ip dst 192.168.20.102', + results => [0, 262144, 0, 0, 262144, 262144, 0, 0, 262144, 262144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, ); # * DLT, expr, netmask and skip: same as in accept_blocks above @@ -10699,6 +10742,11 @@ my %reject_tests = ( expr => 'gateway $af', errstr => 'aid supported only on ARCnet', }, + vntag_not_supported => { + DLT => 'RAW', + expr => 'vntag', + errstr => 'no VNTag support', + }, ); sub accept_test_label { diff --git a/tests/filter/vntag-novntag-mixed.pcap b/tests/filter/vntag-novntag-mixed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..763b38acf92c9e84d7506b035b03b61e843c1b31 GIT binary patch literal 3312 zcmds)Uu;uV9LG<`){UDq83;mjgX2OB1N;AOgDZ)Ju|FuoX6t-l$h^HhE!W-M+uR-s zjRG@^nW!OC112Me`k(<+V$duz7~_Lu7>O~#2VdlAOL!s1CHkbn-|u#9FLzxtUwrT+ zCq1`4J)iUYet+k8TEXKHCJ7;Zdv-qNg4YL2%|%`!-Kxf@VltVt1dZqDC^J*4o|345 zp9Ce!S%zo)Nmb*_^s8yFZg{ggms7s9BBu+pLXKJX&ud8o!8IAo1~kEa;2w@Fy%JJO zYDha7)JJsVgznc3PUWnuYH}Plrs~u%RoIZMQP={bNy8kK`F2f>Bfgc8wXm8S*20Es z;+W$Dv;%%OX`URPj=>mv&9?c@#H%Fw!o=DKXP?9wuMv`HAYFv)iS3J!hrPt8nbb@9==~&_hoHOgYd}W+qfKYtyxo#EGP5GyJcp9Rv z&6x=K+wDpt54j|IYbq~pcp12x^6PgutUqKiqrj;{ZvW8SB(jT44L1P|#JLH$BV&zZ;phX1%Votlh&*Iqd_P0ShOBGA`U1w-tFSJ{_FJq-Mb>7r zFr0jt)zcND{$otm_%YfWi$(oGU(h2AW|-;Sn_@bbsB+xY{2ji~ZePG7^cop>3-i;L z(u}yQ`Ba^8A2(H9^V9L^qwtJr_-9M$XqpazoBr(y%`QQ1GFv9JIM4DKPJ&o z*drX2Gu+!}B-EtJ5=*3DJ3VA%kt70D3X75$E|GMm;L;KuVJz*HH8smTLRUP|A&MMpz!6qbJohuS% z?~&-8=^GvBw)2+QI`HOBd>?O#LanjqA+r9UWtQEq0&8X*Z-?Y?S-IJJO*en<0p2dW z9mYO@9oE*BZig4Yo=p~*#kdlPhlZ@}PbjVXV2piPTw<`$^V1uk6Jr@^?~M9&cRkIM{hfF^itE!#l!ZB4|<1C z_wqTg=g!^g3~DicsK~HY`s$46o^l+{+#%x3b<7>#?uT<{@i63P9`keln^Jy`BByd3 zPj^*i{A4j6ugHMPK7XUcFji(9u`!m_46zk{AxGTTPdc>9gC9fa#{&GFz+bDdejE4! I_ZIr_4=j6)r2qf` literal 0 HcmV?d00001