Merge pull request #30956 from yuwata/nspawn-network-pass-wifi

nspawn: support passing wireless interface to container
This commit is contained in:
Luca Boccassi
2024-01-19 11:41:58 +00:00
committed by GitHub
25 changed files with 574 additions and 135 deletions
+77 -39
View File
@@ -33,71 +33,78 @@ const struct namespace_info namespace_info[] = {
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
_cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF;
int rfd = -EBADF;
int namespace_open(
pid_t pid,
int *ret_pidns_fd,
int *ret_mntns_fd,
int *ret_netns_fd,
int *ret_userns_fd,
int *ret_root_fd) {
_cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
userns_fd = -EBADF, root_fd = -EBADF;
assert(pid >= 0);
if (mntns_fd) {
const char *mntns;
mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (mntnsfd < 0)
return -errno;
}
if (pidns_fd) {
if (ret_pidns_fd) {
const char *pidns;
pidns = pid_namespace_path(pid, NAMESPACE_PID);
pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (pidnsfd < 0)
pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (pidns_fd < 0)
return -errno;
}
if (netns_fd) {
if (ret_mntns_fd) {
const char *mntns;
mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (mntns_fd < 0)
return -errno;
}
if (ret_netns_fd) {
const char *netns;
netns = pid_namespace_path(pid, NAMESPACE_NET);
netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (netnsfd < 0)
netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (netns_fd < 0)
return -errno;
}
if (userns_fd) {
if (ret_userns_fd) {
const char *userns;
userns = pid_namespace_path(pid, NAMESPACE_USER);
usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (usernsfd < 0 && errno != ENOENT)
userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (userns_fd < 0 && errno != ENOENT)
return -errno;
}
if (root_fd) {
if (ret_root_fd) {
const char *root;
root = procfs_file_alloca(pid, "root");
rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (rfd < 0)
root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (root_fd < 0)
return -errno;
}
if (pidns_fd)
*pidns_fd = TAKE_FD(pidnsfd);
if (ret_pidns_fd)
*ret_pidns_fd = TAKE_FD(pidns_fd);
if (mntns_fd)
*mntns_fd = TAKE_FD(mntnsfd);
if (ret_mntns_fd)
*ret_mntns_fd = TAKE_FD(mntns_fd);
if (netns_fd)
*netns_fd = TAKE_FD(netnsfd);
if (ret_netns_fd)
*ret_netns_fd = TAKE_FD(netns_fd);
if (userns_fd)
*userns_fd = TAKE_FD(usernsfd);
if (ret_userns_fd)
*ret_userns_fd = TAKE_FD(userns_fd);
if (root_fd)
*root_fd = TAKE_FD(rfd);
if (ret_root_fd)
*ret_root_fd = TAKE_FD(root_fd);
return 0;
}
@@ -221,7 +228,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to fork process (sd-mkuserns): %m");
if (r == 0)
/* Child. We do nothing here, just freeze until somebody kills us. */
freeze();
@@ -229,19 +236,50 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
xsprintf(path, "/proc/" PID_FMT "/uid_map", pid);
r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_error_errno(r, "Failed to write UID map: %m");
return log_debug_errno(r, "Failed to write UID map: %m");
xsprintf(path, "/proc/" PID_FMT "/gid_map", pid);
r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_error_errno(r, "Failed to write GID map: %m");
return log_debug_errno(r, "Failed to write GID map: %m");
r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
r = namespace_open(pid,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
/* ret_netns_fd = */ NULL,
&userns_fd,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to open userns fd: %m");
return log_debug_errno(r, "Failed to open userns fd: %m");
return TAKE_FD(userns_fd);
}
int netns_acquire(void) {
_cleanup_(sigkill_waitp) pid_t pid = 0;
_cleanup_close_ int netns_fd = -EBADF;
int r;
/* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
* the process again. This way we have a netns fd that is not bound to any process. */
r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid);
if (r < 0)
return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m");
if (r == 0)
/* Child. We do nothing here, just freeze until somebody kills us. */
freeze();
r = namespace_open(pid,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
&netns_fd,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_debug_errno(r, "Failed to open netns fd: %m");
return TAKE_FD(netns_fd);
}
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
+8 -1
View File
@@ -22,7 +22,13 @@ extern const struct namespace_info {
unsigned int clone_flag;
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
int namespace_open(
pid_t pid,
int *ret_pidns_fd,
int *ret_mntns_fd,
int *ret_netns_fd,
int *ret_userns_fd,
int *ret_root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
int fd_is_ns(int fd, unsigned long nsflag);
@@ -45,4 +51,5 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
}
int userns_acquire(const char *uid_map, const char *gid_map);
int netns_acquire(void);
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
+3 -2
View File
@@ -1541,10 +1541,11 @@ int safe_fork_full(
}
}
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
(FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
else
pid = fork();
if (pid < 0)
+6 -5
View File
@@ -154,11 +154,11 @@ int must_be_root(void);
pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
/* 💣 Note that FORK_NEW_USERNS + FORK_NEW_MOUNTNS should not be called in threaded programs, because they
* cause us to use raw_clone() which does not synchronize the glibc malloc() locks, and thus will cause
* deadlocks if the parent uses threads and the child does memory allocations. Hence: if the parent is
* threaded these flags may not be used. These flags cannot be used if the parent uses threads or the child
* uses malloc(). 💣 */
/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
* programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
* and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
* the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
* or the child uses malloc(). 💣 */
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
@@ -179,6 +179,7 @@ typedef enum ForkFlags {
FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
} ForkFlags;
int safe_fork_full(
+2 -2
View File
@@ -1211,7 +1211,7 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
assert(pid > 0);
r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
@@ -1262,7 +1262,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pid_t child;
int r;
r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
+1 -1
View File
@@ -34,7 +34,7 @@ int bus_container_connect_socket(sd_bus *b) {
log_debug("sd-bus: connecting bus%s%s to namespace of PID "PID_FMT"...",
b->description ? " " : "", strempty(b->description), b->nspid);
r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return log_debug_errno(r, "Failed to open namespace of PID "PID_FMT": %m", b->nspid);
@@ -21,6 +21,7 @@ int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
+19
View File
@@ -2423,6 +2423,25 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
return v > 0;
}
int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value) {
const char *value;
int r;
r = sd_device_get_sysattr_value(device, sysattr, &value);
if (r < 0)
return r;
uint32_t v;
r = safe_atou32(value, &v);
if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
if (ret_value)
*ret_value = v;
/* We return "true" if the value is positive. */
return v > 0;
}
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value;
int r;
@@ -199,6 +199,7 @@ static const NLAPolicy genl_nl80211_policies[] = {
[NL80211_ATTR_SSID] = BUILD_POLICY_WITH_SIZE(BINARY, IEEE80211_MAX_SSID_LEN),
[NL80211_ATTR_STATUS_CODE] = BUILD_POLICY(U16),
[NL80211_ATTR_4ADDR] = BUILD_POLICY(U8),
[NL80211_ATTR_NETNS_FD] = BUILD_POLICY(U32),
};
/***************** genl wireguard type systems *****************/
+38
View File
@@ -20,6 +20,13 @@ static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
assert(name);
/* Assign the requested name. */
if (!*rtnl) {
r = sd_netlink_open(rtnl);
if (r < 0)
return r;
}
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
if (r < 0)
return r;
@@ -31,6 +38,37 @@ static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
return sd_netlink_call(*rtnl, message, 0, NULL);
}
int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) {
_cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
int r, ifindex;
assert(orig_name);
assert(new_name);
/* This does not check alternative names. Callers must check the requested name is not used as an
* alternative name. */
if (streq(orig_name, new_name))
return 0;
if (!ifname_valid(new_name))
return -EINVAL;
if (!rtnl)
rtnl = &our_rtnl;
if (!*rtnl) {
r = sd_netlink_open(rtnl);
if (r < 0)
return r;
}
ifindex = rtnl_resolve_ifname(rtnl, orig_name);
if (ifindex < 0)
return ifindex;
return set_link_name(rtnl, ifindex, new_name);
}
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) {
_cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL;
bool altname_deleted = false;
+1
View File
@@ -31,6 +31,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free);
int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name);
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names);
static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
return rtnl_set_link_name(rtnl, ifindex, NULL, alternative_names);
+13
View File
@@ -681,6 +681,19 @@ TEST(rtnl_set_link_name) {
_cleanup_free_ char *resolved = NULL;
assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
assert_se(streq_ptr(resolved, "test-shortname"));
resolved = mfree(resolved);
assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname") >= 0);
assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname2") >= 0);
assert_se(rtnl_rename_link(NULL, "test-shortname2", "test-shortname3") >= 0);
assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
assert_se(streq_ptr(resolved, "test-shortname3"));
resolved = mfree(resolved);
assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-shortname3", &resolved) == ifindex);
assert_se(streq_ptr(resolved, "test-shortname3"));
resolved = mfree(resolved);
}
DEFINE_TEST_MAIN(LOG_DEBUG);
+18 -3
View File
@@ -232,7 +232,12 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (streq(us, them))
return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
r = namespace_open(m->leader.pid, NULL, NULL, &netns_fd, NULL, NULL);
r = namespace_open(m->leader.pid,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
&netns_fd,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return r;
@@ -366,7 +371,12 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
_cleanup_fclose_ FILE *f = NULL;
pid_t child;
r = namespace_open(m->leader.pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
r = namespace_open(m->leader.pid,
&pidns_fd,
&mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
&root_fd);
if (r < 0)
return r;
@@ -1069,7 +1079,12 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
pid_t child;
r = namespace_open(m->leader.pid, NULL, &mntns_fd, NULL, NULL, &root_fd);
r = namespace_open(m->leader.pid,
/* ret_pidns_fd = */ NULL,
&mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
&root_fd);
if (r < 0)
return r;
+16 -3
View File
@@ -1388,17 +1388,30 @@ int wipe_fully_visible_fs(int mntns_fd) {
_cleanup_close_ int orig_mntns_fd = -EBADF;
int r, rr;
r = namespace_open(0, NULL, &orig_mntns_fd, NULL, NULL, NULL);
r = namespace_open(0,
/* ret_pidns_fd = */ NULL,
&orig_mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to pin originating mount namespace: %m");
r = namespace_enter(-EBADF, mntns_fd, -EBADF, -EBADF, -EBADF);
r = namespace_enter(/* pidns_fd = */ -EBADF,
mntns_fd,
/* netns_fd = */ -EBADF,
/* userns_fd = */ -EBADF,
/* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter mount namespace: %m");
rr = do_wipe_fully_visible_fs();
r = namespace_enter(-EBADF, orig_mntns_fd, -EBADF, -EBADF, -EBADF);
r = namespace_enter(/* pidns_fd = */ -EBADF,
orig_mntns_fd,
/* netns_fd = */ -EBADF,
/* userns_fd = */ -EBADF,
/* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter original mount namespace: %m");
+302 -29
View File
@@ -2,22 +2,31 @@
#include <net/if.h>
#include <linux/if.h>
#include <linux/nl80211.h>
#include <linux/veth.h>
#include <sys/file.h>
#include <sys/mount.h>
#include "sd-device.h"
#include "sd-id128.h"
#include "sd-netlink.h"
#include "alloc-util.h"
#include "device-private.h"
#include "device-util.h"
#include "ether-addr-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
#include "lock-util.h"
#include "missing_network.h"
#include "mkdir.h"
#include "mount-util.h"
#include "namespace-util.h"
#include "netif-naming-scheme.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "parse-util.h"
#include "process-util.h"
#include "siphash24.h"
#include "socket-netlink.h"
#include "socket-util.h"
@@ -503,42 +512,306 @@ int test_network_interfaces_initialized(char **iface_pairs) {
return 0;
}
int move_network_interfaces(int netns_fd, char **iface_pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
static int netns_child_begin(int netns_fd, int *ret_original_netns_fd) {
_cleanup_close_ int original_netns_fd = -EBADF;
int r;
assert(netns_fd >= 0);
if (ret_original_netns_fd) {
r = namespace_open(0,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
&original_netns_fd,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to open original network namespace: %m");
}
r = namespace_enter(/* pidns_fd = */ -EBADF,
/* mntns_fd = */ -EBADF,
netns_fd,
/* userns_fd = */ -EBADF,
/* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter child network namespace: %m");
r = umount_recursive("/sys/", /* flags = */ 0);
if (r < 0)
log_debug_errno(r, "Failed to unmount directories below /sys/, ignoring: %m");
(void) mkdir_p("/sys/", 0755);
/* Populate new sysfs instance associated with the client netns, to make sd_device usable. */
r = mount_nofollow_verbose(LOG_ERR, "sysfs", "/sys/", "sysfs",
MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, /* opts = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to mount sysfs on /sys/: %m");
/* udev_avaliable() might be called previously and the result may be cached.
* Now, we (re-)mount sysfs. Hence, we need to reset the cache. */
reset_cached_udev_availability();
if (ret_original_netns_fd)
*ret_original_netns_fd = TAKE_FD(original_netns_fd);
return 0;
}
static int netns_fork_and_wait(int netns_fd, int *ret_original_netns_fd) {
int r;
assert(netns_fd >= 0);
r = safe_fork("(sd-netns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
if (r < 0)
return log_error_errno(r, "Failed to fork process (sd-netns): %m");
if (r == 0) {
if (netns_child_begin(netns_fd, ret_original_netns_fd) < 0)
_exit(EXIT_FAILURE);
return 0;
}
if (ret_original_netns_fd)
*ret_original_netns_fd = -EBADF;
return 1;
}
static int needs_rename(sd_netlink **rtnl, sd_device *dev, const char *name) {
int r;
assert(rtnl);
assert(dev);
assert(name);
const char *ifname;
r = sd_device_get_sysname(dev, &ifname);
if (r < 0)
return r;
if (streq(name, ifname))
return false;
int ifindex;
r = sd_device_get_ifindex(dev, &ifindex);
if (r < 0)
return r;
_cleanup_strv_free_ char **altnames = NULL;
r = rtnl_get_link_alternative_names(rtnl, ifindex, &altnames);
if (r == -EOPNOTSUPP)
return true; /* alternative interface name is not supported, hence the name is not
* assigned to the interface. */
if (r < 0)
return r;
return !strv_contains(altnames, name);
}
static int move_wlan_interface_impl(sd_netlink **genl, int netns_fd, sd_device *dev) {
_cleanup_(sd_netlink_unrefp) sd_netlink *our_genl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netns_fd >= 0);
assert(dev);
if (!genl)
genl = &our_genl;
if (!*genl) {
r = sd_genl_socket_open(genl);
if (r < 0)
return log_error_errno(r, "Failed to connect to generic netlink: %m");
}
r = sd_genl_message_new(*genl, NL80211_GENL_NAME, NL80211_CMD_SET_WIPHY_NETNS, &m);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
uint32_t phy_index;
r = device_get_sysattr_u32(dev, "phy80211/index", &phy_index);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get phy index: %m");
r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, phy_index);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to append phy index to netlink message: %m");
r = sd_netlink_message_append_u32(m, NL80211_ATTR_NETNS_FD, netns_fd);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
r = sd_netlink_call(*genl, m, 0, NULL);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
return 0;
}
static int move_wlan_interface_one(
sd_netlink **rtnl,
sd_netlink **genl,
int *temp_netns_fd,
int netns_fd,
sd_device *dev,
const char *name) {
int r;
assert(rtnl);
assert(genl);
assert(temp_netns_fd);
assert(netns_fd >= 0);
assert(dev);
assert(name);
r = needs_rename(rtnl, dev, name);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to determine if the interface should be renamed to '%s': %m", name);
if (r == 0)
return move_wlan_interface_impl(genl, netns_fd, dev);
/* The command NL80211_CMD_SET_WIPHY_NETNS takes phy instead of network interface, and does not take
* an interface name in the passed network namespace. Hence, we need to move the phy and interface to
* a temporary network namespace, rename the interface in it, and move them to the requested netns. */
if (*temp_netns_fd < 0) {
r = netns_acquire();
if (r < 0)
return log_error_errno(r, "Failed to acquire new network namespace: %m");
*temp_netns_fd = r;
}
r = move_wlan_interface_impl(genl, *temp_netns_fd, dev);
if (r < 0)
return r;
const char *sysname;
r = sd_device_get_sysname(dev, &sysname);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get interface name: %m");
r = netns_fork_and_wait(*temp_netns_fd, NULL);
if (r < 0)
return log_error_errno(r, "Failed to fork process (nspawn-rename-wlan): %m");
if (r == 0) {
_cleanup_(sd_device_unrefp) sd_device *temp_dev = NULL;
r = rtnl_rename_link(NULL, sysname, name);
if (r < 0) {
log_error_errno(r, "Failed to rename network interface '%s' to '%s': %m", sysname, name);
goto finalize;
}
r = sd_device_new_from_ifname(&temp_dev, name);
if (r < 0) {
log_error_errno(r, "Failed to acquire device '%s': %m", name);
goto finalize;
}
r = move_wlan_interface_impl(NULL, netns_fd, temp_dev);
finalize:
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
return 0;
}
static int move_network_interface_one(sd_netlink **rtnl, int netns_fd, sd_device *dev, const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(rtnl);
assert(netns_fd >= 0);
assert(dev);
assert(name);
if (!*rtnl) {
r = sd_netlink_open(rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to rtnetlink: %m");
}
int ifindex;
r = sd_device_get_ifindex(dev, &ifindex);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
r = sd_rtnl_message_new_link(*rtnl, &m, RTM_SETLINK, ifindex);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
r = needs_rename(rtnl, dev, name);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to determine if the interface should be renamed to '%s': %m", name);
if (r > 0) {
r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to add netlink interface name: %m");
}
r = sd_netlink_call(*rtnl, m, 0, NULL);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
return 0;
}
int move_network_interfaces(int netns_fd, char **iface_pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL, *genl = NULL;
_cleanup_close_ int temp_netns_fd = -EBADF;
int r;
assert(netns_fd >= 0);
if (strv_isempty(iface_pairs))
return 0;
r = sd_netlink_open(&rtnl);
STRV_FOREACH_PAIR(from, to, iface_pairs) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
r = sd_device_new_from_ifname(&dev, *from);
if (r < 0)
return log_error_errno(r, "Unknown interface name %s: %m", *from);
if (device_is_devtype(dev, "wlan"))
r = move_wlan_interface_one(&rtnl, &genl, &temp_netns_fd, netns_fd, dev, *to);
else
r = move_network_interface_one(&rtnl, netns_fd, dev, *to);
if (r < 0)
return r;
}
return 0;
}
int move_back_network_interfaces(int child_netns_fd, char **interface_pairs) {
_cleanup_close_ int parent_netns_fd = -EBADF;
int r;
assert(child_netns_fd >= 0);
if (strv_isempty(interface_pairs))
return 0;
r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
return r;
if (r == 0) {
/* Reverse network interfaces pair list so that interfaces get their initial name back.
* This is about ensuring interfaces get their old name back when being moved back. */
interface_pairs = strv_reverse(interface_pairs);
STRV_FOREACH_PAIR(i, b, iface_pairs) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi;
ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi);
if (r < 0)
return log_error_errno(r, "Failed to allocate netlink message: %m");
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 fd to netlink message: %m");
if (!streq(*b, *i)) {
r = sd_netlink_message_append_string(m, IFLA_IFNAME, *b);
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface name: %m");
}
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
r = move_network_interfaces(parent_netns_fd, interface_pairs);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
return 0;
+1
View File
@@ -19,6 +19,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs);
int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs);
int move_network_interfaces(int netns_fd, char **iface_pairs);
int move_back_network_interfaces(int child_netns_fd, char **interface_pairs);
int veth_extra_parse(char ***l, const char *p);
+18 -32
View File
@@ -3781,7 +3781,12 @@ static int outer_child(
return r;
if (arg_userns_mode != USER_NAMESPACE_NO) {
r = namespace_open(0, NULL, &mntns_fd, NULL, NULL, NULL);
r = namespace_open(0,
/* ret_pidns_fd = */ NULL,
&mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to pin outer mount namespace: %m");
@@ -4130,7 +4135,11 @@ static int outer_child(
* user if user namespaces are turned on. */
if (arg_network_namespace_path) {
r = namespace_enter(-1, -1, netns_fd, -1, -1);
r = namespace_enter(/* pidns_fd = */ -EBADF,
/* mntns_fd = */ -EBADF,
netns_fd,
/* userns_fd = */ -EBADF,
/* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to join network namespace: %m");
}
@@ -5078,7 +5087,12 @@ static int run_container(
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);
r = namespace_open(*pid,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
&child_netns_fd,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to open child network namespace: %m");
}
@@ -5363,37 +5377,9 @@ static int run_container(
fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
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_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
r = move_back_network_interfaces(child_netns_fd, arg_network_interfaces);
if (r < 0)
return r;
if (r == 0) {
_cleanup_close_ int parent_netns_fd = -EBADF;
r = namespace_open(getpid_cached(), 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);
}
/* Reverse network interfaces pair list so that interfaces get their initial name back.
* This is about ensuring interfaces get their old name back when being moved back. */
arg_network_interfaces = strv_reverse(arg_network_interfaces);
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(TAKE_PID(*pid), &container_status);
+1 -1
View File
@@ -1688,7 +1688,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (r < 0)
return r;
r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
if (r < 0)
return r;
+11 -2
View File
@@ -265,7 +265,12 @@ int machine_id_commit(const char *root) {
fd = safe_close(fd);
/* Store current mount namespace */
r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
r = namespace_open(0,
/* ret_pidns_fd = */ NULL,
&initial_mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Can't fetch current mount namespace: %m");
@@ -284,7 +289,11 @@ int machine_id_commit(const char *root) {
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
/* Return to initial namespace and proceed a lazy tmpfs unmount */
r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
r = namespace_enter(/* pidns_fd = */ -EBADF,
initial_mntns_fd,
/* netns_fd = */ -EBADF,
/* userns_fd = */ -EBADF,
/* root_fd = */ -EBADF);
if (r < 0)
return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
+1 -1
View File
@@ -1100,7 +1100,7 @@ static int mount_in_namespace(
if (!pidref_is_set(target))
return -ESRCH;
r = namespace_open(target->pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
r = namespace_open(target->pid, &pidns_fd, &mntns_fd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &root_fd);
if (r < 0)
return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
+4 -4
View File
@@ -420,11 +420,11 @@ int netns_get_nsid(int netnsfd, uint32_t *ret) {
if (netnsfd < 0) {
r = namespace_open(
0,
/* pidns_fd= */ NULL,
/* mntns_fd= */ NULL,
/* ret_pidns_fd = */ NULL,
/* ret_mntns_fd = */ NULL,
&_netns_fd,
/* userns_fd= */ NULL,
/* root_fd= */ NULL);
/* ret_userns_fd = */ NULL,
/* ret_root_fd = */ NULL);
if (r < 0)
return r;
+10 -5
View File
@@ -14,6 +14,7 @@
#include "id128-util.h"
#include "log.h"
#include "macro.h"
#include "missing_threads.h"
#include "parse-util.h"
#include "path-util.h"
#include "signal-util.h"
@@ -363,18 +364,22 @@ int udev_queue_is_empty(void) {
(errno == ENOENT ? true : -errno) : false;
}
bool udev_available(void) {
static int cache = -1;
static int cached_udev_availability = -1;
void reset_cached_udev_availability(void) {
cached_udev_availability = -1;
}
bool udev_available(void) {
/* The service systemd-udevd is started only when /sys is read write.
* See systemd-udevd.service: ConditionPathIsReadWrite=/sys
* Also, our container interface (http://systemd.io/CONTAINER_INTERFACE/) states that /sys must
* be mounted in read-only mode in containers. */
if (cache >= 0)
return cache;
if (cached_udev_availability >= 0)
return cached_udev_availability;
return (cache = (path_is_read_only_fs("/sys/") <= 0));
return (cached_udev_availability = (path_is_read_only_fs("/sys/") <= 0));
}
int device_get_vendor_string(sd_device *device, const char **ret) {
+1
View File
@@ -23,6 +23,7 @@ size_t udev_replace_chars(char *str, const char *allow);
int udev_queue_is_empty(void);
void reset_cached_udev_availability(void);
bool udev_available(void);
int device_get_vendor_string(sd_device *device, const char **ret);
+4
View File
@@ -13,6 +13,10 @@ test_append_files() {
local workspace="${1:?}"
local container="$workspace/testsuite-13-container-template"
# For virtual wlan interface.
instmods mac80211_hwsim
generate_module_dependencies
# Create a dummy container "template" with a minimal toolset, which we can
# then use as a base for our nspawn/machinectl tests
initdir="$container" setup_basic_dirs
+17 -5
View File
@@ -324,7 +324,7 @@ EOF
}
nspawn_settings_cleanup() {
for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do
for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
ip link del "$dev" || :
done
@@ -332,7 +332,7 @@ nspawn_settings_cleanup() {
}
testcase_nspawn_settings() {
local root container dev private_users
local root container dev private_users wlan_names='' wlan_checks=''
mkdir -p /run/systemd/nspawn
root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
@@ -341,10 +341,17 @@ testcase_nspawn_settings() {
rm -f "/etc/systemd/nspawn/$container.nspawn"
mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
# add virtual wlan interfaces
if modprobe mac80211_hwsim radios=2; then
wlan_names='wlan0 wlan1:wl-renamed1'
wlan_checks='ip link | grep wlan0\nip link | grep wl-renamed1'
fi
for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
ip link add "$dev" type dummy
done
udevadm settle
ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
ip link
trap nspawn_settings_cleanup RETURN
@@ -394,7 +401,7 @@ Private=yes
VirtualEthernet=yes
VirtualEthernetExtra=my-fancy-veth1
VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
Interface=sd-shared1 sd-shared2:sd-shared2
Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names}
MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
Zone=sd-zone0
@@ -437,12 +444,17 @@ ip link | grep host0@
ip link | grep my-fancy-veth1@
ip link | grep my-fancy-veth2@
ip link | grep sd-shared1
ip link | grep sd-shared2
ip link | grep sd-renamed2
ip link | grep sd-shared3
ip link | grep sd-altname3
ip link | grep sd-altname-tooooooooooooo-long
ip link | grep mv-sd-macvlan1@
ip link | grep my-macvlan2@
ip link | grep iv-sd-ipvlan1@
ip link | grep my-ipvlan2@
EOF
echo -e "$wlan_checks" >>"$root/entrypoint.sh"
timeout 30 systemd-nspawn --directory="$root"
# And now for stuff that needs to run separately