* [nf-next PATCH v5 00/18] Dynamic hook interface binding
@ 2024-09-26 9:56 Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 01/18] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
` (18 more replies)
0 siblings, 19 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Changes since v4:
- Extend netlink notifications to clarify confusing DELDEV, NEWDEV
messages with identical interface name for CHANGENAME events. Include
the interface name spec, so user space may log e.g.:
| add device flowtable ip t f2 hook f2_i* { f2_if1 }
| delete device flowtable ip t f1 hook f1_if2 { f2_if1 }
(upon renaming f1_if2 to f2_if1)
- Add missing annotation to new NFTA_DEVICE_* attributes.
- Fix for NETDEV_CHANGENAME event unregistering the newly registered
hook again.
- Drop extras from chain's netdev notifier needed for chain deletion
support.
- Limit max run-time of kselftest (build system set
kselftest_timeout=1800, leading to 24min run-time).
Patch 1 eliminates a pointless check and allows for some code
consolidation.
The next three patches introduce external storing of the user-supplied
interface name in nft_hook structs to decouple code from values in
->ops.dev or ->ops value in general.
Patch 5 eliminates a quirk in netdev-family chain netdev event handler,
aligns behaviour with flowtables and paves the way for following
changes. Patch 6 cleans up remnants afterwards.
Patches 7-11 prepare for and implement nf_hook_ops lists in nft_hook
objects. This is crucial for wildcard interface specs and convenient
with dynamic netdev hook registration upon NETDEV_REGISTER events.
Patches 12-15 leverage the new infrastructure to correctly handle
NETDEV_REGISTER and NETDEV_CHANGENAME events.
Patch 16 prepares the code for non-NUL-terminated interface names passed
by user space which resemble prefixes to match on. As a side-effect,
hook allocation code becomes tolerant to non-matching interface specs.
The final two patches implement netlink notifications for netdev
add/remove events and add a kselftest.
Phil Sutter (18):
netfilter: nf_tables: Flowtable hook's pf value never varies
netfilter: nf_tables: Store user-defined hook ifname
netfilter: nf_tables: Use stored ifname in netdev hook dumps
netfilter: nf_tables: Compare netdev hooks based on stored name
netfilter: nf_tables: Tolerate chains with no remaining hooks
netfilter: nf_tables: Simplify chain netdev notifier
netfilter: nf_tables: Introduce functions freeing nft_hook objects
netfilter: nf_tables: Introduce nft_hook_find_ops()
netfilter: nf_tables: Introduce nft_register_flowtable_ops()
netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks()
netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook
netfilter: nf_tables: chain: Respect NETDEV_REGISTER events
netfilter: nf_tables: flowtable: Respect NETDEV_REGISTER events
netfilter: nf_tables: Wrap netdev notifiers
netfilter: nf_tables: Handle NETDEV_CHANGENAME events
netfilter: nf_tables: Support wildcard netdev hook specs
netfilter: nf_tables: Add notications for hook changes
selftests: netfilter: Torture nftables netdev hooks
include/linux/netfilter.h | 3 +
include/net/netfilter/nf_tables.h | 14 +-
include/uapi/linux/netfilter/nf_tables.h | 10 +
net/netfilter/nf_tables_api.c | 447 ++++++++++++------
net/netfilter/nf_tables_offload.c | 51 +-
net/netfilter/nft_chain_filter.c | 122 +++--
net/netfilter/nft_flow_offload.c | 2 +-
.../testing/selftests/net/netfilter/Makefile | 1 +
.../net/netfilter/nft_interface_stress.sh | 151 ++++++
9 files changed, 593 insertions(+), 208 deletions(-)
create mode 100755 tools/testing/selftests/net/netfilter/nft_interface_stress.sh
--
2.43.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 01/18] netfilter: nf_tables: Flowtable hook's pf value never varies
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 02/18] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
` (17 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 02/18] netfilter: nf_tables: Store user-defined hook ifname
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 01/18] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 03/18] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
` (16 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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>
---
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] 22+ messages in thread
* [nf-next PATCH v5 03/18] netfilter: nf_tables: Use stored ifname in netdev hook dumps
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 01/18] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 02/18] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 04/18] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
` (15 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 04/18] netfilter: nf_tables: Compare netdev hooks based on stored name
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (2 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 03/18] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 05/18] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
` (14 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 05/18] netfilter: nf_tables: Tolerate chains with no remaining hooks
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (3 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 04/18] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 06/18] netfilter: nf_tables: Simplify chain netdev notifier Phil Sutter
` (13 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 06/18] netfilter: nf_tables: Simplify chain netdev notifier
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (4 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 05/18] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 07/18] netfilter: nf_tables: Introduce functions freeing nft_hook objects Phil Sutter
` (12 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
With conditional chain deletion gone, callback code simplifies: Instead
of filling an nft_ctx object, just pass basechain to the per-chain
function. Also plain list_for_each_entry() is safe now.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v4:
- New patch.
---
net/netfilter/nft_chain_filter.c | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 543f258b7c6b..19a553550c76 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -319,17 +319,16 @@ 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)
+ struct nft_base_chain *basechain)
{
- struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
struct nft_hook *hook;
list_for_each_entry(hook, &basechain->hook_list, list) {
if (hook->ops.dev != dev)
continue;
- if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(ctx->net, &hook->ops);
+ if (!(basechain->chain.table->flags & NFT_TABLE_F_DORMANT))
+ nf_unregister_net_hook(dev_net(dev), &hook->ops);
list_del_rcu(&hook->list);
kfree_rcu(hook, rcu);
@@ -343,25 +342,20 @@ static int nf_tables_netdev_event(struct notifier_block *this,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_base_chain *basechain;
struct nftables_pernet *nft_net;
- struct nft_chain *chain, *nr;
+ struct nft_chain *chain;
struct nft_table *table;
- struct nft_ctx ctx = {
- .net = dev_net(dev),
- };
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
- nft_net = nft_pernet(ctx.net);
+ nft_net = nft_pernet(dev_net(dev));
mutex_lock(&nft_net->commit_mutex);
list_for_each_entry(table, &nft_net->tables, list) {
if (table->family != NFPROTO_NETDEV &&
table->family != NFPROTO_INET)
continue;
- ctx.family = table->family;
- ctx.table = table;
- list_for_each_entry_safe(chain, nr, &table->chains, list) {
+ list_for_each_entry(chain, &table->chains, list) {
if (!nft_is_base_chain(chain))
continue;
@@ -370,8 +364,7 @@ static int nf_tables_netdev_event(struct notifier_block *this,
basechain->ops.hooknum != NF_INET_INGRESS)
continue;
- ctx.chain = chain;
- nft_netdev_event(event, dev, &ctx);
+ nft_netdev_event(event, dev, basechain);
}
}
mutex_unlock(&nft_net->commit_mutex);
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 07/18] netfilter: nf_tables: Introduce functions freeing nft_hook objects
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (5 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 06/18] netfilter: nf_tables: Simplify chain netdev notifier Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 08/18] netfilter: nf_tables: Introduce nft_hook_find_ops() Phil Sutter
` (11 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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>
---
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] 22+ messages in thread
* [nf-next PATCH v5 08/18] netfilter: nf_tables: Introduce nft_hook_find_ops()
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (6 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 07/18] netfilter: nf_tables: Introduce functions freeing nft_hook objects Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 09/18] netfilter: nf_tables: Introduce nft_register_flowtable_ops() Phil Sutter
` (10 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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 19a553550c76..783e4b5ef3e0 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -321,14 +321,16 @@ static const struct nft_chain_type nft_chain_filter_netdev = {
static void nft_netdev_event(unsigned long event, struct net_device *dev,
struct nft_base_chain *basechain)
{
+ 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 (!(basechain->chain.table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(dev_net(dev), &hook->ops);
+ nf_unregister_net_hook(dev_net(dev), 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] 22+ messages in thread
* [nf-next PATCH v5 09/18] netfilter: nf_tables: Introduce nft_register_flowtable_ops()
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (7 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 08/18] netfilter: nf_tables: Introduce nft_hook_find_ops() Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 10/18] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks() Phil Sutter
` (9 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 10/18] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks()
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (8 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 09/18] netfilter: nf_tables: Introduce nft_register_flowtable_ops() Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 11/18] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook Phil Sutter
` (8 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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] 22+ messages in thread
* [nf-next PATCH v5 11/18] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (9 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 10/18] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks() Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 12/18] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events Phil Sutter
` (7 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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>
---
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 | 5 +-
5 files changed, 138 insertions(+), 70 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 783e4b5ef3e0..bac5aa8970a4 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -332,9 +332,8 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
if (!(basechain->chain.table->flags & NFT_TABLE_F_DORMANT))
nf_unregister_net_hook(dev_net(dev), 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] 22+ messages in thread
* [nf-next PATCH v5 12/18] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (10 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 11/18] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 13/18] netfilter: nf_tables: flowtable: " Phil Sutter
` (6 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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 v4:
- Introduce table pointer variable to reduce max line length.
---
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 bac5aa8970a4..b1aa2d469776 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_base_chain *basechain)
+static int nft_netdev_event(unsigned long event, struct net_device *dev,
+ struct nft_base_chain *basechain)
{
+ struct nft_table *table = basechain->chain.table;
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 (!(basechain->chain.table->flags & NFT_TABLE_F_DORMANT))
- nf_unregister_net_hook(dev_net(dev), ops);
+ if (!(table->flags & NFT_TABLE_F_DORMANT))
+ nf_unregister_net_hook(dev_net(dev), ops);
+ list_del_rcu(&ops->list);
+ kfree_rcu(ops, rcu);
+ break;
+ case NETDEV_REGISTER:
+ if (strcmp(hook->ifname, dev->name))
+ continue;
- 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 (!(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,
@@ -346,7 +370,8 @@ static int nf_tables_netdev_event(struct notifier_block *this,
struct nft_chain *chain;
struct nft_table *table;
- if (event != NETDEV_UNREGISTER)
+ if (event != NETDEV_REGISTER &&
+ event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
nft_net = nft_pernet(dev_net(dev));
@@ -365,7 +390,10 @@ static int nf_tables_netdev_event(struct notifier_block *this,
basechain->ops.hooknum != NF_INET_INGRESS)
continue;
- nft_netdev_event(event, dev, basechain);
+ if (nft_netdev_event(event, dev, basechain)) {
+ mutex_unlock(&nft_net->commit_mutex);
+ return NOTIFY_BAD;
+ }
}
}
mutex_unlock(&nft_net->commit_mutex);
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 13/18] netfilter: nf_tables: flowtable: Respect NETDEV_REGISTER events
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (11 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 12/18] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 14/18] netfilter: nf_tables: Wrap netdev notifiers Phil Sutter
` (5 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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>
---
net/netfilter/nf_tables_api.c | 57 +++++++++++++++++++++++++++--------
1 file changed, 44 insertions(+), 13 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index f3b0bc2fe0e3..74df6048a892 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9317,23 +9317,50 @@ 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 +9372,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] 22+ messages in thread
* [nf-next PATCH v5 14/18] netfilter: nf_tables: Wrap netdev notifiers
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (12 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 13/18] netfilter: nf_tables: flowtable: " Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 15/18] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
` (4 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Eric Garver
Handling NETDEV_CHANGENAME events has to traverse all chains/flowtables
twice, prepare for this. No functional change intended.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v4:
- New patch.
---
net/netfilter/nf_tables_api.c | 34 ++++++++++++++++++----------
net/netfilter/nft_chain_filter.c | 38 ++++++++++++++++++++------------
2 files changed, 46 insertions(+), 26 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 74df6048a892..77d0efbad641 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9363,13 +9363,28 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev,
return 0;
}
+static int __nf_tables_flowtable_event(unsigned long event,
+ struct net_device *dev)
+{
+ struct nftables_pernet *nft_net = nft_pernet(dev_net(dev));
+ struct nft_flowtable *flowtable;
+ struct nft_table *table;
+
+ list_for_each_entry(table, &nft_net->tables, list) {
+ list_for_each_entry(flowtable, &table->flowtables, list) {
+ if (nft_flowtable_event(event, dev, flowtable))
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int nf_tables_flowtable_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct nft_flowtable *flowtable;
struct nftables_pernet *nft_net;
- struct nft_table *table;
+ int ret = NOTIFY_DONE;
struct net *net;
if (event != NETDEV_REGISTER &&
@@ -9379,17 +9394,12 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
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) {
- if (nft_flowtable_event(event, dev, flowtable)) {
- mutex_unlock(&nft_net->commit_mutex);
- return NOTIFY_BAD;
- }
- }
- }
- mutex_unlock(&nft_net->commit_mutex);
- return NOTIFY_DONE;
+ if (__nf_tables_flowtable_event(event, dev))
+ ret = NOTIFY_BAD;
+
+ mutex_unlock(&nft_net->commit_mutex);
+ return ret;
}
static struct notifier_block nf_tables_flowtable_notifier = {
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index b1aa2d469776..073bafdf56c0 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -361,21 +361,14 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev,
return 0;
}
-static int nf_tables_netdev_event(struct notifier_block *this,
- unsigned long event, void *ptr)
+static int __nf_tables_netdev_event(unsigned long event, struct net_device *dev)
{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_base_chain *basechain;
struct nftables_pernet *nft_net;
struct nft_chain *chain;
struct nft_table *table;
- if (event != NETDEV_REGISTER &&
- event != NETDEV_UNREGISTER)
- return NOTIFY_DONE;
-
nft_net = nft_pernet(dev_net(dev));
- mutex_lock(&nft_net->commit_mutex);
list_for_each_entry(table, &nft_net->tables, list) {
if (table->family != NFPROTO_NETDEV &&
table->family != NFPROTO_INET)
@@ -390,15 +383,32 @@ static int nf_tables_netdev_event(struct notifier_block *this,
basechain->ops.hooknum != NF_INET_INGRESS)
continue;
- if (nft_netdev_event(event, dev, basechain)) {
- mutex_unlock(&nft_net->commit_mutex);
- return NOTIFY_BAD;
- }
+ if (nft_netdev_event(event, dev, basechain))
+ return 1;
}
}
- mutex_unlock(&nft_net->commit_mutex);
+ return 0;
+}
+
+static int nf_tables_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct nftables_pernet *nft_net;
+ int ret = NOTIFY_DONE;
+
+ if (event != NETDEV_REGISTER &&
+ event != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
- return NOTIFY_DONE;
+ nft_net = nft_pernet(dev_net(dev));
+ mutex_lock(&nft_net->commit_mutex);
+
+ if (__nf_tables_netdev_event(event, dev))
+ ret = NOTIFY_BAD;
+
+ mutex_unlock(&nft_net->commit_mutex);
+ return ret;
}
static struct notifier_block nf_tables_netdev_notifier = {
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 15/18] netfilter: nf_tables: Handle NETDEV_CHANGENAME events
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (13 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 14/18] netfilter: nf_tables: Wrap netdev notifiers Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 16/18] netfilter: nf_tables: Support wildcard netdev hook specs Phil Sutter
` (3 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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.
To avoid unregistering the newly registered hook again during the
following fake NETDEV_UNREGISTER event, leave hooks alone if their
interface spec matches the new name.
Note how this patch also skips for NETDEV_REGISTER if the device is
already registered. This is not yet possible as the new name would have
to match the old one. This will change with wildcard interface specs,
though.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v4:
- Avoid unregistering the new ops along with the old one(s) by accident.
---
net/netfilter/nf_tables_api.c | 33 +++++++++++++++++++++++---------
net/netfilter/nft_chain_filter.c | 33 +++++++++++++++++++++++---------
2 files changed, 48 insertions(+), 18 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 77d0efbad641..50221d4d747b 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9318,16 +9318,20 @@ struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
- struct nft_flowtable *flowtable)
+ struct nft_flowtable *flowtable, bool changename)
{
struct nf_hook_ops *ops;
struct nft_hook *hook;
+ bool match;
list_for_each_entry(hook, &flowtable->hook_list, list) {
+ ops = nft_hook_find_ops(hook, dev);
+ match = !strcmp(hook->ifname, dev->name);
+
switch (event) {
case NETDEV_UNREGISTER:
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
+ /* NOP if not found or new name still matching */
+ if (!ops || (changename && match))
continue;
/* flow_offload_netdev_event() cleans up entries for us. */
@@ -9337,7 +9341,8 @@ 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))
+ /* NOP if not matching or already registered */
+ if (!match || (changename && ops))
continue;
ops = kzalloc(sizeof(struct nf_hook_ops),
@@ -9364,7 +9369,8 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev,
}
static int __nf_tables_flowtable_event(unsigned long event,
- struct net_device *dev)
+ struct net_device *dev,
+ bool changename)
{
struct nftables_pernet *nft_net = nft_pernet(dev_net(dev));
struct nft_flowtable *flowtable;
@@ -9372,7 +9378,8 @@ static int __nf_tables_flowtable_event(unsigned long event,
list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) {
- if (nft_flowtable_event(event, dev, flowtable))
+ if (nft_flowtable_event(event, dev,
+ flowtable, changename))
return 1;
}
}
@@ -9388,16 +9395,24 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
struct net *net;
if (event != NETDEV_REGISTER &&
- event != NETDEV_UNREGISTER)
+ event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
return NOTIFY_DONE;
net = dev_net(dev);
nft_net = nft_pernet(net);
mutex_lock(&nft_net->commit_mutex);
- if (__nf_tables_flowtable_event(event, dev))
+ if (event == NETDEV_CHANGENAME) {
+ if (__nf_tables_flowtable_event(NETDEV_REGISTER, dev, true)) {
+ ret = NOTIFY_BAD;
+ goto out_unlock;
+ }
+ __nf_tables_flowtable_event(NETDEV_UNREGISTER, dev, true);
+ } else if (__nf_tables_flowtable_event(event, dev, false)) {
ret = NOTIFY_BAD;
-
+ }
+out_unlock:
mutex_unlock(&nft_net->commit_mutex);
return ret;
}
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 073bafdf56c0..ea07460d2bef 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -319,17 +319,21 @@ static const struct nft_chain_type nft_chain_filter_netdev = {
};
static int nft_netdev_event(unsigned long event, struct net_device *dev,
- struct nft_base_chain *basechain)
+ struct nft_base_chain *basechain, bool changename)
{
struct nft_table *table = basechain->chain.table;
struct nf_hook_ops *ops;
struct nft_hook *hook;
+ bool match;
list_for_each_entry(hook, &basechain->hook_list, list) {
+ ops = nft_hook_find_ops(hook, dev);
+ match = !strcmp(hook->ifname, dev->name);
+
switch (event) {
case NETDEV_UNREGISTER:
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
+ /* NOP if not found or new name still matching */
+ if (!ops || (changename && match))
continue;
if (!(table->flags & NFT_TABLE_F_DORMANT))
@@ -338,7 +342,8 @@ 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))
+ /* NOP if not matching or already registered */
+ if (!match || (changename && ops))
continue;
ops = kmemdup(&basechain->ops,
@@ -361,7 +366,9 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev,
return 0;
}
-static int __nf_tables_netdev_event(unsigned long event, struct net_device *dev)
+static int __nf_tables_netdev_event(unsigned long event,
+ struct net_device *dev,
+ bool changename)
{
struct nft_base_chain *basechain;
struct nftables_pernet *nft_net;
@@ -383,7 +390,7 @@ static int __nf_tables_netdev_event(unsigned long event, struct net_device *dev)
basechain->ops.hooknum != NF_INET_INGRESS)
continue;
- if (nft_netdev_event(event, dev, basechain))
+ if (nft_netdev_event(event, dev, basechain, changename))
return 1;
}
}
@@ -398,15 +405,23 @@ static int nf_tables_netdev_event(struct notifier_block *this,
int ret = NOTIFY_DONE;
if (event != NETDEV_REGISTER &&
- event != NETDEV_UNREGISTER)
+ event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
return NOTIFY_DONE;
nft_net = nft_pernet(dev_net(dev));
mutex_lock(&nft_net->commit_mutex);
- if (__nf_tables_netdev_event(event, dev))
+ if (event == NETDEV_CHANGENAME) {
+ if (__nf_tables_netdev_event(NETDEV_REGISTER, dev, true)) {
+ ret = NOTIFY_BAD;
+ goto out_unlock;
+ }
+ __nf_tables_netdev_event(NETDEV_UNREGISTER, dev, true);
+ } else if (__nf_tables_netdev_event(event, dev, false)) {
ret = NOTIFY_BAD;
-
+ }
+out_unlock:
mutex_unlock(&nft_net->commit_mutex);
return ret;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 16/18] netfilter: nf_tables: Support wildcard netdev hook specs
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (14 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 15/18] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 17/18] netfilter: nf_tables: Add notications for hook changes Phil Sutter
` (2 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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 50221d4d747b..d5ce3ddeee26 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;
}
@@ -9326,7 +9325,7 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev,
list_for_each_entry(hook, &flowtable->hook_list, list) {
ops = nft_hook_find_ops(hook, dev);
- match = !strcmp(hook->ifname, dev->name);
+ match = !strncmp(hook->ifname, dev->name, hook->ifnamelen);
switch (event) {
case NETDEV_UNREGISTER:
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index ea07460d2bef..6a69f73126eb 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -328,7 +328,7 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev,
list_for_each_entry(hook, &basechain->hook_list, list) {
ops = nft_hook_find_ops(hook, dev);
- match = !strcmp(hook->ifname, dev->name);
+ match = !strncmp(hook->ifname, dev->name, hook->ifnamelen);
switch (event) {
case NETDEV_UNREGISTER:
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 17/18] netfilter: nf_tables: Add notications for hook changes
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (15 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 16/18] netfilter: nf_tables: Support wildcard netdev hook specs Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 18/18] selftests: netfilter: Torture nftables netdev hooks Phil Sutter
2024-10-21 13:05 ` [nf-next PATCH v5 00/18] Dynamic hook interface binding Florian Westphal
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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.
Upon NETDEV_CHANGENAME, the callback has no information about the
interface's old name. To provide a clear message to user space, include
the hook's stored interface name in the notification.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v4:
- Introduce NFTA_DEVICE_SPEC to contain the hook's stored ifname
- Describe new attributes in top comment
---
include/net/netfilter/nf_tables.h | 5 ++
include/uapi/linux/netfilter/nf_tables.h | 10 ++++
net/netfilter/nf_tables_api.c | 59 ++++++++++++++++++++++++
net/netfilter/nft_chain_filter.c | 2 +
4 files changed, 76 insertions(+)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index eaf2f5184bdf..884ecf030323 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1133,6 +1133,11 @@ 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);
+struct nft_hook;
+void nf_tables_chain_device_notify(const struct nft_chain *chain,
+ const struct nft_hook *hook,
+ const struct net_device *dev, int event);
+
enum nft_chain_types {
NFT_CHAIN_T_DEFAULT = 0,
NFT_CHAIN_T_ROUTE,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 9e9079321380..cb92375b77b0 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,
};
@@ -1768,10 +1770,18 @@ enum nft_synproxy_attributes {
* enum nft_device_attributes - nf_tables device netlink attributes
*
* @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
+ * @NFTA_DEVICE_TABLE: table containing the flowtable or chain hooking into the device (NLA_STRING)
+ * @NFTA_DEVICE_FLOWTABLE: flowtable hooking into the device (NLA_STRING)
+ * @NFTA_DEVICE_CHAIN: chain hooking into the device (NLA_STRING)
+ * @NFTA_DEVICE_SPEC: hook spec matching the device (NLA_STRING)
*/
enum nft_devices_attributes {
NFTA_DEVICE_UNSPEC,
NFTA_DEVICE_NAME,
+ NFTA_DEVICE_TABLE,
+ NFTA_DEVICE_FLOWTABLE,
+ NFTA_DEVICE_CHAIN,
+ NFTA_DEVICE_SPEC,
__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 d5ce3ddeee26..15a8b7eee3ff 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9316,6 +9316,64 @@ 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 nft_hook *hook,
+ 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(skb, NFTA_DEVICE_SPEC, hook->ifnamelen, hook->ifname) ||
+ 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 nft_hook *hook,
+ const struct net_device *dev, int event)
+{
+ nf_tables_device_notify(chain->table, NFTA_DEVICE_CHAIN,
+ chain->name, hook, dev, event);
+}
+
+static void
+nf_tables_flowtable_device_notify(const struct nft_flowtable *ft,
+ const struct nft_hook *hook,
+ const struct net_device *dev, int event)
+{
+ nf_tables_device_notify(ft->table, NFTA_DEVICE_FLOWTABLE,
+ ft->name, hook, dev, event);
+}
+
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
struct nft_flowtable *flowtable, bool changename)
{
@@ -9363,6 +9421,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, hook, dev, event);
}
return 0;
}
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index 6a69f73126eb..7ca079a26e0f 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -362,6 +362,8 @@ 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(&basechain->chain,
+ hook, dev, event);
}
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [nf-next PATCH v5 18/18] selftests: netfilter: Torture nftables netdev hooks
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (16 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 17/18] netfilter: nf_tables: Add notications for hook changes Phil Sutter
@ 2024-09-26 9:56 ` Phil Sutter
2024-10-21 13:05 ` [nf-next PATCH v5 00/18] Dynamic hook interface binding Florian Westphal
18 siblings, 0 replies; 22+ messages in thread
From: Phil Sutter @ 2024-09-26 9:56 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>
---
Changes since v4:
- Limit maximum run-time to 48s.
---
.../testing/selftests/net/netfilter/Makefile | 1 +
.../net/netfilter/nft_interface_stress.sh | 151 ++++++++++++++++++
2 files changed, 152 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..27ef082a09a7
--- /dev/null
+++ b/tools/testing/selftests/net/netfilter/nft_interface_stress.sh
@@ -0,0 +1,151 @@
+#!/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 but don't exceed 48s
+TEST_RUNTIME=$((${kselftest_timeout:-60} * 8 / 10))
+[[ $TEST_RUNTIME -gt 48 ]] && TEST_RUNTIME=48
+
+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] 22+ messages in thread
* Re: [nf-next PATCH v5 00/18] Dynamic hook interface binding
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
` (17 preceding siblings ...)
2024-09-26 9:56 ` [nf-next PATCH v5 18/18] selftests: netfilter: Torture nftables netdev hooks Phil Sutter
@ 2024-10-21 13:05 ` Florian Westphal
2024-10-22 11:22 ` Phil Sutter
18 siblings, 1 reply; 22+ messages in thread
From: Florian Westphal @ 2024-10-21 13:05 UTC (permalink / raw)
To: Phil Sutter
Cc: Pablo Neira Ayuso, netfilter-devel, Florian Westphal, Eric Garver
Phil Sutter <phil@nwl.cc> wrote:
I started to review this, I would suggest to apply the first 10 patches
for the next net-next PR so that its exposed to wider audience.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [nf-next PATCH v5 00/18] Dynamic hook interface binding
2024-10-21 13:05 ` [nf-next PATCH v5 00/18] Dynamic hook interface binding Florian Westphal
@ 2024-10-22 11:22 ` Phil Sutter
2024-10-22 13:00 ` Pablo Neira Ayuso
0 siblings, 1 reply; 22+ messages in thread
From: Phil Sutter @ 2024-10-22 11:22 UTC (permalink / raw)
To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel, Eric Garver
Hi Florian,
On Mon, Oct 21, 2024 at 03:05:44PM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
>
> I started to review this, I would suggest to apply the first 10 patches
> for the next net-next PR so that its exposed to wider audience.
Maybe worth noting that patches 7, 8 and 9 are rather pointless if not
followed up by the remaining ones. Patch 10 OTOH may apply to HEAD by
itself.
Should I prepare a series with just patches 1-6 and 10 for nf-next?
Thanks, Phil
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [nf-next PATCH v5 00/18] Dynamic hook interface binding
2024-10-22 11:22 ` Phil Sutter
@ 2024-10-22 13:00 ` Pablo Neira Ayuso
0 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2024-10-22 13:00 UTC (permalink / raw)
To: Phil Sutter, Florian Westphal, netfilter-devel, Eric Garver
On Tue, Oct 22, 2024 at 01:22:03PM +0200, Phil Sutter wrote:
> Hi Florian,
>
> On Mon, Oct 21, 2024 at 03:05:44PM +0200, Florian Westphal wrote:
> > Phil Sutter <phil@nwl.cc> wrote:
> >
> > I started to review this, I would suggest to apply the first 10 patches
> > for the next net-next PR so that its exposed to wider audience.
>
> Maybe worth noting that patches 7, 8 and 9 are rather pointless if not
> followed up by the remaining ones. Patch 10 OTOH may apply to HEAD by
> itself.
>
> Should I prepare a series with just patches 1-6 and 10 for nf-next?
Please, post them so I have a chance to review this smaller batch.
Thanks
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2024-10-22 13:00 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-26 9:56 [nf-next PATCH v5 00/18] Dynamic hook interface binding Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 01/18] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 02/18] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 03/18] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 04/18] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 05/18] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 06/18] netfilter: nf_tables: Simplify chain netdev notifier Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 07/18] netfilter: nf_tables: Introduce functions freeing nft_hook objects Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 08/18] netfilter: nf_tables: Introduce nft_hook_find_ops() Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 09/18] netfilter: nf_tables: Introduce nft_register_flowtable_ops() Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 10/18] netfilter: nf_tables: Drop __nft_unregister_flowtable_net_hooks() Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 11/18] netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 12/18] netfilter: nf_tables: chain: Respect NETDEV_REGISTER events Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 13/18] netfilter: nf_tables: flowtable: " Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 14/18] netfilter: nf_tables: Wrap netdev notifiers Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 15/18] netfilter: nf_tables: Handle NETDEV_CHANGENAME events Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 16/18] netfilter: nf_tables: Support wildcard netdev hook specs Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 17/18] netfilter: nf_tables: Add notications for hook changes Phil Sutter
2024-09-26 9:56 ` [nf-next PATCH v5 18/18] selftests: netfilter: Torture nftables netdev hooks Phil Sutter
2024-10-21 13:05 ` [nf-next PATCH v5 00/18] Dynamic hook interface binding Florian Westphal
2024-10-22 11:22 ` Phil Sutter
2024-10-22 13:00 ` Pablo Neira Ayuso
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.