[PATCH RFC net-next 2/4] nf_flow_table: track sub-interface and bridge ifindex in flow tuple

Daniel Golle
2026-04-09T13:07:46Z

Store the net_device ifindex alongside each encap entry and for
bridge devices during path discovery so the flow offload stats
path can later update sub-interface (VLAN, PPPoE, bridge)
counters for hw-offloaded flows.

The indices are placed below __hash so they do not affect flow
tuple lookups.

No functional change -- the indices are stored but not yet used.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
include/net/netfilter/nf_flow_table.h | 5 +++++
net/netfilter/nf_flow_table_core.c | 2 ++
net/netfilter/nf_flow_table_path.c | 8 ++++++++
3 files changed, 15 insertions(+)

diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index b09c11c048d51..ec1a18cfd9621 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -148,6 +148,9 @@ struct flow_offload_tuple {
/* All members above are keys for lookups, see flow_offload_hash(). */
struct { } __hash;

+ int encap_ifidx[NF_FLOW_TABLE_ENCAP_MAX];
+ int bridge_ifidx;
+
u8 dir:2,
xmit_type:3,
encap_num:2,
@@ -221,11 +224,13 @@ struct nf_flow_route {
struct {
u16 id;
__be16 proto;
+ int ifindex;
} encap[NF_FLOW_TABLE_ENCAP_MAX];
struct flow_offload_tunnel tun;
u8 num_encaps:2,
num_tuns:2,
ingress_vlans:2;
+ int bridge_ifindex;
} in;
struct {
u32 ifindex;
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 2c4140e6f53c5..9bc8be177b392 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -115,6 +115,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
for (i = route->tuple[dir].in.num_encaps - 1; i >= 0; i--) {
flow_tuple->encap[j].id = route->tuple[dir].in.encap[i].id;
flow_tuple->encap[j].proto = route->tuple[dir].in.encap[i].proto;
+ flow_tuple->encap_ifidx[j] = route->tuple[dir].in.encap[i].ifindex;
if (route->tuple[dir].in.ingress_vlans & BIT(i))
flow_tuple->in_vlan_ingress |= BIT(j);
j++;
@@ -123,6 +124,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
flow_tuple->tun = route->tuple[dir].in.tun;
flow_tuple->encap_num = route->tuple[dir].in.num_encaps;
flow_tuple->tun_num = route->tuple[dir].in.num_tuns;
+ flow_tuple->bridge_ifidx = route->tuple[dir].in.bridge_ifindex;

switch (route->tuple[dir].xmit_type) {
case FLOW_OFFLOAD_XMIT_DIRECT:
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 6bb9579dcc2ab..c5817cb96a9f6 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -79,8 +79,10 @@ struct nft_forward_info {
struct id {
__u16 id;
__be16 proto;
+ int ifindex;
} encap[NF_FLOW_TABLE_ENCAP_MAX];
u8 num_encaps;
+ int bridge_ifindex;
struct flow_offload_tunnel tun;
u8 num_tuns;
u8 ingress_vlans;
@@ -136,12 +138,15 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
path->encap.id;
info->encap[info->num_encaps].proto =
path->encap.proto;
+ info->encap[info->num_encaps].ifindex =
+ path->dev->ifindex;
info->num_encaps++;
}
if (path->type == DEV_PATH_PPPOE)
memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
break;
case DEV_PATH_BRIDGE:
+ info->bridge_ifindex = path->dev->ifindex;
if (is_zero_ether_addr(info->h_source))
memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);

@@ -156,6 +161,7 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
}
info->encap[info->num_encaps].id = path->bridge.vlan_id;
info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
+ info->encap[info->num_encaps].ifindex = path->dev->ifindex;
info->num_encaps++;
break;
case DEV_PATH_BR_VLAN_UNTAG:
@@ -261,6 +267,7 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt,
for (i = 0; i < info.num_encaps; i++) {
route->tuple[!dir].in.encap[i].id = info.encap[i].id;
route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
+ route->tuple[!dir].in.encap[i].ifindex = info.encap[i].ifindex;
}

if (info.num_tuns &&
@@ -273,6 +280,7 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt,

route->tuple[!dir].in.num_encaps = info.num_encaps;
route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
+ route->tuple[!dir].in.bridge_ifindex = info.bridge_ifindex;

if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
--
2.53.0