From: Christian Brauner Date: Fri, 26 Feb 2021 13:05:09 +0000 (+0100) Subject: network: use two passes through networks X-Git-Tag: lxc-5.0.0~265^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F3699%2Fhead;p=thirdparty%2Flxc.git network: use two passes through networks 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 --- diff --git a/src/lxc/network.c b/src/lxc/network.c index 52d980e79..ed4b8ff33 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -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; }