[PATCH net-next] netfilter: nf_flow_table_bpf: populate VLAN encap in XDP flowtable lookup
From: Adriano Cordova
Date: Thu Sep 24 2026 - 16:26:52 EST
encap[] is part of the flow_offload_tuple rhashtable key
(flow_offload_hash() hashes up to __hash), so a lookup has to fill it like
the skb path does. bpf_xdp_flow_lookup() builds its key from struct
bpf_fib_lookup only, which has no encap fields, so it leaves encap[]
zeroed and can never match a flow whose tuple carries a VLAN encap.
Fill encap[0] from an in-band 802.1Q tag, mirroring the ETH_P_8021Q case
of nf_flow_tuple_encap().
Link: https://github.com/xdp-project/xdp-tools/issues/488
Signed-off-by: Adriano Cordova <adrianox@xxxxxxxxx>
---
net/netfilter/nf_flow_table_bpf.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/net/netfilter/nf_flow_table_bpf.c b/net/netfilter/nf_flow_table_bpf.c
index cbd5b97a6329..479f8242da2b 100644
--- a/net/netfilter/nf_flow_table_bpf.c
+++ b/net/netfilter/nf_flow_table_bpf.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/if_vlan.h>
#include <net/netfilter/nf_flow_table.h>
#include <linux/bpf.h>
#include <linux/btf.h>
@@ -60,6 +61,8 @@ bpf_xdp_flow_lookup(struct xdp_md *ctx, struct bpf_fib_lookup *fib_tuple,
struct bpf_flowtable_opts *opts, u32 opts_len)
{
struct xdp_buff *xdp = (struct xdp_buff *)ctx;
+ const struct ethhdr *eth = xdp->data;
+ const struct vlan_hdr *vh;
struct flow_offload_tuple tuple = {
.iifidx = fib_tuple->ifindex,
.l3proto = fib_tuple->family,
@@ -91,6 +94,18 @@ bpf_xdp_flow_lookup(struct xdp_md *ctx, struct bpf_fib_lookup *fib_tuple,
return NULL;
}
+ /* encap[] is part of the lookup key, fill it from the 802.1Q tag. */
+ if (eth + 1 <= (const struct ethhdr *)xdp->data_end &&
+ eth->h_proto == htons(ETH_P_8021Q)) {
+ vh = (const struct vlan_hdr *)(eth + 1);
+ if (unlikely(vh + 1 > (const struct vlan_hdr *)xdp->data_end)) {
+ opts->error = -EINVAL;
+ return NULL;
+ }
+ tuple.encap[0].id = ntohs(vh->h_vlan_TCI);
+ tuple.encap[0].proto = htons(ETH_P_8021Q);
+ }
+
tuplehash = bpf_xdp_flow_tuple_lookup(xdp->rxq->dev, &tuple, proto);
if (IS_ERR(tuplehash)) {
opts->error = PTR_ERR(tuplehash);
--
2.51.0