* [PATCH v11 nf-next 1/6] bridge: Add filling forward path from port to port
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
@ 2025-04-08 14:27 ` Eric Woudstra
2025-04-08 14:27 ` [PATCH v11 nf-next 2/6] net: core: dev: Add dev_fill_bridge_path() Eric Woudstra
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:27 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
If a port is passed as argument instead of the master, then:
At br_fill_forward_path(): find the master and use it to fill the
forward path.
At br_vlan_fill_forward_path_pvid(): lookup vlan group from port
instead.
Changed call to br_vlan_group() into br_vlan_group_rcu() while at it.
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/bridge/br_device.c | 19 ++++++++++++++-----
net/bridge/br_private.h | 2 ++
net/bridge/br_vlan.c | 6 +++++-
3 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 80b75c2e229b..b4746a239b88 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -385,16 +385,25 @@ static int br_del_slave(struct net_device *dev, struct net_device *slave_dev)
static int br_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
+ struct net_bridge_port *src, *dst;
struct net_bridge_fdb_entry *f;
- struct net_bridge_port *dst;
struct net_bridge *br;
- if (netif_is_bridge_port(ctx->dev))
- return -1;
+ if (netif_is_bridge_port(ctx->dev)) {
+ struct net_device *br_dev;
+
+ br_dev = netdev_master_upper_dev_get_rcu((struct net_device *)ctx->dev);
+ if (!br_dev)
+ return -1;
- br = netdev_priv(ctx->dev);
+ src = br_port_get_rcu(ctx->dev);
+ br = netdev_priv(br_dev);
+ } else {
+ src = NULL;
+ br = netdev_priv(ctx->dev);
+ }
- br_vlan_fill_forward_path_pvid(br, ctx, path);
+ br_vlan_fill_forward_path_pvid(br, src, ctx, path);
f = br_fdb_find_rcu(br, ctx->daddr, path->bridge.vlan_id);
if (!f)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index c3395320a4f3..759e7685de9b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1584,6 +1584,7 @@ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end);
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
+ struct net_bridge_port *p,
struct net_device_path_ctx *ctx,
struct net_device_path *path);
int br_vlan_fill_forward_path_mode(struct net_bridge *br,
@@ -1753,6 +1754,7 @@ static inline int nbp_get_num_vlan_infos(struct net_bridge_port *p,
}
static inline void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
+ struct net_bridge_port *p,
struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 0f714df92118..114d47d5f90f 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1446,6 +1446,7 @@ int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
+ struct net_bridge_port *p,
struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
@@ -1458,7 +1459,10 @@ void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
return;
- vg = br_vlan_group(br);
+ if (p)
+ vg = nbp_vlan_group_rcu(p);
+ else
+ vg = br_vlan_group_rcu(br);
if (idx >= 0 &&
ctx->vlan[idx].proto == br->vlan_proto) {
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v11 nf-next 2/6] net: core: dev: Add dev_fill_bridge_path()
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
2025-04-08 14:27 ` [PATCH v11 nf-next 1/6] bridge: Add filling forward path from port to port Eric Woudstra
@ 2025-04-08 14:27 ` Eric Woudstra
2025-04-08 14:27 ` [PATCH v11 nf-next 3/6] netfilter :nf_flow_table_offload: Add nf_flow_rule_bridge() Eric Woudstra
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:27 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
New function dev_fill_bridge_path(), similar to dev_fill_forward_path().
It handles starting from a bridge port instead of the bridge master.
The structures ctx and nft_forward_info need to be already filled in with
the (vlan) encaps.
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
include/linux/netdevice.h | 2 ++
net/core/dev.c | 66 +++++++++++++++++++++++++++++++--------
2 files changed, 55 insertions(+), 13 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 4e8eaae8c441..cab2482a6967 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3323,6 +3323,8 @@ void dev_remove_offload(struct packet_offload *po);
int dev_get_iflink(const struct net_device *dev);
int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+int dev_fill_bridge_path(struct net_device_path_ctx *ctx,
+ struct net_device_path_stack *stack);
int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
struct net_device_path_stack *stack);
struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
diff --git a/net/core/dev.c b/net/core/dev.c
index 0608605cfc24..3c365e816107 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -729,44 +729,84 @@ static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
return &stack->path[k];
}
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
- struct net_device_path_stack *stack)
+static int dev_fill_forward_path_common(struct net_device_path_ctx *ctx,
+ struct net_device_path_stack *stack)
{
const struct net_device *last_dev;
- struct net_device_path_ctx ctx = {
- .dev = dev,
- };
struct net_device_path *path;
int ret = 0;
- memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
- stack->num_paths = 0;
- while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
- last_dev = ctx.dev;
+ while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) {
+ last_dev = ctx->dev;
path = dev_fwd_path(stack);
if (!path)
return -1;
memset(path, 0, sizeof(struct net_device_path));
- ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
+ ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path);
if (ret < 0)
return -1;
- if (WARN_ON_ONCE(last_dev == ctx.dev))
+ if (WARN_ON_ONCE(last_dev == ctx->dev))
return -1;
}
- if (!ctx.dev)
+ if (!ctx->dev)
return ret;
path = dev_fwd_path(stack);
if (!path)
return -1;
path->type = DEV_PATH_ETHERNET;
- path->dev = ctx.dev;
+ path->dev = ctx->dev;
return ret;
}
+
+int dev_fill_bridge_path(struct net_device_path_ctx *ctx,
+ struct net_device_path_stack *stack)
+{
+ const struct net_device *last_dev, *br_dev;
+ struct net_device_path *path;
+
+ stack->num_paths = 0;
+
+ if (!ctx->dev || !netif_is_bridge_port(ctx->dev))
+ return -1;
+
+ br_dev = netdev_master_upper_dev_get_rcu((struct net_device *)ctx->dev);
+ if (!br_dev || !br_dev->netdev_ops->ndo_fill_forward_path)
+ return -1;
+
+ last_dev = ctx->dev;
+ path = dev_fwd_path(stack);
+ if (!path)
+ return -1;
+
+ memset(path, 0, sizeof(struct net_device_path));
+ if (br_dev->netdev_ops->ndo_fill_forward_path(ctx, path) < 0)
+ return -1;
+
+ if (!ctx->dev || WARN_ON_ONCE(last_dev == ctx->dev))
+ return -1;
+
+ return dev_fill_forward_path_common(ctx, stack);
+}
+EXPORT_SYMBOL_GPL(dev_fill_bridge_path);
+
+int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+ struct net_device_path_stack *stack)
+{
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+ };
+
+ memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
+
+ stack->num_paths = 0;
+
+ return dev_fill_forward_path_common(&ctx, stack);
+}
EXPORT_SYMBOL_GPL(dev_fill_forward_path);
/* must be called under rcu_read_lock(), as we dont take a reference */
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v11 nf-next 3/6] netfilter :nf_flow_table_offload: Add nf_flow_rule_bridge()
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
2025-04-08 14:27 ` [PATCH v11 nf-next 1/6] bridge: Add filling forward path from port to port Eric Woudstra
2025-04-08 14:27 ` [PATCH v11 nf-next 2/6] net: core: dev: Add dev_fill_bridge_path() Eric Woudstra
@ 2025-04-08 14:27 ` Eric Woudstra
2025-04-08 14:28 ` [PATCH v11 nf-next 4/6] netfilter: nf_flow_table_inet: Add nf_flowtable_type flowtable_bridge Eric Woudstra
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:27 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
Add nf_flow_rule_bridge().
It only calls the common rule and adds the redirect.
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
include/net/netfilter/nf_flow_table.h | 3 +++
net/netfilter/nf_flow_table_offload.c | 13 +++++++++++++
2 files changed, 16 insertions(+)
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 9d9363e91587..f60ecedf2fa7 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -344,6 +344,9 @@ void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable);
int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
struct net_device *dev,
enum flow_block_command cmd);
+int nf_flow_rule_bridge(struct net *net, struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule);
int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule);
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index e06bc36f49fe..5543ce03a196 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -679,6 +679,19 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
return 0;
}
+int nf_flow_rule_bridge(struct net *net, struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
+{
+ if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
+ return -1;
+
+ flow_offload_redirect(net, flow, dir, flow_rule);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nf_flow_rule_bridge);
+
int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
enum flow_offload_tuple_dir dir,
struct nf_flow_rule *flow_rule)
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v11 nf-next 4/6] netfilter: nf_flow_table_inet: Add nf_flowtable_type flowtable_bridge
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
` (2 preceding siblings ...)
2025-04-08 14:27 ` [PATCH v11 nf-next 3/6] netfilter :nf_flow_table_offload: Add nf_flow_rule_bridge() Eric Woudstra
@ 2025-04-08 14:28 ` Eric Woudstra
2025-04-08 14:28 ` [PATCH v11 nf-next 5/6] netfilter: nft_flow_offload: Add NFPROTO_BRIDGE to validate Eric Woudstra
2025-04-08 14:28 ` [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval() Eric Woudstra
5 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:28 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
This will allow a flowtable to be added to the nft bridge family.
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/netfilter/nf_flow_table_inet.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c
index b0f199171932..80b238196f29 100644
--- a/net/netfilter/nf_flow_table_inet.c
+++ b/net/netfilter/nf_flow_table_inet.c
@@ -65,6 +65,16 @@ static int nf_flow_rule_route_inet(struct net *net,
return err;
}
+static struct nf_flowtable_type flowtable_bridge = {
+ .family = NFPROTO_BRIDGE,
+ .init = nf_flow_table_init,
+ .setup = nf_flow_table_offload_setup,
+ .action = nf_flow_rule_bridge,
+ .free = nf_flow_table_free,
+ .hook = nf_flow_offload_inet_hook,
+ .owner = THIS_MODULE,
+};
+
static struct nf_flowtable_type flowtable_inet = {
.family = NFPROTO_INET,
.init = nf_flow_table_init,
@@ -97,6 +107,7 @@ static struct nf_flowtable_type flowtable_ipv6 = {
static int __init nf_flow_inet_module_init(void)
{
+ nft_register_flowtable_type(&flowtable_bridge);
nft_register_flowtable_type(&flowtable_ipv4);
nft_register_flowtable_type(&flowtable_ipv6);
nft_register_flowtable_type(&flowtable_inet);
@@ -109,6 +120,7 @@ static void __exit nf_flow_inet_module_exit(void)
nft_unregister_flowtable_type(&flowtable_inet);
nft_unregister_flowtable_type(&flowtable_ipv6);
nft_unregister_flowtable_type(&flowtable_ipv4);
+ nft_unregister_flowtable_type(&flowtable_bridge);
}
module_init(nf_flow_inet_module_init);
@@ -118,5 +130,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_ALIAS_NF_FLOWTABLE(AF_INET);
MODULE_ALIAS_NF_FLOWTABLE(AF_INET6);
+MODULE_ALIAS_NF_FLOWTABLE(AF_BRIDGE);
MODULE_ALIAS_NF_FLOWTABLE(1); /* NFPROTO_INET */
MODULE_DESCRIPTION("Netfilter flow table mixed IPv4/IPv6 module");
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v11 nf-next 5/6] netfilter: nft_flow_offload: Add NFPROTO_BRIDGE to validate
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
` (3 preceding siblings ...)
2025-04-08 14:28 ` [PATCH v11 nf-next 4/6] netfilter: nf_flow_table_inet: Add nf_flowtable_type flowtable_bridge Eric Woudstra
@ 2025-04-08 14:28 ` Eric Woudstra
2025-04-08 14:28 ` [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval() Eric Woudstra
5 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:28 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
Need to add NFPROTO_BRIDGE to nft_flow_offload_validate() to support
the bridge-fastpath.
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/netfilter/nft_flow_offload.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index e77164424891..889393edc629 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -438,7 +438,8 @@ static int nft_flow_offload_validate(const struct nft_ctx *ctx,
if (ctx->family != NFPROTO_IPV4 &&
ctx->family != NFPROTO_IPV6 &&
- ctx->family != NFPROTO_INET)
+ ctx->family != NFPROTO_INET &&
+ ctx->family != NFPROTO_BRIDGE)
return -EOPNOTSUPP;
return nft_chain_validate_hooks(ctx->chain, hook_mask);
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval()
2025-04-08 14:27 [PATCH v11 nf-next 0/6] netfilter: Add bridge-fastpath Eric Woudstra
` (4 preceding siblings ...)
2025-04-08 14:28 ` [PATCH v11 nf-next 5/6] netfilter: nft_flow_offload: Add NFPROTO_BRIDGE to validate Eric Woudstra
@ 2025-04-08 14:28 ` Eric Woudstra
2025-04-11 10:57 ` Simon Horman
5 siblings, 1 reply; 9+ messages in thread
From: Eric Woudstra @ 2025-04-08 14:28 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
Edit nft_flow_offload_eval() to make it possible to handle a flowtable of
the nft bridge family.
Use nft_flow_offload_bridge_init() to fill the flow tuples. It uses
nft_dev_fill_bridge_path() in each direction.
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/netfilter/nft_flow_offload.c | 148 +++++++++++++++++++++++++++++--
1 file changed, 143 insertions(+), 5 deletions(-)
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 889393edc629..05294955881e 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -195,6 +195,134 @@ static bool nft_flowtable_find_dev(const struct net_device *dev,
return found;
}
+static int nft_dev_fill_bridge_path(struct flow_offload *flow,
+ struct nft_flowtable *ft,
+ enum ip_conntrack_dir dir,
+ const struct net_device *src_dev,
+ const struct net_device *dst_dev,
+ unsigned char *src_ha,
+ unsigned char *dst_ha)
+{
+ struct flow_offload_tuple_rhash *th = flow->tuplehash;
+ struct net_device_path_ctx ctx = {};
+ struct net_device_path_stack stack;
+ struct nft_forward_info info = {};
+ int i, j = 0;
+
+ for (i = th[dir].tuple.encap_num - 1; i >= 0 ; i--) {
+ if (info.num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
+ return -1;
+
+ if (th[dir].tuple.in_vlan_ingress & BIT(i))
+ continue;
+
+ info.encap[info.num_encaps].id = th[dir].tuple.encap[i].id;
+ info.encap[info.num_encaps].proto = th[dir].tuple.encap[i].proto;
+ info.num_encaps++;
+
+ if (th[dir].tuple.encap[i].proto == htons(ETH_P_PPP_SES))
+ continue;
+
+ if (ctx.num_vlans >= NET_DEVICE_PATH_VLAN_MAX)
+ return -1;
+ ctx.vlan[ctx.num_vlans].id = th[dir].tuple.encap[i].id;
+ ctx.vlan[ctx.num_vlans].proto = th[dir].tuple.encap[i].proto;
+ ctx.num_vlans++;
+ }
+ ctx.dev = src_dev;
+ ether_addr_copy(ctx.daddr, dst_ha);
+
+ if (dev_fill_bridge_path(&ctx, &stack) < 0)
+ return -1;
+
+ nft_dev_path_info(&stack, &info, dst_ha, &ft->data);
+
+ if (!info.indev || info.indev != dst_dev)
+ return -1;
+
+ th[!dir].tuple.iifidx = info.indev->ifindex;
+ for (i = info.num_encaps - 1; i >= 0; i--) {
+ th[!dir].tuple.encap[j].id = info.encap[i].id;
+ th[!dir].tuple.encap[j].proto = info.encap[i].proto;
+ if (info.ingress_vlans & BIT(i))
+ th[!dir].tuple.in_vlan_ingress |= BIT(j);
+ j++;
+ }
+ th[!dir].tuple.encap_num = info.num_encaps;
+
+ th[dir].tuple.mtu = dst_dev->mtu;
+ ether_addr_copy(th[dir].tuple.out.h_source, src_ha);
+ ether_addr_copy(th[dir].tuple.out.h_dest, dst_ha);
+ th[dir].tuple.out.ifidx = info.outdev->ifindex;
+ th[dir].tuple.out.hw_ifidx = info.hw_outdev->ifindex;
+ th[dir].tuple.out.bridge_vid = info.bridge_vid;
+ th[dir].tuple.xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+
+ return 0;
+}
+
+static int nft_flow_offload_bridge_init(struct flow_offload *flow,
+ const struct nft_pktinfo *pkt,
+ enum ip_conntrack_dir dir,
+ struct nft_flowtable *ft)
+{
+ const struct net_device *in_dev, *out_dev;
+ struct ethhdr *eth = eth_hdr(pkt->skb);
+ struct flow_offload_tuple *tuple;
+ int err, i = 0;
+
+ in_dev = nft_in(pkt);
+ if (!in_dev || !nft_flowtable_find_dev(in_dev, ft))
+ return -1;
+
+ out_dev = nft_out(pkt);
+ if (!out_dev || !nft_flowtable_find_dev(out_dev, ft))
+ return -1;
+
+ tuple = &flow->tuplehash[!dir].tuple;
+
+ if (skb_vlan_tag_present(pkt->skb)) {
+ tuple->encap[i].id = skb_vlan_tag_get(pkt->skb);
+ tuple->encap[i].proto = pkt->skb->vlan_proto;
+ i++;
+ }
+
+ switch (eth_hdr(pkt->skb)->h_proto) {
+ case htons(ETH_P_8021Q): {
+ struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb_mac_header(pkt->skb)
+ + sizeof(struct ethhdr));
+ tuple->encap[i].id = ntohs(vhdr->h_vlan_TCI);
+ tuple->encap[i].proto = htons(ETH_P_8021Q);
+ i++;
+ break;
+ }
+ case htons(ETH_P_PPP_SES): {
+ struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb_mac_header(pkt->skb)
+ + sizeof(struct ethhdr));
+
+ tuple->encap[i].id = ntohs(phdr->sid);
+ tuple->encap[i].proto = htons(ETH_P_PPP_SES);
+ i++;
+ break;
+ }
+ }
+ tuple->encap_num = i;
+
+ err = nft_dev_fill_bridge_path(flow, ft, !dir, out_dev, in_dev,
+ eth->h_dest, eth->h_source);
+ if (err < 0)
+ return err;
+
+ memset(tuple->encap, 0, sizeof(tuple->encap));
+
+ err = nft_dev_fill_bridge_path(flow, ft, dir, in_dev, out_dev,
+ eth->h_source, eth->h_dest);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
static void nft_dev_forward_path(struct nf_flow_route *route,
const struct nf_conn *ct,
enum ip_conntrack_dir dir,
@@ -315,6 +443,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
{
struct nft_flow_offload *priv = nft_expr_priv(expr);
struct nf_flowtable *flowtable = &priv->flowtable->data;
+ bool routing = flowtable->type->family != NFPROTO_BRIDGE;
struct tcphdr _tcph, *tcph = NULL;
struct nf_flow_route route = {};
enum ip_conntrack_info ctinfo;
@@ -368,14 +497,21 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
goto out;
dir = CTINFO2DIR(ctinfo);
- if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
- goto err_flow_route;
+ if (routing) {
+ if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
+ goto err_flow_route;
+ }
flow = flow_offload_alloc(ct);
if (!flow)
goto err_flow_alloc;
- flow_offload_route_init(flow, &route);
+ if (routing)
+ flow_offload_route_init(flow, &route);
+ else
+ if (nft_flow_offload_bridge_init(flow, pkt, dir, priv->flowtable) < 0)
+ goto err_flow_add;
+
if (tcph)
flow_offload_ct_tcp(ct);
@@ -423,8 +559,10 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
err_flow_add:
flow_offload_free(flow);
err_flow_alloc:
- dst_release(route.tuple[dir].dst);
- dst_release(route.tuple[!dir].dst);
+ if (routing) {
+ dst_release(route.tuple[dir].dst);
+ dst_release(route.tuple[!dir].dst);
+ }
err_flow_route:
clear_bit(IPS_OFFLOAD_BIT, &ct->status);
out:
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval()
2025-04-08 14:28 ` [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval() Eric Woudstra
@ 2025-04-11 10:57 ` Simon Horman
2025-04-11 15:24 ` Eric Woudstra
0 siblings, 1 reply; 9+ messages in thread
From: Simon Horman @ 2025-04-11 10:57 UTC (permalink / raw)
To: Eric Woudstra
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin, netdev,
netfilter-devel, bridge
On Tue, Apr 08, 2025 at 04:28:02PM +0200, Eric Woudstra wrote:
> Edit nft_flow_offload_eval() to make it possible to handle a flowtable of
> the nft bridge family.
>
> Use nft_flow_offload_bridge_init() to fill the flow tuples. It uses
> nft_dev_fill_bridge_path() in each direction.
>
> Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
> ---
> net/netfilter/nft_flow_offload.c | 148 +++++++++++++++++++++++++++++--
> 1 file changed, 143 insertions(+), 5 deletions(-)
>
> diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
...
> +static int nft_dev_fill_bridge_path(struct flow_offload *flow,
> + struct nft_flowtable *ft,
> + enum ip_conntrack_dir dir,
> + const struct net_device *src_dev,
> + const struct net_device *dst_dev,
> + unsigned char *src_ha,
> + unsigned char *dst_ha)
> +{
> + struct flow_offload_tuple_rhash *th = flow->tuplehash;
> + struct net_device_path_ctx ctx = {};
> + struct net_device_path_stack stack;
> + struct nft_forward_info info = {};
> + int i, j = 0;
> +
> + for (i = th[dir].tuple.encap_num - 1; i >= 0 ; i--) {
> + if (info.num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
> + return -1;
> +
> + if (th[dir].tuple.in_vlan_ingress & BIT(i))
> + continue;
> +
> + info.encap[info.num_encaps].id = th[dir].tuple.encap[i].id;
> + info.encap[info.num_encaps].proto = th[dir].tuple.encap[i].proto;
> + info.num_encaps++;
> +
> + if (th[dir].tuple.encap[i].proto == htons(ETH_P_PPP_SES))
> + continue;
> +
> + if (ctx.num_vlans >= NET_DEVICE_PATH_VLAN_MAX)
> + return -1;
> + ctx.vlan[ctx.num_vlans].id = th[dir].tuple.encap[i].id;
> + ctx.vlan[ctx.num_vlans].proto = th[dir].tuple.encap[i].proto;
> + ctx.num_vlans++;
> + }
> + ctx.dev = src_dev;
> + ether_addr_copy(ctx.daddr, dst_ha);
> +
> + if (dev_fill_bridge_path(&ctx, &stack) < 0)
> + return -1;
> +
> + nft_dev_path_info(&stack, &info, dst_ha, &ft->data);
> +
> + if (!info.indev || info.indev != dst_dev)
> + return -1;
> +
> + th[!dir].tuple.iifidx = info.indev->ifindex;
> + for (i = info.num_encaps - 1; i >= 0; i--) {
> + th[!dir].tuple.encap[j].id = info.encap[i].id;
> + th[!dir].tuple.encap[j].proto = info.encap[i].proto;
> + if (info.ingress_vlans & BIT(i))
> + th[!dir].tuple.in_vlan_ingress |= BIT(j);
> + j++;
> + }
> + th[!dir].tuple.encap_num = info.num_encaps;
> +
> + th[dir].tuple.mtu = dst_dev->mtu;
> + ether_addr_copy(th[dir].tuple.out.h_source, src_ha);
> + ether_addr_copy(th[dir].tuple.out.h_dest, dst_ha);
> + th[dir].tuple.out.ifidx = info.outdev->ifindex;
> + th[dir].tuple.out.hw_ifidx = info.hw_outdev->ifindex;
> + th[dir].tuple.out.bridge_vid = info.bridge_vid;
Hi Eric,
I guess I am doing something daft.
But with this patchset applied on top of nf-next I see
the following with allmodconfig builds on x86_64.:
CC [M] net/netfilter/nft_flow_offload.o
net/netfilter/nft_flow_offload.c: In function 'nft_dev_fill_bridge_path':
net/netfilter/nft_flow_offload.c:248:26: error: 'struct <anonymous>' has no member named 'bridge_vid'
248 | th[dir].tuple.out.bridge_vid = info.bridge_vid;
| ^
net/netfilter/nft_flow_offload.c:248:44: error: 'struct nft_forward_info' has no member named 'bridge_vid'
248 | th[dir].tuple.out.bridge_vid = info.bridge_vid;
| ^
> + th[dir].tuple.xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
> +
> + return 0;
> +}
...
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v11 nf-next 6/6] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval()
2025-04-11 10:57 ` Simon Horman
@ 2025-04-11 15:24 ` Eric Woudstra
0 siblings, 0 replies; 9+ messages in thread
From: Eric Woudstra @ 2025-04-11 15:24 UTC (permalink / raw)
To: Simon Horman
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, Pablo Neira Ayuso, Jozsef Kadlecsik,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Ahmed Zaki, Alexander Lobakin, netdev,
netfilter-devel, bridge
On 4/11/25 12:57 PM, Simon Horman wrote:
> On Tue, Apr 08, 2025 at 04:28:02PM +0200, Eric Woudstra wrote:
>> Edit nft_flow_offload_eval() to make it possible to handle a flowtable of
>> the nft bridge family.
>>
>> Use nft_flow_offload_bridge_init() to fill the flow tuples. It uses
>> nft_dev_fill_bridge_path() in each direction.
>>
>> Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
>> ---
>> net/netfilter/nft_flow_offload.c | 148 +++++++++++++++++++++++++++++--
>> 1 file changed, 143 insertions(+), 5 deletions(-)
>>
>> diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
>
> ...
>
>> +static int nft_dev_fill_bridge_path(struct flow_offload *flow,
>> + struct nft_flowtable *ft,
>> + enum ip_conntrack_dir dir,
>> + const struct net_device *src_dev,
>> + const struct net_device *dst_dev,
>> + unsigned char *src_ha,
>> + unsigned char *dst_ha)
>> +{
>> + struct flow_offload_tuple_rhash *th = flow->tuplehash;
>> + struct net_device_path_ctx ctx = {};
>> + struct net_device_path_stack stack;
>> + struct nft_forward_info info = {};
>> + int i, j = 0;
>> +
>> + for (i = th[dir].tuple.encap_num - 1; i >= 0 ; i--) {
>> + if (info.num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
>> + return -1;
>> +
>> + if (th[dir].tuple.in_vlan_ingress & BIT(i))
>> + continue;
>> +
>> + info.encap[info.num_encaps].id = th[dir].tuple.encap[i].id;
>> + info.encap[info.num_encaps].proto = th[dir].tuple.encap[i].proto;
>> + info.num_encaps++;
>> +
>> + if (th[dir].tuple.encap[i].proto == htons(ETH_P_PPP_SES))
>> + continue;
>> +
>> + if (ctx.num_vlans >= NET_DEVICE_PATH_VLAN_MAX)
>> + return -1;
>> + ctx.vlan[ctx.num_vlans].id = th[dir].tuple.encap[i].id;
>> + ctx.vlan[ctx.num_vlans].proto = th[dir].tuple.encap[i].proto;
>> + ctx.num_vlans++;
>> + }
>> + ctx.dev = src_dev;
>> + ether_addr_copy(ctx.daddr, dst_ha);
>> +
>> + if (dev_fill_bridge_path(&ctx, &stack) < 0)
>> + return -1;
>> +
>> + nft_dev_path_info(&stack, &info, dst_ha, &ft->data);
>> +
>> + if (!info.indev || info.indev != dst_dev)
>> + return -1;
>> +
>> + th[!dir].tuple.iifidx = info.indev->ifindex;
>> + for (i = info.num_encaps - 1; i >= 0; i--) {
>> + th[!dir].tuple.encap[j].id = info.encap[i].id;
>> + th[!dir].tuple.encap[j].proto = info.encap[i].proto;
>> + if (info.ingress_vlans & BIT(i))
>> + th[!dir].tuple.in_vlan_ingress |= BIT(j);
>> + j++;
>> + }
>> + th[!dir].tuple.encap_num = info.num_encaps;
>> +
>> + th[dir].tuple.mtu = dst_dev->mtu;
>> + ether_addr_copy(th[dir].tuple.out.h_source, src_ha);
>> + ether_addr_copy(th[dir].tuple.out.h_dest, dst_ha);
>> + th[dir].tuple.out.ifidx = info.outdev->ifindex;
>> + th[dir].tuple.out.hw_ifidx = info.hw_outdev->ifindex;
>> + th[dir].tuple.out.bridge_vid = info.bridge_vid;
>
> Hi Eric,
>
> I guess I am doing something daft.
> But with this patchset applied on top of nf-next I see
> the following with allmodconfig builds on x86_64.:
>
> CC [M] net/netfilter/nft_flow_offload.o
> net/netfilter/nft_flow_offload.c: In function 'nft_dev_fill_bridge_path':
> net/netfilter/nft_flow_offload.c:248:26: error: 'struct <anonymous>' has no member named 'bridge_vid'
> 248 | th[dir].tuple.out.bridge_vid = info.bridge_vid;
> | ^
> net/netfilter/nft_flow_offload.c:248:44: error: 'struct nft_forward_info' has no member named 'bridge_vid'
> 248 | th[dir].tuple.out.bridge_vid = info.bridge_vid;
> | ^
>
>> + th[dir].tuple.xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
>> +
>> + return 0;
>> +}
>
> ...
Hi Simon,
This is from the patch-set:
[PATCH v2 nf-next 0/3] flow offload teardown when layer 2 roaming
My guess is that it could be accepted before this patch-set.
They do not need each other, but 1 needs to be applied before the other.
Regards,
Eric
^ permalink raw reply [flat|nested] 9+ messages in thread