netfilter: nf_tables: Support wildcard netdev hook specs
authorPhil Sutter <phil@nwl.cc>
Wed, 21 May 2025 20:44:32 +0000 (22:44 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 23 May 2025 11:57:14 +0000 (13:57 +0200)
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>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_tables_api.c
net/netfilter/nft_chain_filter.c

index fabc82c988715a9370909163f861d8aef6885682..a7240736f98e6a2b39d5c23cfbcbdc36d9547fe4 100644 (file)
@@ -2330,24 +2330,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_free;
-       }
+       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_free;
+               ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
+               if (!ops) {
+                       err = -ENOMEM;
+                       goto err_hook_free;
+               }
+               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_free:
-       kfree(hook);
+       nft_netdev_hook_free(hook);
        return ERR_PTR(err);
 }
 
@@ -2357,7 +2355,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;
        }
 
@@ -9696,7 +9695,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:
index b59f8be6370e266ecb8f54a4b4478d673787b30f..b16185e9a6dd7211400e36e7ec34aefcf2c7b9ce 100644 (file)
@@ -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: