]> www.infradead.org Git - users/griffoul/linux.git/commitdiff
netfilter: nf_tables: Introduce NFTA_DEVICE_PREFIX
authorPhil Sutter <phil@nwl.cc>
Thu, 7 Aug 2025 13:49:59 +0000 (15:49 +0200)
committerFlorian Westphal <fw@strlen.de>
Thu, 4 Sep 2025 07:19:25 +0000 (09:19 +0200)
This new attribute is supposed to be used instead of NFTA_DEVICE_NAME
for simple wildcard interface specs. It holds a NUL-terminated string
representing an interface name prefix to match on.

While kernel code to distinguish full names from prefixes in
NFTA_DEVICE_NAME is simpler than this solution, reusing the existing
attribute with different semantics leads to confusion between different
versions of kernel and user space though:

* With old kernels, wildcards submitted by user space are accepted yet
  silently treated as regular names.
* With old user space, wildcards submitted by kernel may cause crashes
  since libnftnl expects NUL-termination when there is none.

Using a distinct attribute type sanitizes these situations as the
receiving part detects and rejects the unexpected attribute nested in
*_HOOK_DEVS attributes.

Fixes: 6d07a289504a ("netfilter: nf_tables: Support wildcard netdev hook specs")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Florian Westphal <fw@strlen.de>
include/uapi/linux/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c

index 2beb30be2c5f8e7452cb270daf3a3213abe4c2cf..8e0eb832bc01ec2541eba004a94a3741c538666e 100644 (file)
@@ -1784,10 +1784,12 @@ enum nft_synproxy_attributes {
  * enum nft_device_attributes - nf_tables device netlink attributes
  *
  * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
+ * @NFTA_DEVICE_PREFIX: device name prefix, a simple wildcard (NLA_STRING)
  */
 enum nft_devices_attributes {
        NFTA_DEVICE_UNSPEC,
        NFTA_DEVICE_NAME,
+       NFTA_DEVICE_PREFIX,
        __NFTA_DEVICE_MAX
 };
 #define NFTA_DEVICE_MAX                (__NFTA_DEVICE_MAX - 1)
index 58c5425d61c225f1bd49432ef897fc01ff8a4b3f..c1082de096567613dfef8bbf666b165161e7a5e9 100644 (file)
@@ -1959,6 +1959,18 @@ nla_put_failure:
        return -ENOSPC;
 }
 
+static bool hook_is_prefix(struct nft_hook *hook)
+{
+       return strlen(hook->ifname) >= hook->ifnamelen;
+}
+
+static int nft_nla_put_hook_dev(struct sk_buff *skb, struct nft_hook *hook)
+{
+       int attr = hook_is_prefix(hook) ? NFTA_DEVICE_PREFIX : NFTA_DEVICE_NAME;
+
+       return nla_put_string(skb, attr, hook->ifname);
+}
+
 static int nft_dump_basechain_hook(struct sk_buff *skb,
                                   const struct net *net, int family,
                                   const struct nft_base_chain *basechain,
@@ -1990,16 +2002,15 @@ static int nft_dump_basechain_hook(struct sk_buff *skb,
                        if (!first)
                                first = hook;
 
-                       if (nla_put(skb, NFTA_DEVICE_NAME,
-                                   hook->ifnamelen, hook->ifname))
+                       if (nft_nla_put_hook_dev(skb, hook))
                                goto nla_put_failure;
                        n++;
                }
                nla_nest_end(skb, nest_devs);
 
                if (n == 1 &&
-                   nla_put(skb, NFTA_HOOK_DEV,
-                           first->ifnamelen, first->ifname))
+                   !hook_is_prefix(first) &&
+                   nla_put_string(skb, NFTA_HOOK_DEV, first->ifname))
                        goto nla_put_failure;
        }
        nla_nest_end(skb, nest);
@@ -2310,7 +2321,8 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
 }
 
 static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
-                                             const struct nlattr *attr)
+                                             const struct nlattr *attr,
+                                             bool prefix)
 {
        struct nf_hook_ops *ops;
        struct net_device *dev;
@@ -2327,7 +2339,8 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
        if (err < 0)
                goto err_hook_free;
 
-       hook->ifnamelen = nla_len(attr);
+       /* include the terminating NUL-char when comparing non-prefixes */
+       hook->ifnamelen = strlen(hook->ifname) + !prefix;
 
        /* nf_tables_netdev_event() is called under rtnl_mutex, this is
         * indirectly serializing all the other holders of the commit_mutex with
@@ -2374,14 +2387,22 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
        struct nft_hook *hook, *next;
        const struct nlattr *tmp;
        int rem, n = 0, err;
+       bool prefix;
 
        nla_for_each_nested(tmp, attr, rem) {
-               if (nla_type(tmp) != NFTA_DEVICE_NAME) {
+               switch (nla_type(tmp)) {
+               case NFTA_DEVICE_NAME:
+                       prefix = false;
+                       break;
+               case NFTA_DEVICE_PREFIX:
+                       prefix = true;
+                       break;
+               default:
                        err = -EINVAL;
                        goto err_hook;
                }
 
-               hook = nft_netdev_hook_alloc(net, tmp);
+               hook = nft_netdev_hook_alloc(net, tmp, prefix);
                if (IS_ERR(hook)) {
                        NL_SET_BAD_ATTR(extack, tmp);
                        err = PTR_ERR(hook);
@@ -2427,7 +2448,7 @@ static int nft_chain_parse_netdev(struct net *net, struct nlattr *tb[],
        int err;
 
        if (tb[NFTA_HOOK_DEV]) {
-               hook = nft_netdev_hook_alloc(net, tb[NFTA_HOOK_DEV]);
+               hook = nft_netdev_hook_alloc(net, tb[NFTA_HOOK_DEV], false);
                if (IS_ERR(hook)) {
                        NL_SET_BAD_ATTR(extack, tb[NFTA_HOOK_DEV]);
                        return PTR_ERR(hook);
@@ -9458,8 +9479,7 @@ 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(skb, NFTA_DEVICE_NAME,
-                           hook->ifnamelen, hook->ifname))
+               if (nft_nla_put_hook_dev(skb, hook))
                        goto nla_put_failure;
        }
        nla_nest_end(skb, nest_devs);