* [nf-next PATCH v7 1/6] netfilter: nf_tables: Flowtable hook's pf value never varies
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 2/6] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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.
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 c4af283356e7..1357238bb64d 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8851,7 +8851,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;
@@ -8860,12 +8860,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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [nf-next PATCH v7 2/6] netfilter: nf_tables: Store user-defined hook ifname
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 1/6] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 3/6] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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 4afa64c81304..d8fb1d0d6c8a 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 1357238bb64d..fdb14b357f71 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2276,7 +2276,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;
@@ -2286,12 +2285,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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [nf-next PATCH v7 3/6] netfilter: nf_tables: Use stored ifname in netdev hook dumps
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 1/6] netfilter: nf_tables: Flowtable hook's pf value never varies Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 2/6] netfilter: nf_tables: Store user-defined hook ifname Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 4/6] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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 fdb14b357f71..900b6c7d5fd6 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1956,15 +1956,16 @@ static int nft_dump_basechain_hook(struct sk_buff *skb,
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);
@@ -9280,7 +9281,8 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
list_for_each_entry_rcu(hook, hook_list, list,
lockdep_commit_lock_is_held(net)) {
- 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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [nf-next PATCH v7 4/6] netfilter: nf_tables: Compare netdev hooks based on stored name
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
` (2 preceding siblings ...)
2025-01-09 17:31 ` [nf-next PATCH v7 3/6] netfilter: nf_tables: Use stored ifname in netdev hook dumps Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 5/6] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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 900b6c7d5fd6..9a14f0e542f3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2317,7 +2317,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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [nf-next PATCH v7 5/6] netfilter: nf_tables: Tolerate chains with no remaining hooks
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
` (3 preceding siblings ...)
2025-01-09 17:31 ` [nf-next PATCH v7 4/6] netfilter: nf_tables: Compare netdev hooks based on stored name Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-09 17:31 ` [nf-next PATCH v7 6/6] netfilter: nf_tables: Simplify chain netdev notifier Phil Sutter
2025-01-15 16:22 ` [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Pablo Neira Ayuso
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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 to
restore it 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 | 41 -------------------------------
net/netfilter/nft_chain_filter.c | 29 ++++++----------------
3 files changed, 7 insertions(+), 65 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index d8fb1d0d6c8a..ffa5068e1a3d 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 9a14f0e542f3..30efde5417b7 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -11697,47 +11697,6 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
}
EXPORT_SYMBOL_GPL(nft_data_dump);
-static void __nft_release_basechain_now(struct nft_ctx *ctx)
-{
- struct nft_rule *rule, *nr;
-
- list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
- list_del(&rule->list);
- nf_tables_rule_release(ctx, rule);
- }
- nf_tables_chain_destroy(ctx->chain);
-}
-
-int __nft_release_basechain(struct nft_ctx *ctx)
-{
- struct nft_rule *rule;
-
- if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain)))
- return 0;
-
- nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
- list_for_each_entry(rule, &ctx->chain->rules, list)
- nft_use_dec(&ctx->chain->use);
-
- nft_chain_del(ctx->chain);
- nft_use_dec(&ctx->table->use);
-
- if (!maybe_get_net(ctx->net)) {
- __nft_release_basechain_now(ctx);
- return 0;
- }
-
- /* wait for ruleset dumps to complete. Owning chain is no longer in
- * lists, so new dumps can't find any of these rules anymore.
- */
- synchronize_rcu();
-
- __nft_release_basechain_now(ctx);
- put_net(ctx->net);
- 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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [nf-next PATCH v7 6/6] netfilter: nf_tables: Simplify chain netdev notifier
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
` (4 preceding siblings ...)
2025-01-09 17:31 ` [nf-next PATCH v7 5/6] netfilter: nf_tables: Tolerate chains with no remaining hooks Phil Sutter
@ 2025-01-09 17:31 ` Phil Sutter
2025-01-15 16:22 ` [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Pablo Neira Ayuso
6 siblings, 0 replies; 8+ messages in thread
From: Phil Sutter @ 2025-01-09 17:31 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>
---
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.47.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1
2025-01-09 17:31 [nf-next PATCH v7 0/6] Dynamic hook interface binding part 1 Phil Sutter
` (5 preceding siblings ...)
2025-01-09 17:31 ` [nf-next PATCH v7 6/6] netfilter: nf_tables: Simplify chain netdev notifier Phil Sutter
@ 2025-01-15 16:22 ` Pablo Neira Ayuso
6 siblings, 0 replies; 8+ messages in thread
From: Pablo Neira Ayuso @ 2025-01-15 16:22 UTC (permalink / raw)
To: Phil Sutter; +Cc: netfilter-devel, Florian Westphal, Eric Garver
On Thu, Jan 09, 2025 at 06:31:31PM +0100, Phil Sutter wrote:
> Changes since v6:
> - Rebase onto "netfilter: nf_tables: imbalance in flowtable binding"
> patch which is in nf-25-01-09 tag but missing in nf-next
> - Drop patch 7 which removed __nft_unregister_flowtable_net_hooks(): The
> function is no longer a duplicate of nft_netdev_unregister_hooks()
>
> This series makes netdev hooks store the interface name spec they were
> created for and establishes this stored name as the key identifier. The
> previous one which is the hook's 'ops.dev' pointer is thereby freed to
> vanish, so a vanishing netdev no longer has to drag the hook along with
> it. (Patches 2-4)
>
> Furthermore, it aligns behaviour of netdev-family chains with that of
> flowtables in situations of vanishing interfaces. When previously a
> chain losing its last interface was torn down and deleted, it may now
> remain in place (albeit with no remaining interfaces). (Patch 5)
>
> Patch 6 is a cleanup following patch 5, patch 1 is an independent
> code simplification.
Series applied to nf-next, thanks Phil.
^ permalink raw reply [flat|nested] 8+ messages in thread