* [nf-next PATCH v4 01/16] netfilter: nf_tables: Flowtable hook's pf value never varies
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 02/16] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
` (14 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
When checking for duplicate hooks in nft_register_flowtable_net_hooks(),
comparing ops.pf value is pointless as it is always NFPROTO_NETDEV with
flowtable hooks.
Dropping the check leaves the search identical to the one in
nft_hook_list_find() so call that function instead of open coding.
Fixes: 3f0465a9ef02 ("netfilter: nf_tables: dynamically allocate hooks per net_device in flowtables")
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 042080aeb46c..b85f15ed77ed 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8571,7 +8571,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
struct list_head *hook_list,
struct nft_flowtable *flowtable)
{
- struct nft_hook *hook, *hook2, *next;
+ struct nft_hook *hook, *next;
struct nft_flowtable *ft;
int err, i = 0;
@@ -8580,12 +8580,9 @@ static int nft_register_flowtable_net_hooks(struct net *net,
if (!nft_is_active_next(net, ft))
continue;
- list_for_each_entry(hook2, &ft->hook_list, list) {
- if (hook->ops.dev == hook2->ops.dev &&
- hook->ops.pf == hook2->ops.pf) {
- err = -EEXIST;
- goto err_unregister_net_hooks;
- }
+ if (nft_hook_list_find(&ft->hook_list, hook)) {
+ err = -EEXIST;
+ goto err_unregister_net_hooks;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 02/16] netfilter: nf_tables: Store user-defined hook ifname
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 01/16] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 03/16] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
` (13 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Prepare for hooks with NULL ops.dev pointer (due to non-existent device)
and store the interface name and length as specified by the user upon
creation. No functional change intended.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Assert max attribute length to avoid bogus values in hook->ifnamelen
as per Florian's suggestion.
---
include/net/netfilter/nf_tables.h | 2 ++
net/netfilter/nf_tables_api.c | 10 +++++++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 49708e7e1339..73714e9d9392 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1192,6 +1192,8 @@ struct nft_hook {
struct list_head list;
struct nf_hook_ops ops;
struct rcu_head rcu;
+ char ifname[IFNAMSIZ];
+ u8 ifnamelen;
};
/**
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index b85f15ed77ed..2ec3e407f91f 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2173,7 +2173,6 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
const struct nlattr *attr)
{
struct net_device *dev;
- char ifname[IFNAMSIZ];
struct nft_hook *hook;
int err;
@@ -2183,12 +2182,17 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
goto err_hook_alloc;
}
- nla_strscpy(ifname, attr, IFNAMSIZ);
+ err = nla_strscpy(hook->ifname, attr, IFNAMSIZ);
+ if (err < 0)
+ goto err_hook_dev;
+
+ hook->ifnamelen = nla_len(attr);
+
/* nf_tables_netdev_event() is called under rtnl_mutex, this is
* indirectly serializing all the other holders of the commit_mutex with
* the rtnl_mutex.
*/
- dev = __dev_get_by_name(net, ifname);
+ dev = __dev_get_by_name(net, hook->ifname);
if (!dev) {
err = -ENOENT;
goto err_hook_dev;
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 03/16] netfilter: nf_tables: Use stored ifname in netdev hook dumps
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 01/16] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 02/16] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 04/16] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
` (12 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
The stored ifname and ops.dev->name may deviate after creation due to
interface name changes. Prefer the more deterministic stored name in
dumps which also helps avoiding inadvertent changes to stored ruleset
dumps.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 2ec3e407f91f..c53afdecef24 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1853,15 +1853,16 @@ static int nft_dump_basechain_hook(struct sk_buff *skb, int family,
if (!first)
first = hook;
- if (nla_put_string(skb, NFTA_DEVICE_NAME,
- hook->ops.dev->name))
+ if (nla_put(skb, NFTA_DEVICE_NAME,
+ hook->ifnamelen, hook->ifname))
goto nla_put_failure;
n++;
}
nla_nest_end(skb, nest_devs);
if (n == 1 &&
- nla_put_string(skb, NFTA_HOOK_DEV, first->ops.dev->name))
+ nla_put(skb, NFTA_HOOK_DEV,
+ first->ifnamelen, first->ifname))
goto nla_put_failure;
}
nla_nest_end(skb, nest);
@@ -8999,7 +9000,8 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
hook_list = &flowtable->hook_list;
list_for_each_entry_rcu(hook, hook_list, list) {
- if (nla_put_string(skb, NFTA_DEVICE_NAME, hook->ops.dev->name))
+ if (nla_put(skb, NFTA_DEVICE_NAME,
+ hook->ifnamelen, hook->ifname))
goto nla_put_failure;
}
nla_nest_end(skb, nest_devs);
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 04/16] netfilter: nf_tables: Compare netdev hooks based on stored name
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (2 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 03/16] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 05/16] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
` (11 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
The 1:1 relationship between nft_hook and nf_hook_ops is about to break,
so choose the stored ifname to uniquely identify hooks.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index c53afdecef24..f6e28a6ac9b0 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2214,7 +2214,7 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
struct nft_hook *hook;
list_for_each_entry(hook, hook_list, list) {
- if (this->ops.dev == hook->ops.dev)
+ if (!strcmp(hook->ifname, this->ifname))
return hook;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 05/16] netfilter: nf_tables: Tolerate chains with no remaining hooks
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (3 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 04/16] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 06/16] netfilter: nf_tables: Introduce functions freeing nft_hook objects Phil Sutter
` (10 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Do not drop a netdev-family chain if the last interface it is registered
for vanishes. Users dumping and storing the ruleset upon shutdown for
restore upon next boot may otherwise lose the chain and all contained
rules. They will still lose the list of devices, a later patch will fix
that. For now, this aligns the event handler's behaviour with that for
flowtables.
The controversal situation at netns exit should be no problem here:
event handler will unregister the hooks, core nftables cleanup code will
drop the chain itself.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
include/net/netfilter/nf_tables.h | 2 --
net/netfilter/nf_tables_api.c | 21 ---------------------
net/netfilter/nft_chain_filter.c | 29 +++++++----------------------
3 files changed, 7 insertions(+), 45 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 73714e9d9392..6aa39c4a8c3c 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1229,8 +1229,6 @@ static inline bool nft_is_base_chain(const struct nft_chain *chain)
return chain->flags & NFT_CHAIN_BASE;
}
-int __nft_release_basechain(struct nft_ctx *ctx);
-
unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
static inline bool nft_use_inc(u32 *use)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index f6e28a6ac9b0..f77ba323a906 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -11433,27 +11433,6 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
}
EXPORT_SYMBOL_GPL(nft_data_dump);
-int __nft_release_basechain(struct nft_ctx *ctx)
-{
- struct nft_rule *rule, *nr;
-
- if (WARN_ON(!nft_is_base_chain(ctx->chain)))
- return 0;
-
- nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
- list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
- list_del(&rule->list);
- nft_use_dec(&ctx->chain->use);
- nf_tables_rule_release(ctx, rule);
- }
- nft_chain_del(ctx->chain);
- nft_use_dec(&ctx->table->use);
- nf_tables_chain_destroy(ctx->chain);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(__nft_release_basechain);
-
static void __nft_release_hook(struct net *net, struct nft_table *table)
{
struct nft_flowtable *flowtable;
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 7010541fcca6..543f258b7c6b 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -322,34 +322,19 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
struct nft_ctx *ctx)
{
struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
- struct nft_hook *hook, *found = NULL;
- int n = 0;
+ struct nft_hook *hook;
list_for_each_entry(hook, &basechain->hook_list, list) {
- if (hook->ops.dev == dev)
- found = hook;
-
- n++;
- }
- if (!found)
- return;
+ if (hook->ops.dev != dev)
+ continue;
- if (n > 1) {
if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(ctx->net, &found->ops);
+ nf_unregister_net_hook(ctx->net, &hook->ops);
- list_del_rcu(&found->list);
- kfree_rcu(found, rcu);
- return;
+ list_del_rcu(&hook->list);
+ kfree_rcu(hook, rcu);
+ break;
}
-
- /* UNREGISTER events are also happening on netns exit.
- *
- * Although nf_tables core releases all tables/chains, only this event
- * handler provides guarantee that hook->ops.dev is still accessible,
- * so we cannot skip exiting net namespaces.
- */
- __nft_release_basechain(ctx);
}
static int nf_tables_netdev_event(struct notifier_block *this,
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 06/16] netfilter: nf_tables: Introduce functions freeing nft_hook objects
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (4 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 05/16] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 07/16] netfilter: nf_tables: Introduce nft_hook_find_ops() Phil Sutter
` (9 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Pointless wrappers around kfree() for now, prep work for an embedded
list of nf_hook_ops.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Move function declarations up and use the wrapper in
nft_netdev_unregister_hooks(), too.
---
net/netfilter/nf_tables_api.c | 36 ++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index f77ba323a906..a0482c7fc659 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -322,6 +322,16 @@ static int nft_netdev_register_hooks(struct net *net,
return err;
}
+static void nft_netdev_hook_free(struct nft_hook *hook)
+{
+ kfree(hook);
+}
+
+static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
+{
+ kfree_rcu(hook, rcu);
+}
+
static void nft_netdev_unregister_hooks(struct net *net,
struct list_head *hook_list,
bool release_netdev)
@@ -332,7 +342,7 @@ static void nft_netdev_unregister_hooks(struct net *net,
nf_unregister_net_hook(net, &hook->ops);
if (release_netdev) {
list_del(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
}
}
@@ -2152,7 +2162,7 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
list_for_each_entry_safe(hook, next,
&basechain->hook_list, list) {
list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
}
module_put(basechain->type->owner);
@@ -2244,7 +2254,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
}
if (nft_hook_list_find(hook_list, hook)) {
NL_SET_BAD_ATTR(extack, tmp);
- kfree(hook);
+ nft_netdev_hook_free(hook);
err = -EEXIST;
goto err_hook;
}
@@ -2262,7 +2272,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
err_hook:
list_for_each_entry_safe(hook, next, hook_list, list) {
list_del(&hook->list);
- kfree(hook);
+ nft_netdev_hook_free(hook);
}
return err;
}
@@ -2405,7 +2415,7 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook)
list_for_each_entry_safe(h, next, &hook->list, list) {
list_del(&h->list);
- kfree(h);
+ nft_netdev_hook_free(h);
}
module_put(hook->type->owner);
}
@@ -2695,7 +2705,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
if (nft_hook_list_find(&basechain->hook_list, h)) {
list_del(&h->list);
- kfree(h);
+ nft_netdev_hook_free(h);
}
}
} else {
@@ -2816,7 +2826,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
if (unregister)
nf_unregister_net_hook(ctx->net, &h->ops);
list_del(&h->list);
- kfree_rcu(h, rcu);
+ nft_netdev_hook_free_rcu(h);
}
module_put(hook.type->owner);
}
@@ -8617,7 +8627,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
nft_unregister_flowtable_hook(net, flowtable, hook);
list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
return err;
@@ -8629,7 +8639,7 @@ static void nft_hooks_destroy(struct list_head *hook_list)
list_for_each_entry_safe(hook, next, hook_list, list) {
list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
}
@@ -8653,7 +8663,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
if (nft_hook_list_find(&flowtable->hook_list, hook)) {
list_del(&hook->list);
- kfree(hook);
+ nft_netdev_hook_free(hook);
}
}
@@ -8700,7 +8710,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
if (unregister)
nft_unregister_flowtable_hook(ctx->net, flowtable, hook);
list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
return err;
@@ -8846,7 +8856,7 @@ static void nft_flowtable_hook_release(struct nft_flowtable_hook *flowtable_hook
list_for_each_entry_safe(this, next, &flowtable_hook->list, list) {
list_del(&this->list);
- kfree(this);
+ nft_netdev_hook_free(this);
}
}
@@ -9210,7 +9220,7 @@ static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
flowtable->data.type->setup(&flowtable->data, hook->ops.dev,
FLOW_BLOCK_UNBIND);
list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_netdev_hook_free_rcu(hook);
}
kfree(flowtable->name);
module_put(flowtable->data.type->owner);
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 07/16] netfilter: nf_tables: Introduce nft_hook_find_ops()
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (5 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 06/16] netfilter: nf_tables: Introduce functions freeing nft_hook objects Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 08/16] netfilter: nf_tables: Introduce nft_register_flowtable_ops() Phil Sutter
` (8 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Also a pretty dull wrapper around the hook->ops.dev comparison for now.
Will search the embedded nf_hook_ops list in future. The ugly cast to
eliminate the const qualifier will vanish then, too.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
include/net/netfilter/nf_tables.h | 3 +++
net/netfilter/nf_tables_api.c | 14 +++++++++++++-
net/netfilter/nf_tables_offload.c | 2 +-
net/netfilter/nft_chain_filter.c | 6 ++++--
net/netfilter/nft_flow_offload.c | 2 +-
5 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 6aa39c4a8c3c..37d1110ccfd9 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1196,6 +1196,9 @@ struct nft_hook {
u8 ifnamelen;
};
+struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
+ const struct net_device *dev);
+
/**
* struct nft_base_chain - nf_tables base chain
*
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index a0482c7fc659..8326395c5752 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9253,13 +9253,25 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
return -EMSGSIZE;
}
+struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
+ const struct net_device *dev)
+{
+ if (hook->ops.dev == dev)
+ return (struct nf_hook_ops *)&hook->ops;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nft_hook_find_ops);
+
static void nft_flowtable_event(unsigned long event, struct net_device *dev,
struct nft_flowtable *flowtable)
{
+ struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry(hook, &flowtable->hook_list, list) {
- if (hook->ops.dev != dev)
+ ops = nft_hook_find_ops(hook, dev);
+ if (!ops)
continue;
/* flow_offload_netdev_event() cleans up entries for us. */
diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c
index 64675f1c7f29..75b756f0b9f0 100644
--- a/net/netfilter/nf_tables_offload.c
+++ b/net/netfilter/nf_tables_offload.c
@@ -638,7 +638,7 @@ static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *n
found = NULL;
basechain = nft_base_chain(chain);
list_for_each_entry(hook, &basechain->hook_list, list) {
- if (hook->ops.dev != dev)
+ if (!nft_hook_find_ops(hook, dev))
continue;
found = hook;
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 543f258b7c6b..d34c6fe7ba72 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -322,14 +322,16 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
struct nft_ctx *ctx)
{
struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
+ struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry(hook, &basechain->hook_list, list) {
- if (hook->ops.dev != dev)
+ ops = nft_hook_find_ops(hook, dev);
+ if (!ops)
continue;
if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(ctx->net, &hook->ops);
+ nf_unregister_net_hook(ctx->net, ops);
list_del_rcu(&hook->list);
kfree_rcu(hook, rcu);
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 2f732fae5a83..83415d7aadda 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -175,7 +175,7 @@ static bool nft_flowtable_find_dev(const struct net_device *dev,
bool found = false;
list_for_each_entry_rcu(hook, &ft->hook_list, list) {
- if (hook->ops.dev != dev)
+ if (!nft_hook_find_ops(hook, dev))
continue;
found = true;
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 08/16] netfilter: nf_tables: Introduce nft_register_flowtable_ops()
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (6 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 07/16] netfilter: nf_tables: Introduce nft_hook_find_ops() Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 09/16] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks() Phil Sutter
` (7 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Facilitate binding and registering of a flowtable hook via a single
function call.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 8326395c5752..3721f4636e0a 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8581,6 +8581,26 @@ static void nft_unregister_flowtable_net_hooks(struct net *net,
__nft_unregister_flowtable_net_hooks(net, hook_list, false);
}
+static int nft_register_flowtable_ops(struct net *net,
+ struct nft_flowtable *flowtable,
+ struct nf_hook_ops *ops)
+{
+ int err;
+
+ err = flowtable->data.type->setup(&flowtable->data,
+ ops->dev, FLOW_BLOCK_BIND);
+ if (err < 0)
+ return err;
+
+ err = nf_register_net_hook(net, ops);
+ if (!err)
+ return 0;
+
+ flowtable->data.type->setup(&flowtable->data,
+ ops->dev, FLOW_BLOCK_UNBIND);
+ return err;
+}
+
static int nft_register_flowtable_net_hooks(struct net *net,
struct nft_table *table,
struct list_head *hook_list,
@@ -8601,20 +8621,10 @@ static int nft_register_flowtable_net_hooks(struct net *net,
}
}
- err = flowtable->data.type->setup(&flowtable->data,
- hook->ops.dev,
- FLOW_BLOCK_BIND);
+ err = nft_register_flowtable_ops(net, flowtable, &hook->ops);
if (err < 0)
goto err_unregister_net_hooks;
- err = nf_register_net_hook(net, &hook->ops);
- if (err < 0) {
- flowtable->data.type->setup(&flowtable->data,
- hook->ops.dev,
- FLOW_BLOCK_UNBIND);
- goto err_unregister_net_hooks;
- }
-
i++;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 09/16] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks()
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (7 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 08/16] netfilter: nf_tables: Introduce nft_register_flowtable_ops() Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 10/16] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook Phil Sutter
` (6 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
The function is a 1:1 copy of nft_netdev_unregister_hooks(), use the
latter in its place.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 3721f4636e0a..7a721df27f12 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8560,25 +8560,10 @@ static void nft_unregister_flowtable_hook(struct net *net,
FLOW_BLOCK_UNBIND);
}
-static void __nft_unregister_flowtable_net_hooks(struct net *net,
- struct list_head *hook_list,
- bool release_netdev)
-{
- struct nft_hook *hook, *next;
-
- list_for_each_entry_safe(hook, next, hook_list, list) {
- nf_unregister_net_hook(net, &hook->ops);
- if (release_netdev) {
- list_del(&hook->list);
- kfree_rcu(hook, rcu);
- }
- }
-}
-
static void nft_unregister_flowtable_net_hooks(struct net *net,
struct list_head *hook_list)
{
- __nft_unregister_flowtable_net_hooks(net, hook_list, false);
+ nft_netdev_unregister_hooks(net, hook_list, false);
}
static int nft_register_flowtable_ops(struct net *net,
@@ -11473,8 +11458,7 @@ static void __nft_release_hook(struct net *net, struct nft_table *table)
list_for_each_entry(chain, &table->chains, list)
__nf_tables_unregister_hook(net, table, chain, true);
list_for_each_entry(flowtable, &table->flowtables, list)
- __nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list,
- true);
+ nft_netdev_unregister_hooks(net, &flowtable->hook_list, true);
}
static void __nft_release_hooks(struct net *net)
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 10/16] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (8 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 09/16] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks() Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 11/16] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events Phil Sutter
` (5 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Supporting a 1:n relationship between nft_hook and nf_hook_ops is
convenient since a chain's or flowtable's nft_hooks may remain in place
despite matching interfaces disappearing. This stabilizes ruleset dumps
in that regard and opens the possibility to claim newly added interfaces
which match the spec. Also it prepares for wildcard interface specs
since these will potentially match multiple interfaces.
All spots dealing with hook registration are updated to handle a list of
multiple nf_hook_ops, but nft_netdev_hook_alloc() only adds a single
item for now to retain the old behaviour. The only expected functional
change here is how vanishing interfaces are handled: Instead of dropping
the respective nft_hook, only the matching nf_hook_ops are dropped.
To safely remove individual ops from the list in netdev handlers, an
rcu_head is added to struct nf_hook_ops so kfree_rcu() may be used.
There is at least nft_flowtable_find_dev() which may be iterating
through the list at the same time.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Add an rcu_head to nf_hook_ops.
- RCU-free an nft_hook along with its ops_list via call_rcu() and a
callback.
---
include/linux/netfilter.h | 3 +
include/net/netfilter/nf_tables.h | 2 +-
net/netfilter/nf_tables_api.c | 149 +++++++++++++++++++++---------
net/netfilter/nf_tables_offload.c | 49 +++++-----
net/netfilter/nft_chain_filter.c | 6 +-
5 files changed, 138 insertions(+), 71 deletions(-)
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 2683b2b77612..18372de587b4 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -95,6 +95,9 @@ enum nf_hook_ops_type {
};
struct nf_hook_ops {
+ struct list_head list;
+ struct rcu_head rcu;
+
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 37d1110ccfd9..eaf2f5184bdf 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1190,7 +1190,7 @@ struct nft_stats {
struct nft_hook {
struct list_head list;
- struct nf_hook_ops ops;
+ struct list_head ops_list;
struct rcu_head rcu;
char ifname[IFNAMSIZ];
u8 ifnamelen;
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 7a721df27f12..f3b0bc2fe0e3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -299,47 +299,72 @@ void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
static int nft_netdev_register_hooks(struct net *net,
struct list_head *hook_list)
{
+ struct nf_hook_ops *ops;
struct nft_hook *hook;
int err, j;
j = 0;
list_for_each_entry(hook, hook_list, list) {
- err = nf_register_net_hook(net, &hook->ops);
- if (err < 0)
- goto err_register;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ err = nf_register_net_hook(net, ops);
+ if (err < 0)
+ goto err_register;
- j++;
+ j++;
+ }
}
return 0;
err_register:
list_for_each_entry(hook, hook_list, list) {
- if (j-- <= 0)
- break;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ if (j-- <= 0)
+ break;
- nf_unregister_net_hook(net, &hook->ops);
+ nf_unregister_net_hook(net, ops);
+ }
}
return err;
}
+static void nft_netdev_hook_free_ops(struct nft_hook *hook)
+{
+ struct nf_hook_ops *ops, *next;
+
+ list_for_each_entry_safe(ops, next, &hook->ops_list, list) {
+ list_del(&ops->list);
+ kfree(ops);
+ }
+}
+
static void nft_netdev_hook_free(struct nft_hook *hook)
{
+ nft_netdev_hook_free_ops(hook);
kfree(hook);
}
+static void __nft_netdev_hook_free_rcu(struct rcu_head *rcu)
+{
+ struct nft_hook *hook = container_of(rcu, struct nft_hook, rcu);
+
+ nft_netdev_hook_free(hook);
+}
+
static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
{
- kfree_rcu(hook, rcu);
+ call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu);
}
static void nft_netdev_unregister_hooks(struct net *net,
struct list_head *hook_list,
bool release_netdev)
{
+ struct nf_hook_ops *ops, *nextops;
struct nft_hook *hook, *next;
list_for_each_entry_safe(hook, next, hook_list, list) {
- nf_unregister_net_hook(net, &hook->ops);
+ list_for_each_entry_safe(ops, nextops, &hook->ops_list, list)
+ nf_unregister_net_hook(net, ops);
if (release_netdev) {
list_del(&hook->list);
nft_netdev_hook_free_rcu(hook);
@@ -2183,6 +2208,7 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
const struct nlattr *attr)
{
+ struct nf_hook_ops *ops;
struct net_device *dev;
struct nft_hook *hook;
int err;
@@ -2192,6 +2218,7 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
err = -ENOMEM;
goto err_hook_alloc;
}
+ INIT_LIST_HEAD(&hook->ops_list);
err = nla_strscpy(hook->ifname, attr, IFNAMSIZ);
if (err < 0)
@@ -2208,7 +2235,14 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
err = -ENOENT;
goto err_hook_dev;
}
- hook->ops.dev = dev;
+
+ ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
+ if (!ops) {
+ err = -ENOMEM;
+ goto err_hook_dev;
+ }
+ ops->dev = dev;
+ list_add_tail(&ops->list, &hook->ops_list);
return hook;
@@ -2468,6 +2502,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
struct nft_chain_hook *hook, u32 flags)
{
struct nft_chain *chain;
+ struct nf_hook_ops *ops;
struct nft_hook *h;
basechain->type = hook->type;
@@ -2476,8 +2511,10 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
if (nft_base_chain_netdev(family, hook->num)) {
list_splice_init(&hook->list, &basechain->hook_list);
- list_for_each_entry(h, &basechain->hook_list, list)
- nft_basechain_hook_init(&h->ops, family, hook, chain);
+ list_for_each_entry(h, &basechain->hook_list, list) {
+ list_for_each_entry(ops, &h->ops_list, list)
+ nft_basechain_hook_init(ops, family, hook, chain);
+ }
}
nft_basechain_hook_init(&basechain->ops, family, hook, chain);
@@ -2697,11 +2734,13 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
list_for_each_entry_safe(h, next, &hook.list, list) {
- h->ops.pf = basechain->ops.pf;
- h->ops.hooknum = basechain->ops.hooknum;
- h->ops.priority = basechain->ops.priority;
- h->ops.priv = basechain->ops.priv;
- h->ops.hook = basechain->ops.hook;
+ list_for_each_entry(ops, &h->ops_list, list) {
+ ops->pf = basechain->ops.pf;
+ ops->hooknum = basechain->ops.hooknum;
+ ops->priority = basechain->ops.priority;
+ ops->priv = basechain->ops.priv;
+ ops->hook = basechain->ops.hook;
+ }
if (nft_hook_list_find(&basechain->hook_list, h)) {
list_del(&h->list);
@@ -2823,8 +2862,10 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
err_hooks:
if (nla[NFTA_CHAIN_HOOK]) {
list_for_each_entry_safe(h, next, &hook.list, list) {
- if (unregister)
- nf_unregister_net_hook(ctx->net, &h->ops);
+ if (unregister) {
+ list_for_each_entry(ops, &h->ops_list, list)
+ nf_unregister_net_hook(ctx->net, ops);
+ }
list_del(&h->list);
nft_netdev_hook_free_rcu(h);
}
@@ -8451,6 +8492,7 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
struct netlink_ext_ack *extack, bool add)
{
struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
+ struct nf_hook_ops *ops;
struct nft_hook *hook;
int hooknum, priority;
int err;
@@ -8505,11 +8547,13 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
}
list_for_each_entry(hook, &flowtable_hook->list, list) {
- hook->ops.pf = NFPROTO_NETDEV;
- hook->ops.hooknum = flowtable_hook->num;
- hook->ops.priority = flowtable_hook->priority;
- hook->ops.priv = &flowtable->data;
- hook->ops.hook = flowtable->data.type->hook;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ ops->pf = NFPROTO_NETDEV;
+ ops->hooknum = flowtable_hook->num;
+ ops->priority = flowtable_hook->priority;
+ ops->priv = &flowtable->data;
+ ops->hook = flowtable->data.type->hook;
+ }
}
return err;
@@ -8551,12 +8595,12 @@ nft_flowtable_type_get(struct net *net, u8 family)
}
/* Only called from error and netdev event paths. */
-static void nft_unregister_flowtable_hook(struct net *net,
- struct nft_flowtable *flowtable,
- struct nft_hook *hook)
+static void nft_unregister_flowtable_ops(struct net *net,
+ struct nft_flowtable *flowtable,
+ struct nf_hook_ops *ops)
{
- nf_unregister_net_hook(net, &hook->ops);
- flowtable->data.type->setup(&flowtable->data, hook->ops.dev,
+ nf_unregister_net_hook(net, ops);
+ flowtable->data.type->setup(&flowtable->data, ops->dev,
FLOW_BLOCK_UNBIND);
}
@@ -8593,6 +8637,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
{
struct nft_hook *hook, *next;
struct nft_flowtable *ft;
+ struct nf_hook_ops *ops;
int err, i = 0;
list_for_each_entry(hook, hook_list, list) {
@@ -8606,21 +8651,25 @@ static int nft_register_flowtable_net_hooks(struct net *net,
}
}
- err = nft_register_flowtable_ops(net, flowtable, &hook->ops);
- if (err < 0)
- goto err_unregister_net_hooks;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ err = nft_register_flowtable_ops(net, flowtable, ops);
+ if (err < 0)
+ goto err_unregister_net_hooks;
- i++;
+ i++;
+ }
}
return 0;
err_unregister_net_hooks:
list_for_each_entry_safe(hook, next, hook_list, list) {
- if (i-- <= 0)
- break;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ if (i-- <= 0)
+ break;
- nft_unregister_flowtable_hook(net, flowtable, hook);
+ nft_unregister_flowtable_ops(net, flowtable, ops);
+ }
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
@@ -8645,6 +8694,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
const struct nlattr * const *nla = ctx->nla;
struct nft_flowtable_hook flowtable_hook;
struct nft_hook *hook, *next;
+ struct nf_hook_ops *ops;
struct nft_trans *trans;
bool unregister = false;
u32 flags;
@@ -8702,8 +8752,11 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
err_flowtable_update_hook:
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
- if (unregister)
- nft_unregister_flowtable_hook(ctx->net, flowtable, hook);
+ if (unregister) {
+ list_for_each_entry(ops, &hook->ops_list, list)
+ nft_unregister_flowtable_ops(ctx->net,
+ flowtable, ops);
+ }
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
@@ -9209,11 +9262,14 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
{
struct nft_hook *hook, *next;
+ struct nf_hook_ops *ops;
flowtable->data.type->free(&flowtable->data);
list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
- flowtable->data.type->setup(&flowtable->data, hook->ops.dev,
- FLOW_BLOCK_UNBIND);
+ list_for_each_entry(ops, &hook->ops_list, list)
+ flowtable->data.type->setup(&flowtable->data,
+ ops->dev,
+ FLOW_BLOCK_UNBIND);
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
@@ -9251,9 +9307,12 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
const struct net_device *dev)
{
- if (hook->ops.dev == dev)
- return (struct nf_hook_ops *)&hook->ops;
+ struct nf_hook_ops *ops;
+ list_for_each_entry_rcu(ops, &hook->ops_list, list) {
+ if (ops->dev == dev)
+ return ops;
+ }
return NULL;
}
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
@@ -9270,9 +9329,9 @@ static void nft_flowtable_event(unsigned long event, struct net_device *dev,
continue;
/* flow_offload_netdev_event() cleans up entries for us. */
- nft_unregister_flowtable_hook(dev_net(dev), flowtable, hook);
- list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
+ nft_unregister_flowtable_ops(dev_net(dev), flowtable, ops);
+ list_del_rcu(&ops->list);
+ kfree_rcu(ops, rcu);
break;
}
}
diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c
index 75b756f0b9f0..fd30e205de84 100644
--- a/net/netfilter/nf_tables_offload.c
+++ b/net/netfilter/nf_tables_offload.c
@@ -220,6 +220,7 @@ static int nft_chain_offload_priority(const struct nft_base_chain *basechain)
bool nft_chain_offload_support(const struct nft_base_chain *basechain)
{
+ struct nf_hook_ops *ops;
struct net_device *dev;
struct nft_hook *hook;
@@ -227,13 +228,16 @@ bool nft_chain_offload_support(const struct nft_base_chain *basechain)
return false;
list_for_each_entry(hook, &basechain->hook_list, list) {
- if (hook->ops.pf != NFPROTO_NETDEV ||
- hook->ops.hooknum != NF_NETDEV_INGRESS)
- return false;
-
- dev = hook->ops.dev;
- if (!dev->netdev_ops->ndo_setup_tc && !flow_indr_dev_exists())
- return false;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ if (ops->pf != NFPROTO_NETDEV ||
+ ops->hooknum != NF_NETDEV_INGRESS)
+ return false;
+
+ dev = ops->dev;
+ if (!dev->netdev_ops->ndo_setup_tc &&
+ !flow_indr_dev_exists())
+ return false;
+ }
}
return true;
@@ -455,34 +459,37 @@ static int nft_flow_block_chain(struct nft_base_chain *basechain,
const struct net_device *this_dev,
enum flow_block_command cmd)
{
- struct net_device *dev;
+ struct nf_hook_ops *ops;
struct nft_hook *hook;
int err, i = 0;
list_for_each_entry(hook, &basechain->hook_list, list) {
- dev = hook->ops.dev;
- if (this_dev && this_dev != dev)
- continue;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ if (this_dev && this_dev != ops->dev)
+ continue;
- err = nft_chain_offload_cmd(basechain, dev, cmd);
- if (err < 0 && cmd == FLOW_BLOCK_BIND) {
- if (!this_dev)
- goto err_flow_block;
+ err = nft_chain_offload_cmd(basechain, ops->dev, cmd);
+ if (err < 0 && cmd == FLOW_BLOCK_BIND) {
+ if (!this_dev)
+ goto err_flow_block;
- return err;
+ return err;
+ }
+ i++;
}
- i++;
}
return 0;
err_flow_block:
list_for_each_entry(hook, &basechain->hook_list, list) {
- if (i-- <= 0)
- break;
+ list_for_each_entry(ops, &hook->ops_list, list) {
+ if (i-- <= 0)
+ break;
- dev = hook->ops.dev;
- nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
+ nft_chain_offload_cmd(basechain, ops->dev,
+ FLOW_BLOCK_UNBIND);
+ }
}
return err;
}
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index d34c6fe7ba72..f8c69d28d656 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -332,10 +332,8 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
nf_unregister_net_hook(ctx->net, ops);
-
- list_del_rcu(&hook->list);
- kfree_rcu(hook, rcu);
- break;
+ list_del_rcu(&ops->list);
+ kfree_rcu(ops, rcu);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 11/16] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (9 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 10/16] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 12/16] netfilter: nf_tables: flowtable: " Phil Sutter
` (4 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Hook into new devices if their name matches the hook spec.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Use list_add_tail_rcu() to avoid breaking readers.
- Use kmemdup() instead of kzalloc() && memcpy() as per Florian.
- Return NOTIFY_BAD upon error instead of printing an error message,
also suggested by Florian.
---
net/netfilter/nft_chain_filter.c | 50 +++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 11 deletions(-)
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index f8c69d28d656..562af2773a66 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -318,23 +318,47 @@ static const struct nft_chain_type nft_chain_filter_netdev = {
},
};
-static void nft_netdev_event(unsigned long event, struct net_device *dev,
- struct nft_ctx *ctx)
+static int nft_netdev_event(unsigned long event, struct net_device *dev,
+ struct nft_ctx *ctx)
{
struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry(hook, &basechain->hook_list, list) {
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
- continue;
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ ops = nft_hook_find_ops(hook, dev);
+ if (!ops)
+ continue;
+
+ if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
+ nf_unregister_net_hook(ctx->net, ops);
+ list_del_rcu(&ops->list);
+ kfree_rcu(ops, rcu);
+ break;
+ case NETDEV_REGISTER:
+ if (strcmp(hook->ifname, dev->name))
+ continue;
- if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(ctx->net, ops);
- list_del_rcu(&ops->list);
- kfree_rcu(ops, rcu);
+ ops = kmemdup(&basechain->ops,
+ sizeof(struct nf_hook_ops),
+ GFP_KERNEL_ACCOUNT);
+ if (!ops)
+ return 1;
+
+ ops->dev = dev;
+
+ if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT) &&
+ nf_register_net_hook(dev_net(dev), ops)) {
+ kfree(ops);
+ return 1;
+ }
+ list_add_tail_rcu(&ops->list, &hook->ops_list);
+ break;
+ }
}
+ return 0;
}
static int nf_tables_netdev_event(struct notifier_block *this,
@@ -349,7 +373,8 @@ static int nf_tables_netdev_event(struct notifier_block *this,
.net = dev_net(dev),
};
- if (event != NETDEV_UNREGISTER)
+ if (event != NETDEV_REGISTER &&
+ event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
nft_net = nft_pernet(ctx.net);
@@ -371,7 +396,10 @@ static int nf_tables_netdev_event(struct notifier_block *this,
continue;
ctx.chain = chain;
- nft_netdev_event(event, dev, &ctx);
+ if (nft_netdev_event(event, dev, &ctx)) {
+ mutex_unlock(&nft_net->commit_mutex);
+ return NOTIFY_BAD;
+ }
}
}
mutex_unlock(&nft_net->commit_mutex);
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 12/16] netfilter: nf_tables: flowtable: Respect NETDEV_REGISTER events
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (10 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 11/16] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
` (3 subsequent siblings)
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Hook into new devices if their name matches the hook spec.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Use list_add_tail_rcu() to avoid breaking readers.
- Return NOTIFY_BAD upon error instead of printing an error message as
per Florian.
---
net/netfilter/nf_tables_api.c | 56 +++++++++++++++++++++++++++--------
1 file changed, 43 insertions(+), 13 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index f3b0bc2fe0e3..2684990dd3dc 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9317,23 +9317,49 @@ struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
}
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
-static void nft_flowtable_event(unsigned long event, struct net_device *dev,
- struct nft_flowtable *flowtable)
+static int nft_flowtable_event(unsigned long event, struct net_device *dev,
+ struct nft_flowtable *flowtable)
{
struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry(hook, &flowtable->hook_list, list) {
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
- continue;
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ ops = nft_hook_find_ops(hook, dev);
+ if (!ops)
+ continue;
- /* flow_offload_netdev_event() cleans up entries for us. */
- nft_unregister_flowtable_ops(dev_net(dev), flowtable, ops);
- list_del_rcu(&ops->list);
- kfree_rcu(ops, rcu);
- break;
+ /* flow_offload_netdev_event() cleans up entries for us. */
+ nft_unregister_flowtable_ops(dev_net(dev),
+ flowtable, ops);
+ list_del_rcu(&ops->list);
+ kfree_rcu(ops, rcu);
+ break;
+ case NETDEV_REGISTER:
+ if (strcmp(hook->ifname, dev->name))
+ continue;
+ ops = kzalloc(sizeof(struct nf_hook_ops),
+ GFP_KERNEL_ACCOUNT);
+ if (!ops)
+ return 1;
+
+ ops->pf = NFPROTO_NETDEV;
+ ops->hooknum = flowtable->hooknum;
+ ops->priority = flowtable->data.priority;
+ ops->priv = &flowtable->data;
+ ops->hook = flowtable->data.type->hook;
+ ops->dev = dev;
+ if (nft_register_flowtable_ops(dev_net(dev),
+ flowtable, ops)) {
+ kfree(ops);
+ return 1;
+ }
+ list_add_tail_rcu(&ops->list, &hook->ops_list);
+ break;
+ }
}
+ return 0;
}
static int nf_tables_flowtable_event(struct notifier_block *this,
@@ -9345,15 +9371,19 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
struct nft_table *table;
struct net *net;
- if (event != NETDEV_UNREGISTER)
- return 0;
+ if (event != NETDEV_REGISTER &&
+ event != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
net = dev_net(dev);
nft_net = nft_pernet(net);
mutex_lock(&nft_net->commit_mutex);
list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) {
- nft_flowtable_event(event, dev, flowtable);
+ if (nft_flowtable_event(event, dev, flowtable)) {
+ mutex_unlock(&nft_net->commit_mutex);
+ return NOTIFY_BAD;
+ }
}
}
mutex_unlock(&nft_net->commit_mutex);
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (11 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 12/16] netfilter: nf_tables: flowtable: " Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-22 7:32 ` Florian Westphal
2024-09-20 20:23 ` [nf-next PATCH v4 14/16] netfilter: nf_tables: Support wildcard netdev hook specs Phil Sutter
` (2 subsequent siblings)
15 siblings, 1 reply; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
For the sake of simplicity, treat them like consecutive NETDEV_REGISTER
and NETDEV_UNREGISTER events. If the new name matches a hook spec and
registration fails, escalate the error and keep things as they are.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v3:
- Register first and handle errors to avoid having unregistered the
device but registration fails.
---
net/netfilter/nf_tables_api.c | 5 +++++
net/netfilter/nft_chain_filter.c | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 2684990dd3dc..4d40c1905735 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9371,6 +9371,11 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
struct nft_table *table;
struct net *net;
+ if (event == NETDEV_CHANGENAME) {
+ if (nf_tables_flowtable_event(this, NETDEV_REGISTER, ptr))
+ return NOTIFY_BAD;
+ event = NETDEV_UNREGISTER;
+ }
if (event != NETDEV_REGISTER &&
event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 562af2773a66..0f5706addfcb 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -373,6 +373,11 @@ static int nf_tables_netdev_event(struct notifier_block *this,
.net = dev_net(dev),
};
+ if (event == NETDEV_CHANGENAME) {
+ if (nf_tables_netdev_event(this, NETDEV_REGISTER, ptr))
+ return NOTIFY_BAD;
+ event = NETDEV_UNREGISTER;
+ }
if (event != NETDEV_REGISTER &&
event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events
2024-09-20 20:23 ` [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
@ 2024-09-22 7:32 ` Florian Westphal
2024-09-22 10:48 ` Phil Sutter
0 siblings, 1 reply; 24+ messages in thread
From: Florian Westphal @ 2024-09-22 7:32 UTC (permalink / raw)
To: Phil Sutter
Cc: Pablo Neira Ayuso, netfilter-devel, Florian Westphal, Eric Garver
Phil Sutter <phil@nwl.cc> wrote:
> For the sake of simplicity, treat them like consecutive NETDEV_REGISTER
> and NETDEV_UNREGISTER events. If the new name matches a hook spec and
> registration fails, escalate the error and keep things as they are.
>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> Changes since v3:
> - Register first and handle errors to avoid having unregistered the
> device but registration fails.
> ---
> net/netfilter/nf_tables_api.c | 5 +++++
> net/netfilter/nft_chain_filter.c | 5 +++++
> 2 files changed, 10 insertions(+)
>
> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> index 2684990dd3dc..4d40c1905735 100644
> --- a/net/netfilter/nf_tables_api.c
> +++ b/net/netfilter/nf_tables_api.c
> @@ -9371,6 +9371,11 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
> struct nft_table *table;
> struct net *net;
>
> + if (event == NETDEV_CHANGENAME) {
> + if (nf_tables_flowtable_event(this, NETDEV_REGISTER, ptr))
> + return NOTIFY_BAD;
> + event = NETDEV_UNREGISTER;
> + }
Consider flowtable that should claim devices "pv*".
You get CHANGENAME, device name is, say, pv5.
Device name is registered in nf_tables_flowtable_event().
Then, event is set to UNREGISTER.
AFAICS this may unreg the device again immediately, as unreg part
only compares device pointer and we can't be sure the device was
part of any flowtable when CHANGENAME was triggered.
So I think nf_tables_flowtable_event() must handle CHANGENAME
directly, first check if any flowtable holds the device at this time,
then check if we need to register it with a new name, and do unreg
only if it was previously part of any flowtable.
Same logic needed for netdev chains.
Does that make sense?
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events
2024-09-22 7:32 ` Florian Westphal
@ 2024-09-22 10:48 ` Phil Sutter
0 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-22 10:48 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel, Eric Garver
On Sun, Sep 22, 2024 at 09:32:24AM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
> > For the sake of simplicity, treat them like consecutive NETDEV_REGISTER
> > and NETDEV_UNREGISTER events. If the new name matches a hook spec and
> > registration fails, escalate the error and keep things as they are.
> >
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> > Changes since v3:
> > - Register first and handle errors to avoid having unregistered the
> > device but registration fails.
> > ---
> > net/netfilter/nf_tables_api.c | 5 +++++
> > net/netfilter/nft_chain_filter.c | 5 +++++
> > 2 files changed, 10 insertions(+)
> >
> > diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> > index 2684990dd3dc..4d40c1905735 100644
> > --- a/net/netfilter/nf_tables_api.c
> > +++ b/net/netfilter/nf_tables_api.c
> > @@ -9371,6 +9371,11 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
> > struct nft_table *table;
> > struct net *net;
> >
> > + if (event == NETDEV_CHANGENAME) {
> > + if (nf_tables_flowtable_event(this, NETDEV_REGISTER, ptr))
> > + return NOTIFY_BAD;
> > + event = NETDEV_UNREGISTER;
> > + }
>
> Consider flowtable that should claim devices "pv*".
> You get CHANGENAME, device name is, say, pv5.
>
> Device name is registered in nf_tables_flowtable_event().
> Then, event is set to UNREGISTER.
>
> AFAICS this may unreg the device again immediately, as unreg part
> only compares device pointer and we can't be sure the device was
> part of any flowtable when CHANGENAME was triggered.
>
> So I think nf_tables_flowtable_event() must handle CHANGENAME
> directly, first check if any flowtable holds the device at this time,
> then check if we need to register it with a new name, and do unreg
> only if it was previously part of any flowtable.
>
> Same logic needed for netdev chains.
>
> Does that make sense?
Oh, you're right: Registering the device again (with new name) then
searching *all* flowtables for the device and unregistering it will
undo the previous registration, too! This obviously needs proper
testing, too.
Thanks, Phil
^ permalink raw reply [flat|nested] 24+ messages in thread
* [nf-next PATCH v4 14/16] netfilter: nf_tables: Support wildcard netdev hook specs
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (12 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 13/16] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes Phil Sutter
2024-09-20 20:23 ` [nf-next PATCH v4 16/16] selftests: netfilter: Torture nftables netdev hooks Phil Sutter
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
User space may pass non-nul-terminated NFTA_DEVICE_NAME attribute values
to indicate a suffix wildcard.
Expect for multiple devices to match the given prefix in
nft_netdev_hook_alloc() and populate 'ops_list' with them all.
When checking for duplicate hooks, compare the shortest prefix so a
device may never match more than a single hook spec.
Finally respect the stored prefix length when hooking into new devices
from event handlers.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
net/netfilter/nf_tables_api.c | 33 ++++++++++++++++----------------
net/netfilter/nft_chain_filter.c | 2 +-
2 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 4d40c1905735..ba2038ea56d2 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2222,7 +2222,7 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
err = nla_strscpy(hook->ifname, attr, IFNAMSIZ);
if (err < 0)
- goto err_hook_dev;
+ goto err_ops_alloc;
hook->ifnamelen = nla_len(attr);
@@ -2230,24 +2230,22 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
* indirectly serializing all the other holders of the commit_mutex with
* the rtnl_mutex.
*/
- dev = __dev_get_by_name(net, hook->ifname);
- if (!dev) {
- err = -ENOENT;
- goto err_hook_dev;
- }
+ for_each_netdev(net, dev) {
+ if (strncmp(dev->name, hook->ifname, hook->ifnamelen))
+ continue;
- ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
- if (!ops) {
- err = -ENOMEM;
- goto err_hook_dev;
+ ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
+ if (!ops) {
+ err = -ENOMEM;
+ goto err_ops_alloc;
+ }
+ ops->dev = dev;
+ list_add_tail(&ops->list, &hook->ops_list);
}
- ops->dev = dev;
- list_add_tail(&ops->list, &hook->ops_list);
-
return hook;
-err_hook_dev:
- kfree(hook);
+err_ops_alloc:
+ nft_netdev_hook_free(hook);
err_hook_alloc:
return ERR_PTR(err);
}
@@ -2258,7 +2256,8 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
struct nft_hook *hook;
list_for_each_entry(hook, hook_list, list) {
- if (!strcmp(hook->ifname, this->ifname))
+ if (!strncmp(hook->ifname, this->ifname,
+ min(hook->ifnamelen, this->ifnamelen)))
return hook;
}
@@ -9337,7 +9336,7 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev,
kfree_rcu(ops, rcu);
break;
case NETDEV_REGISTER:
- if (strcmp(hook->ifname, dev->name))
+ if (strncmp(hook->ifname, dev->name, hook->ifnamelen))
continue;
ops = kzalloc(sizeof(struct nf_hook_ops),
GFP_KERNEL_ACCOUNT);
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 0f5706addfcb..f7290dc20a53 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -338,7 +338,7 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev,
kfree_rcu(ops, rcu);
break;
case NETDEV_REGISTER:
- if (strcmp(hook->ifname, dev->name))
+ if (strncmp(hook->ifname, dev->name, hook->ifnamelen))
continue;
ops = kmemdup(&basechain->ops,
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (13 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 14/16] netfilter: nf_tables: Support wildcard netdev hook specs Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
2024-09-21 9:10 ` Florian Westphal
2024-09-20 20:23 ` [nf-next PATCH v4 16/16] selftests: netfilter: Torture nftables netdev hooks Phil Sutter
15 siblings, 1 reply; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Notify user space if netdev hooks are updated due to netdev add/remove
events. Send minimal notification messages by introducing
NFT_MSG_NEWDEV/DELDEV message types describing a single device only.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
include/net/netfilter/nf_tables.h | 2 +
include/uapi/linux/netfilter/nf_tables.h | 5 +++
net/netfilter/nf_tables_api.c | 56 ++++++++++++++++++++++++
net/netfilter/nft_chain_filter.c | 1 +
4 files changed, 64 insertions(+)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index eaf2f5184bdf..f8da38e45277 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1132,6 +1132,8 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
+void nf_tables_chain_device_notify(const struct nft_chain *chain,
+ const struct net_device *dev, int event);
enum nft_chain_types {
NFT_CHAIN_T_DEFAULT = 0,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index d6476ca5d7a6..3a874febf1ac 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -142,6 +142,8 @@ enum nf_tables_msg_types {
NFT_MSG_DESTROYOBJ,
NFT_MSG_DESTROYFLOWTABLE,
NFT_MSG_GETSETELEM_RESET,
+ NFT_MSG_NEWDEV,
+ NFT_MSG_DELDEV,
NFT_MSG_MAX,
};
@@ -1772,6 +1774,9 @@ enum nft_synproxy_attributes {
enum nft_devices_attributes {
NFTA_DEVICE_UNSPEC,
NFTA_DEVICE_NAME,
+ NFTA_DEVICE_TABLE,
+ NFTA_DEVICE_FLOWTABLE,
+ NFTA_DEVICE_CHAIN,
__NFTA_DEVICE_MAX
};
#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index ba2038ea56d2..463ad196a32e 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9316,6 +9316,61 @@ struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
}
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
+static void
+nf_tables_device_notify(const struct nft_table *table, int attr,
+ const char *name, const struct net_device *dev,
+ int event)
+{
+ struct net *net = dev_net(dev);
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ u16 flags = 0;
+
+ if (!nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+ return;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ goto err;
+
+ event = event == NETDEV_REGISTER ? NFT_MSG_NEWDEV : NFT_MSG_DELDEV;
+ event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
+ nlh = nfnl_msg_put(skb, 0, 0, event, flags, table->family,
+ NFNETLINK_V0, nft_base_seq(net));
+ if (!nlh)
+ goto err;
+
+ if (nla_put_string(skb, NFTA_DEVICE_TABLE, table->name) ||
+ nla_put_string(skb, attr, name) ||
+ nla_put_string(skb, NFTA_DEVICE_NAME, dev->name))
+ goto err;
+
+ nlmsg_end(skb, nlh);
+ nfnetlink_send(skb, net, 0, NFNLGRP_NFTABLES,
+ nlmsg_report(nlh), GFP_KERNEL);
+ return;
+err:
+ if (skb)
+ kfree_skb(skb);
+ nfnetlink_set_err(net, 0, NFNLGRP_NFTABLES, -ENOBUFS);
+}
+
+void
+nf_tables_chain_device_notify(const struct nft_chain *chain,
+ const struct net_device *dev, int event)
+{
+ nf_tables_device_notify(chain->table, NFTA_DEVICE_CHAIN,
+ chain->name, dev, event);
+}
+
+static void
+nf_tables_flowtable_device_notify(const struct nft_flowtable *ft,
+ const struct net_device *dev, int event)
+{
+ nf_tables_device_notify(ft->table, NFTA_DEVICE_FLOWTABLE,
+ ft->name, dev, event);
+}
+
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
struct nft_flowtable *flowtable)
{
@@ -9357,6 +9412,7 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev,
list_add_tail_rcu(&ops->list, &hook->ops_list);
break;
}
+ nf_tables_flowtable_device_notify(flowtable, dev, event);
}
return 0;
}
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index f7290dc20a53..7a35b034d0c6 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -357,6 +357,7 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev,
list_add_tail_rcu(&ops->list, &hook->ops_list);
break;
}
+ nf_tables_chain_device_notify(ctx->chain, dev, event);
}
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread* Re: [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-20 20:23 ` [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes Phil Sutter
@ 2024-09-21 9:10 ` Florian Westphal
2024-09-25 17:25 ` Phil Sutter
0 siblings, 1 reply; 24+ messages in thread
From: Florian Westphal @ 2024-09-21 9:10 UTC (permalink / raw)
To: Phil Sutter
Cc: Pablo Neira Ayuso, netfilter-devel, Florian Westphal, Eric Garver
Phil Sutter <phil@nwl.cc> wrote:
> Notify user space if netdev hooks are updated due to netdev add/remove
> events. Send minimal notification messages by introducing
> NFT_MSG_NEWDEV/DELDEV message types describing a single device only.
>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> include/net/netfilter/nf_tables.h | 2 +
> include/uapi/linux/netfilter/nf_tables.h | 5 +++
> net/netfilter/nf_tables_api.c | 56 ++++++++++++++++++++++++
> net/netfilter/nft_chain_filter.c | 1 +
> 4 files changed, 64 insertions(+)
>
> diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
> index eaf2f5184bdf..f8da38e45277 100644
> --- a/include/net/netfilter/nf_tables.h
> +++ b/include/net/netfilter/nf_tables.h
> @@ -1132,6 +1132,8 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
> int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
> int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
> void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
> +void nf_tables_chain_device_notify(const struct nft_chain *chain,
> + const struct net_device *dev, int event);
>
> enum nft_chain_types {
> NFT_CHAIN_T_DEFAULT = 0,
> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> index d6476ca5d7a6..3a874febf1ac 100644
> --- a/include/uapi/linux/netfilter/nf_tables.h
> +++ b/include/uapi/linux/netfilter/nf_tables.h
> @@ -142,6 +142,8 @@ enum nf_tables_msg_types {
> NFT_MSG_DESTROYOBJ,
> NFT_MSG_DESTROYFLOWTABLE,
> NFT_MSG_GETSETELEM_RESET,
> + NFT_MSG_NEWDEV,
> + NFT_MSG_DELDEV,
This relies on implicit NFNL_CB_UNSPEC == 0 and nfnetlink
bailing out whe NFT_MSG_NEWDEV appears in a netlink message
coming from userspace.
Is there precedence for this?
If not, maybe better to add explicit entries to the
nf_tables_cb[] array?
AFAICS its fine as-is, nfnetlink won't blindly invoke
NULL ->call() pointer, but I'm not sure this was designed
to be this way or if this is a coincidence.
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-21 9:10 ` Florian Westphal
@ 2024-09-25 17:25 ` Phil Sutter
2024-09-25 17:51 ` Florian Westphal
0 siblings, 1 reply; 24+ messages in thread
From: Phil Sutter @ 2024-09-25 17:25 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel, Eric Garver
On Sat, Sep 21, 2024 at 11:10:34AM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
> > Notify user space if netdev hooks are updated due to netdev add/remove
> > events. Send minimal notification messages by introducing
> > NFT_MSG_NEWDEV/DELDEV message types describing a single device only.
> >
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> > include/net/netfilter/nf_tables.h | 2 +
> > include/uapi/linux/netfilter/nf_tables.h | 5 +++
> > net/netfilter/nf_tables_api.c | 56 ++++++++++++++++++++++++
> > net/netfilter/nft_chain_filter.c | 1 +
> > 4 files changed, 64 insertions(+)
> >
> > diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
> > index eaf2f5184bdf..f8da38e45277 100644
> > --- a/include/net/netfilter/nf_tables.h
> > +++ b/include/net/netfilter/nf_tables.h
> > @@ -1132,6 +1132,8 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
> > int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
> > int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
> > void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
> > +void nf_tables_chain_device_notify(const struct nft_chain *chain,
> > + const struct net_device *dev, int event);
> >
> > enum nft_chain_types {
> > NFT_CHAIN_T_DEFAULT = 0,
> > diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> > index d6476ca5d7a6..3a874febf1ac 100644
> > --- a/include/uapi/linux/netfilter/nf_tables.h
> > +++ b/include/uapi/linux/netfilter/nf_tables.h
> > @@ -142,6 +142,8 @@ enum nf_tables_msg_types {
> > NFT_MSG_DESTROYOBJ,
> > NFT_MSG_DESTROYFLOWTABLE,
> > NFT_MSG_GETSETELEM_RESET,
> > + NFT_MSG_NEWDEV,
> > + NFT_MSG_DELDEV,
>
> This relies on implicit NFNL_CB_UNSPEC == 0 and nfnetlink
> bailing out whe NFT_MSG_NEWDEV appears in a netlink message
> coming from userspace.
I guess with 'implicit NFNL_CB_UNSPEC == 0' you mean the extra
nf_tables_cb array fields' 'type' value being 0 (nfnetlink.h explicitly
defines NFNL_CB_UNSPEC value as 0). I don't see the connection here
though, probably I miss nfnetlink_rcv_msg() relying on that field value
or so.
I do see implicit dependency on attr_count field being 0 via
nla_parse_deprecated().
> Is there precedence for this?
> If not, maybe better to add explicit entries to the
> nf_tables_cb[] array?
>
> AFAICS its fine as-is, nfnetlink won't blindly invoke
> NULL ->call() pointer, but I'm not sure this was designed
> to be this way or if this is a coincidence.
I see at least NFNL_MSG_ACCT_OVERQUOTA missing from nfnl_acct_cb. The
former was introduced in 2014. May I claim grandfathering? ;)
Cheers, Phil
^ permalink raw reply [flat|nested] 24+ messages in thread* Re: [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-25 17:25 ` Phil Sutter
@ 2024-09-25 17:51 ` Florian Westphal
2024-09-25 18:16 ` Phil Sutter
0 siblings, 1 reply; 24+ messages in thread
From: Florian Westphal @ 2024-09-25 17:51 UTC (permalink / raw)
To: Phil Sutter, Florian Westphal, Pablo Neira Ayuso, netfilter-devel,
Eric Garver
Phil Sutter <phil@nwl.cc> wrote:
> > This relies on implicit NFNL_CB_UNSPEC == 0 and nfnetlink
> > bailing out whe NFT_MSG_NEWDEV appears in a netlink message
> > coming from userspace.
>
> I guess with 'implicit NFNL_CB_UNSPEC == 0' you mean the extra
> nf_tables_cb array fields' 'type' value being 0 (nfnetlink.h explicitly
> defines NFNL_CB_UNSPEC value as 0). I don't see the connection here
> though, probably I miss nfnetlink_rcv_msg() relying on that field value
> or so.
I should have been more clear, I was wondering if we need/want
an -EOPNOTSUPP stub callback rather than reliance of nfnetlink to
detect it.
> I see at least NFNL_MSG_ACCT_OVERQUOTA missing from nfnl_acct_cb. The
> former was introduced in 2014. May I claim grandfathering? ;)
I guess it just means "no we don't worry about it".
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-25 17:51 ` Florian Westphal
@ 2024-09-25 18:16 ` Phil Sutter
2024-09-25 18:17 ` Florian Westphal
0 siblings, 1 reply; 24+ messages in thread
From: Phil Sutter @ 2024-09-25 18:16 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel, Eric Garver
On Wed, Sep 25, 2024 at 07:51:54PM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
> > > This relies on implicit NFNL_CB_UNSPEC == 0 and nfnetlink
> > > bailing out whe NFT_MSG_NEWDEV appears in a netlink message
> > > coming from userspace.
> >
> > I guess with 'implicit NFNL_CB_UNSPEC == 0' you mean the extra
> > nf_tables_cb array fields' 'type' value being 0 (nfnetlink.h explicitly
> > defines NFNL_CB_UNSPEC value as 0). I don't see the connection here
> > though, probably I miss nfnetlink_rcv_msg() relying on that field value
> > or so.
>
> I should have been more clear, I was wondering if we need/want
> an -EOPNOTSUPP stub callback rather than reliance of nfnetlink to
> detect it.
Sure, I got your point. The NFNL_CB_UNSPEC reference was just a bit
confusing.
> > I see at least NFNL_MSG_ACCT_OVERQUOTA missing from nfnl_acct_cb. The
> > former was introduced in 2014. May I claim grandfathering? ;)
>
> I guess it just means "no we don't worry about it".
Maybe. At least we rely upon the behaviour for a while now, possibly by
accident.
We could get rid of the nc->call != NULL check by assigning such stub in
nfnetlink_subsys_register(). OK, technically it would just move the NULL
check. Without such stunts, nfnetlink_rcv_msg() would have to remain
as-is to cover for future users with holes, right?
Cheers, Phil
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes
2024-09-25 18:16 ` Phil Sutter
@ 2024-09-25 18:17 ` Florian Westphal
0 siblings, 0 replies; 24+ messages in thread
From: Florian Westphal @ 2024-09-25 18:17 UTC (permalink / raw)
To: Phil Sutter, Florian Westphal, Pablo Neira Ayuso, netfilter-devel,
Eric Garver
Phil Sutter <phil@nwl.cc> wrote:
> We could get rid of the nc->call != NULL check by assigning such stub in
> nfnetlink_subsys_register(). OK, technically it would just move the NULL
> check. Without such stunts, nfnetlink_rcv_msg() would have to remain
> as-is to cover for future users with holes, right?
I think we should leave it as-is, I thought this was the first use
of NULL .call but as you found thats not the case.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [nf-next PATCH v4 16/16] selftests: netfilter: Torture nftables netdev hooks
2024-09-20 20:23 [nf-next PATCH v4 00/16] Dynamic hook interface binding Phil Sutter
` (14 preceding siblings ...)
2024-09-20 20:23 ` [nf-next PATCH v4 15/16] netfilter: nf_tables: Add notications for hook changes Phil Sutter
@ 2024-09-20 20:23 ` Phil Sutter
15 siblings, 0 replies; 24+ messages in thread
From: Phil Sutter @ 2024-09-20 20:23 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Add a ruleset which binds to various interface names via netdev-family
chains and flowtables and massage the notifiers by frequently renaming
interfaces to match these names. While doing so:
- Keep an 'nft monitor' running in background to receive the notifications
- Loop over 'nft list ruleset' to exercise ruleset dump codepath
- Have iperf running so the involved chains/flowtables see traffic
If supported, also test interface wildcard support separately by
creating a flowtable with 'wild*' interface spec and quickly add/remove
matching dummy interfaces.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
.../testing/selftests/net/netfilter/Makefile | 1 +
.../net/netfilter/nft_interface_stress.sh | 149 ++++++++++++++++++
2 files changed, 150 insertions(+)
create mode 100755 tools/testing/selftests/net/netfilter/nft_interface_stress.sh
diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile
index d13fb5ea3e89..823e0acf7171 100644
--- a/tools/testing/selftests/net/netfilter/Makefile
+++ b/tools/testing/selftests/net/netfilter/Makefile
@@ -21,6 +21,7 @@ TEST_PROGS += nft_concat_range.sh
TEST_PROGS += nft_conntrack_helper.sh
TEST_PROGS += nft_fib.sh
TEST_PROGS += nft_flowtable.sh
+TEST_PROGS += nft_interface_stress.sh
TEST_PROGS += nft_meta.sh
TEST_PROGS += nft_nat.sh
TEST_PROGS += nft_nat_zones.sh
diff --git a/tools/testing/selftests/net/netfilter/nft_interface_stress.sh b/tools/testing/selftests/net/netfilter/nft_interface_stress.sh
new file mode 100755
index 000000000000..92ce1d35ec19
--- /dev/null
+++ b/tools/testing/selftests/net/netfilter/nft_interface_stress.sh
@@ -0,0 +1,149 @@
+#!/bin/bash -e
+#
+# SPDX-License-Identifier: GPL-2.0
+#
+# Torture nftables' netdevice notifier callbacks and related code by frequent
+# renaming of interfaces which netdev-family chains and flowtables hook into.
+
+source lib.sh
+
+checktool "nft --version" "run test without nft tool"
+checktool "iperf3 --version" "run test without iperf3 tool"
+
+# how many seconds to torture the kernel, default to 80% of max run time
+TEST_RUNTIME=$((${kselftest_timeout:-60} * 8 / 10))
+
+trap "cleanup_all_ns" EXIT
+
+setup_ns nsc nsr nss
+
+ip -net $nsc link add cr0 type veth peer name rc0 netns $nsr
+ip -net $nsc addr add 10.0.0.1/24 dev cr0
+ip -net $nsc link set cr0 up
+ip -net $nsc route add default via 10.0.0.2
+
+ip -net $nss link add sr0 type veth peer name rs0 netns $nsr
+ip -net $nss addr add 10.1.0.1/24 dev sr0
+ip -net $nss link set sr0 up
+ip -net $nss route add default via 10.1.0.2
+
+ip -net $nsr addr add 10.0.0.2/24 dev rc0
+ip -net $nsr link set rc0 up
+ip -net $nsr addr add 10.1.0.2/24 dev rs0
+ip -net $nsr link set rs0 up
+ip netns exec $nsr sysctl -q net.ipv4.ip_forward=1
+ip netns exec $nsr sysctl -q net.ipv4.conf.all.forwarding=1
+
+{
+ echo "table netdev t {"
+ for ((i = 0; i < 10; i++)); do
+ cat <<-EOF
+ chain chain_rc$i {
+ type filter hook ingress device rc$i priority 0
+ counter
+ }
+ chain chain_rs$i {
+ type filter hook ingress device rs$i priority 0
+ counter
+ }
+ EOF
+ done
+ echo "}"
+ echo "table ip t {"
+ for ((i = 0; i < 10; i++)); do
+ cat <<-EOF
+ flowtable ft_${i} {
+ hook ingress priority 0
+ devices = { rc$i, rs$i }
+ }
+ EOF
+ done
+ echo "chain c {"
+ echo "type filter hook forward priority 0"
+ for ((i = 0; i < 10; i++)); do
+ echo -n "iifname rc$i oifname rs$i "
+ echo "ip protocol tcp counter flow add @ft_${i}"
+ done
+ echo "counter"
+ echo "}"
+ echo "}"
+} | ip netns exec $nsr nft -f - || {
+ echo "SKIP: Could not load nft ruleset"
+ exit $ksft_skip
+}
+
+for ((o=0, n=1; ; o=n, n++, n %= 10)); do
+ ip -net $nsr link set rc$o name rc$n
+ ip -net $nsr link set rs$o name rs$n
+done &
+rename_loop_pid=$!
+
+while true; do ip netns exec $nsr nft list ruleset >/dev/null 2>&1; done &
+nft_list_pid=$!
+
+ip netns exec $nsr nft monitor >/dev/null &
+nft_monitor_pid=$!
+
+ip netns exec $nss iperf3 --server --daemon -1
+summary_expr='s,^\[SUM\] .* \([0-9]\+\) Mbits/sec .* receiver,\1,p'
+rate=$(ip netns exec $nsc iperf3 \
+ --format m -c 10.1.0.1 --time $TEST_RUNTIME \
+ --length 56 --parallel 10 -i 0 | sed -n "$summary_expr")
+
+kill $nft_list_pid
+kill $nft_monitor_pid
+kill $rename_loop_pid
+wait
+
+ip netns exec $nsr nft -f - <<EOF
+table ip t {
+ flowtable ft_wild {
+ hook ingress priority 0
+ devices = { wild* }
+ }
+}
+EOF
+if [[ $? -ne 0 ]]; then
+ echo "SKIP wildcard tests: not supported by host's nft?"
+else
+ for ((i = 0; i < 100; i++)); do
+ ip -net $nsr link add wild$i type dummy &
+ done
+ wait
+ for ((i = 80; i < 100; i++)); do
+ ip -net $nsr link del wild$i &
+ done
+ for ((i = 0; i < 80; i++)); do
+ ip -net $nsr link del wild$i &
+ done
+ wait
+ for ((i = 0; i < 100; i += 10)); do
+ (
+ for ((j = 0; j < 10; j++)); do
+ ip -net $nsr link add wild$((i + j)) type dummy
+ done
+ for ((j = 0; j < 10; j++)); do
+ ip -net $nsr link del wild$((i + j))
+ done
+ ) &
+ done
+ wait
+fi
+
+[[ $(</proc/sys/kernel/tainted) -eq 0 ]] || {
+ echo "FAIL: Kernel is tainted!"
+ exit $ksft_fail
+}
+
+[[ $rate -gt 0 ]] || {
+ echo "FAIL: Zero throughput in iperf3"
+ exit $ksft_fail
+}
+
+[[ -f /sys/kernel/debug/kmemleak && \
+ -n $(</sys/kernel/debug/kmemleak) ]] && {
+ echo "FAIL: non-empty kmemleak report"
+ exit $ksft_fail
+}
+
+exit $ksft_pass
--
2.43.0
^ permalink raw reply related [flat|nested] 24+ messages in thread