net: check for altname conflicts when changing netdev's netns
authorJakub Kicinski <kuba@kernel.org>
Wed, 18 Oct 2023 01:38:14 +0000 (18:38 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 19 Oct 2023 13:51:16 +0000 (15:51 +0200)
It's currently possible to create an altname conflicting
with an altname or real name of another device by creating
it in another netns and moving it over:

 [ ~]$ ip link add dev eth0 type dummy

 [ ~]$ ip netns add test
 [ ~]$ ip -netns test link add dev ethX netns test type dummy
 [ ~]$ ip -netns test link property add dev ethX altname eth0
 [ ~]$ ip -netns test link set dev ethX netns 1

 [ ~]$ ip link
 ...
 3: eth0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
     link/ether 02:40:88:62:ec:b8 brd ff:ff:ff:ff:ff:ff
 ...
 5: ethX: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
     link/ether 26:b7:28:78:38:0f brd ff:ff:ff:ff:ff:ff
     altname eth0

Create a macro for walking the altnames, this hopefully makes
it clearer that the list we walk contains only altnames.
Which is otherwise not entirely intuitive.

Fixes: 36fbf1e52bd3 ("net: rtnetlink: add linkprop commands to add and delete alternative ifnames")
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/core/dev.c
net/core/dev.h

index f109ad3..ae55719 100644 (file)
@@ -1086,7 +1086,8 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf)
 
                for_each_netdev(net, d) {
                        struct netdev_name_node *name_node;
-                       list_for_each_entry(name_node, &d->name_node->list, list) {
+
+                       netdev_for_each_altname(d, name_node) {
                                if (!sscanf(name_node->name, name, &i))
                                        continue;
                                if (i < 0 || i >= max_netdevices)
@@ -11051,6 +11052,7 @@ EXPORT_SYMBOL(unregister_netdev);
 int __dev_change_net_namespace(struct net_device *dev, struct net *net,
                               const char *pat, int new_ifindex)
 {
+       struct netdev_name_node *name_node;
        struct net *net_old = dev_net(dev);
        char new_name[IFNAMSIZ] = {};
        int err, new_nsid;
@@ -11083,6 +11085,11 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
                if (err < 0)
                        goto out;
        }
+       /* Check that none of the altnames conflicts. */
+       err = -EEXIST;
+       netdev_for_each_altname(dev, name_node)
+               if (netdev_name_in_use(net, name_node->name))
+                       goto out;
 
        /* Check that new_ifindex isn't used yet. */
        if (new_ifindex) {
index e075e19..fa2e9c5 100644 (file)
@@ -62,6 +62,9 @@ struct netdev_name_node {
 int netdev_get_name(struct net *net, char *name, int ifindex);
 int dev_change_name(struct net_device *dev, const char *newname);
 
+#define netdev_for_each_altname(dev, namenode)                         \
+       list_for_each_entry((namenode), &(dev)->name_node->list, list)
+
 int netdev_name_node_alt_create(struct net_device *dev, const char *name);
 int netdev_name_node_alt_destroy(struct net_device *dev, const char *name);