]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
network: use two passes through networks 3699/head
authorChristian Brauner <christian.brauner@ubuntu.com>
Fri, 26 Feb 2021 13:05:09 +0000 (14:05 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Fri, 26 Feb 2021 14:12:40 +0000 (15:12 +0100)
Consider the following network layout:

 lxc.net.0.type = phys
 lxc.net.0.link = eth2
 lxc.net.0.name = eth%d

 lxc.net.1.type = phys
 lxc.net.1.link = eth1
 lxc.net.1.name = eth0

If we simply follow this order and create the first network first the kernel
will allocate eth0 for the first network but the second network requests
that eth1 be renamed to eth0 in the container's network namespace which
would lead to a clash.

Note, we don't handle cases like:

 lxc.net.0.type = phys
 lxc.net.0.link = eth2
 lxc.net.0.name = eth0

 lxc.net.1.type = phys
 lxc.net.1.link = eth1
 lxc.net.1.name = eth0

That'll brutally fail of course but there's nothing we can do about it. But
this can happen when e.g. a has the following LXD configuration:

devices:
  eth2:
    name: eth0
    nictype: physical
    parent: eth2
    type: nic
  eth3:
    name: eth0
    nictype: physical
    parent: eth3
    type: nic

in the container's config and the default profile has:

devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/network.c

index 52d980e79e64960ede47de0380c62bb7498a28c4..ed4b8ff331ac7ae339adb9cd86a92f253bdf3d55 100644 (file)
@@ -3953,24 +3953,78 @@ static int lxc_network_setup_in_child_namespaces_common(struct lxc_netdev *netde
        return 0;
 }
 
+/**
+ * Consider the following network layout:
+ *
+ *  lxc.net.0.type = phys
+ *  lxc.net.0.link = eth2
+ *  lxc.net.0.name = eth%d
+ *
+ *  lxc.net.1.type = phys
+ *  lxc.net.1.link = eth1
+ *  lxc.net.1.name = eth0
+ *
+ * If we simply follow this order and create the first network first the kernel
+ * will allocate eth0 for the first network but the second network requests
+ * that eth1 be renamed to eth0 in the container's network namespace which
+ * would lead to a clash.
+ *
+ * Note, we don't handle cases like:
+ *
+ *  lxc.net.0.type = phys
+ *  lxc.net.0.link = eth2
+ *  lxc.net.0.name = eth0
+ *
+ *  lxc.net.1.type = phys
+ *  lxc.net.1.link = eth1
+ *  lxc.net.1.name = eth0
+ *
+ * That'll brutally fail of course but there's nothing we can do about it.
+ */
 int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
                                          struct lxc_list *network)
 {
        struct lxc_list *iterator;
+       bool needs_second_pass = false;
 
-       lxc_list_for_each (iterator, network) {
+       if (lxc_list_empty(network))
+               return 0;
+
+       /* Configure all devices that have a specific target name. */
+       lxc_list_for_each(iterator, network) {
                struct lxc_netdev *netdev = iterator->elem;
                int ret;
 
+               if (is_empty_string(netdev->name) || strequal(netdev->name, "eth%d")) {
+                       needs_second_pass = true;
+                       continue;
+               }
+
                ret = netdev_configure_container[netdev->type](netdev);
                if (!ret)
                        ret = lxc_network_setup_in_child_namespaces_common(netdev);
                if (ret)
                        return log_error_errno(-1, errno, "Failed to setup netdev");
        }
-
-       if (!lxc_list_empty(network))
-               INFO("Network has been setup");
+       INFO("Finished setting up network devices with caller assigned names");
+
+       if (needs_second_pass) {
+               /* Configure all devices that have a kernel assigned name. */
+               lxc_list_for_each(iterator, network) {
+                       struct lxc_netdev *netdev = iterator->elem;
+                       int ret;
+
+                       if (!is_empty_string(netdev->name) && !strequal(netdev->name, "eth%d"))
+                               continue;
+
+                       ret = netdev_configure_container[netdev->type](netdev);
+                       if (!ret)
+                               ret = lxc_network_setup_in_child_namespaces_common(netdev);
+                       if (ret)
+                               return log_error_errno(-1, errno, "Failed to setup netdev");
+               }
+               INFO("Finished setting up network devices with kernel assigned names");
+       }
 
        return 0;
 }