Re : Re: Rule with double check ignore with 802.1Q packet

"Antoine C." <[email protected]> Sat, 11 Oct 2025 15:57:17 +0200 (CEST)
Newsgroups gmane.comp.security.firewalls.netfilter.general
Message-ID <1402483045.1384217397.1760191037810.JavaMail.root@zimbra62-e11.priv.proxad.net>
----- Brian Davidson <[email protected]> a écrit :
> On Fri, Oct 10, 2025 at 1:45 PM Antoine C. <[email protected]> wrote:
> >
> > Hello,
> >
> > I found a weird behavior with NF tables. I am trying to filter specific packets and I cannot understand what is going on in the following case. It looks like a bug, but I do not know NFT very well so I prefer to ask first.
> >
> > I am using 2 virtual interfaces veth0 and veth1 on Linux:
> > Linux sopvubu22-214d 6.8.0-64-generic #67~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Jun 24 15:19:46 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
> >
> > Interfaces were created with:
> > ip link add veth0 type veth peer name veth1
> >
> >
> > NFT were configured with the following script:
> > ---
> > #!/usr/sbin/nft -f
> >
> > flush ruleset
> >
> > # INPUT Filtering
> > table netdev tb_in {
> >     chain ch_in {
> >         type filter hook ingress device veth1 priority 100;
> >         nftrace set 1
> >         ether saddr AA:BB:CC:DD:00:38   log prefix "--test 1-- "
> >         ip saddr 192.168.140.56         log prefix "--test 2-- "
> >         ether saddr AA:BB:CC:DD:00:38 ip saddr 192.168.140.56 log prefix "--tests 1&2 -- "
> >         ip saddr 192.168.140.56 ether saddr AA:BB:CC:DD:00:38  log prefix "--tests 2&1 -- "
> >     }
> > }
> > ---
> > Then I send this specific packet:
> > echo AA BB CC DD 00 50 AA BB CC DD 00 38 81 00 28 48 08 00 45 00 00 1C 00 01 00 00 40 11 E0 F6 C0 A8 8C 38 C0 A8 8C 50 1D 89 1D 83 00 08 2A F8 | xxd -r -p | socat - INTERFACE:veth0
> >
> > And I get the following log:
> > trace id 5235f40e netdev tb_in ch_in packet: iif "veth1" ether saddr aa:bb:cc:dd:00:38 ether daddr aa:bb:cc:dd:00:50 vlan pcp 1 vlan dei 0 vlan id 2120 ip saddr 192.168.140.56 ip daddr 192.168.140.80 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 1 ip protocol udp ip length 28 udp sport 7561 udp dport 7555 udp length 8
> > trace id 5235f40e netdev tb_in ch_in rule meta nftrace set 1 (verdict continue)
> > trace id 5235f40e netdev tb_in ch_in rule ether saddr aa:bb:cc:dd:00:38 log prefix "--test 1-- " (verdict continue)
> > trace id 5235f40e netdev tb_in ch_in rule ip saddr 192.168.140.56 log prefix "--test 2-- " (verdict continue)
> > trace id 5235f40e netdev tb_in ch_in rule ip saddr 192.168.140.56 ether saddr aa:bb:cc:dd:00:38 log prefix "--tests 2&1 -- " (verdict continue)
> > trace id 5235f40e netdev tb_in ch_in policy accept
> >
> > What is very strange is that the "test 1 & 2" is failing, despite being exactly identical, apart the order, to other tests. I get the same log with 'dmesg'.
> >
> > The packet sent can be decomposed in (ether + vlan + ip + udp) headers but no data. And if I remove the vlan part (81 00 28 48), it works.
> >
> > Is it a bug or do I miss something ? Maybe it has been corrected in a newer kernel version?
> >
> > Thanks for your help,
> > Antoine
> 
> Despite having the same expressions, the rules get interpreted
> differently by nftables. The second rule checks the meta protocol
> also. This is on kernel 6.6.104 and nftables 1.1.1.
> 
> # nft list table netdev t
> table netdev t {
>         chain c {
>               ether saddr aa:bb:cc:dd:00:38 ip saddr 192.168.140.56 \
> log prefix "--tests 1&2 --"
>               ip saddr 192.168.140.56 ether saddr aa:bb:cc:dd:00:38 \
> log prefix "--tests 2&1 --"
>         }
> }
> # nft list table netdev t | nft -c -d netlink -f /dev/stdin
> netdev (null) (null) use 0
> netdev t c
>   [ meta load iiftype => reg 1 ]
>   [ cmp eq reg 1 0x00000001 ]
>   [ payload load 8b @ link header + 6 => reg 1 ]
>   [ cmp eq reg 1 0xddccbbaa 0x00083800 ]
>   [ payload load 4b @ network header + 12 => reg 1 ]
>   [ cmp eq reg 1 0x388ca8c0 ]
>   [ log prefix --tests 1&2 -- ]
> netdev t c
>   [ meta load protocol => reg 1 ]
>   [ cmp eq reg 1 0x00000008 ]
>   [ payload load 4b @ network header + 12 => reg 1 ]
>   [ cmp eq reg 1 0x388ca8c0 ]
>   [ meta load iiftype => reg 1 ]
>   [ cmp eq reg 1 0x00000001 ]
>   [ payload load 6b @ link header + 6 => reg 1 ]
>   [ cmp eq reg 1 0xddccbbaa 0x00003800 ]
>   [ log prefix --tests 2&1 -- ]

Thanks Brian, I did not think of using '-d netlink'

However, my analysis is a bit different: both rules check 
the protocol, but in the `1&2` case, it is loaded at the 
same time than the MAC src, eight byte at a time, as an
erroneous optimization, because in that case the protocol 
is after the 802.1Q header. So it is a bug.

One workaround is to do as in the '2&1' case, by inverting
the order, however this is a bit weird as usually, the
parser is complaining with 
"Error: conflicting protocols specified: xxx vs yyy"
if the layers are specified in the wrong order.
Can anybody think of a better workaround?

Antoine