diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 9742f84839..e91a9d36bf 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -442,40 +442,50 @@ int remove_bridge(const char *bridge_name) { } static int parse_interface(const char *name) { - _cleanup_(sd_device_unrefp) sd_device *d = NULL; int ifi, r; r = parse_ifindex_or_ifname(name, &ifi); if (r < 0) return log_error_errno(r, "Failed to resolve interface %s: %m", name); - if (path_is_read_only_fs("/sys") <= 0) { - char ifi_str[2 + DECIMAL_STR_MAX(int)]; - - /* udev should be around. */ - - sprintf(ifi_str, "n%i", ifi); - r = sd_device_new_from_device_id(&d, ifi_str); - if (r < 0) - return log_error_errno(r, "Failed to get device %s: %m", name); - - r = sd_device_get_is_initialized(d); - if (r < 0) - return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name); - - r = device_is_renaming(d); - if (r < 0) - return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name); - if (r > 0) - return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name); - } - return ifi; } -int move_network_interfaces(pid_t pid, char **ifaces) { +int test_network_interface_initialized(const char *name) { + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + int ifi, r; + char ifi_str[2 + DECIMAL_STR_MAX(int)]; + + if (path_is_read_only_fs("/sys")) + return 0; + + /* udev should be around. */ + + ifi = parse_interface(name); + if (ifi < 0) + return ifi; + + sprintf(ifi_str, "n%i", ifi); + r = sd_device_new_from_device_id(&d, ifi_str); + if (r < 0) + return log_error_errno(r, "Failed to get device %s: %m", name); + + r = sd_device_get_is_initialized(d); + if (r < 0) + return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name); + + r = device_is_renaming(d); + if (r < 0) + return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name); + if (r > 0) + return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name); + + return 0; +} + +int move_network_interfaces(int netns_fd, char **ifaces) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; char **i; int r; @@ -499,9 +509,9 @@ int move_network_interfaces(pid_t pid, char **ifaces) { if (r < 0) return log_error_errno(r, "Failed to allocate netlink message: %m"); - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd); if (r < 0) - return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); + return log_error_errno(r, "Failed to append namespace fd to netlink message: %m"); r = sd_netlink_call(rtnl, m, 0, NULL); if (r < 0) diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 32ea21ccc8..4999b172c4 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -5,6 +5,8 @@ #include #include +int test_network_interface_initialized(const char *name); + int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs); @@ -14,7 +16,7 @@ int remove_bridge(const char *bridge_name); int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces); int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); -int move_network_interfaces(pid_t pid, char **ifaces); +int move_network_interfaces(int netns_fd, char **ifaces); int veth_extra_parse(char ***l, const char *p); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 9113f6e323..32294ed002 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -848,6 +848,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Network interface name not valid: %s", optarg); + r = test_network_interface_initialized(optarg); + if (r < 0) + return r; + if (strv_extend(&arg_network_interfaces, optarg) < 0) return log_oom(); @@ -861,6 +865,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "MACVLAN network interface name not valid: %s", optarg); + r = test_network_interface_initialized(optarg); + if (r < 0) + return r; + if (strv_extend(&arg_network_macvlan, optarg) < 0) return log_oom(); @@ -874,6 +882,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IPVLAN network interface name not valid: %s", optarg); + r = test_network_interface_initialized(optarg); + if (r < 0) + return r; + if (strv_extend(&arg_network_ipvlan, optarg) < 0) return log_oom(); @@ -4199,7 +4211,7 @@ static int run_container( int ifi = 0, r; ssize_t l; sigset_t mask_chld; - _cleanup_close_ int netns_fd = -1; + _cleanup_close_ int child_netns_fd = -1; assert_se(sigemptyset(&mask_chld) == 0); assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); @@ -4258,11 +4270,11 @@ static int run_container( return log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); if (arg_network_namespace_path) { - netns_fd = open(arg_network_namespace_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netns_fd < 0) + child_netns_fd = open(arg_network_namespace_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (child_netns_fd < 0) return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path); - r = fd_is_network_ns(netns_fd); + r = fd_is_network_ns(child_netns_fd); if (r == -EUCLEAN) log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path); else if (r < 0) @@ -4307,7 +4319,7 @@ static int run_container( master_pty_socket_pair[1], unified_cgroup_hierarchy_socket_pair[1], fds, - netns_fd); + child_netns_fd); if (r < 0) _exit(EXIT_FAILURE); @@ -4409,7 +4421,15 @@ static int run_container( return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early"); } - r = move_network_interfaces(*pid, arg_network_interfaces); + if (child_netns_fd < 0) { + /* Make sure we have an open file descriptor to the child's network + * namespace so it stays alive even if the child exits. */ + r = namespace_open(*pid, NULL, NULL, &child_netns_fd, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to open child network namespace: %m"); + } + + r = move_network_interfaces(child_netns_fd, arg_network_interfaces); if (r < 0) return r; @@ -4655,6 +4675,36 @@ static int run_container( /* Normally redundant, but better safe than sorry */ (void) kill(*pid, SIGKILL); + if (arg_private_network) { + /* Move network interfaces back to the parent network namespace. We use `safe_fork` + * to avoid having to move the parent to the child network namespace. */ + r = safe_fork(NULL, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL); + if (r < 0) + return r; + + if (r == 0) { + _cleanup_close_ int parent_netns_fd = -1; + + r = namespace_open(getpid(), NULL, NULL, &parent_netns_fd, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed to open parent network namespace: %m"); + _exit(EXIT_FAILURE); + } + + r = namespace_enter(-1, -1, child_netns_fd, -1, -1); + if (r < 0) { + log_error_errno(r, "Failed to enter child network namespace: %m"); + _exit(EXIT_FAILURE); + } + + r = move_network_interfaces(parent_netns_fd, arg_network_interfaces); + if (r < 0) + log_error_errno(r, "Failed to move network interfaces back to parent network namespace: %m"); + + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + } + } + r = wait_for_container(*pid, &container_status); *pid = 0;