[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250523132712.458507-23-pablo@netfilter.org>
Date: Fri, 23 May 2025 15:27:08 +0200
From: Pablo Neira Ayuso <pablo@...filter.org>
To: netfilter-devel@...r.kernel.org
Cc: davem@...emloft.net,
netdev@...r.kernel.org,
kuba@...nel.org,
pabeni@...hat.com,
edumazet@...gle.com,
fw@...len.de,
horms@...nel.org
Subject: [PATCH net-next 22/26] netfilter: nf_tables: Handle NETDEV_CHANGENAME events
From: Phil Sutter <phil@....cc>
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@....cc>
Signed-off-by: Pablo Neira Ayuso <pablo@...filter.org>
---
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 713ea0e48772..452f8a42d5e6 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9690,16 +9690,20 @@ struct nf_hook_ops *nft_hook_find_ops_rcu(const struct nft_hook *hook,
EXPORT_SYMBOL_GPL(nft_hook_find_ops_rcu);
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. */
@@ -9709,7 +9713,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),
@@ -9737,7 +9742,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;
@@ -9745,7 +9751,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;
}
}
@@ -9761,16 +9768,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 7795dff13408..b59f8be6370e 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))
@@ -339,7 +343,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,
@@ -363,7 +368,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;
@@ -385,7 +392,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;
}
}
@@ -400,15 +407,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.30.2
Powered by blists - more mailing lists