1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

Refactor network namespace specific functions in generic helpers

This commit is contained in:
Xℹ Ruoyao 2021-02-16 20:49:15 +08:00
parent a959cd2812
commit 54c2459d56
No known key found for this signature in database
GPG Key ID: D95E4716CCBB34DC
11 changed files with 88 additions and 59 deletions

View File

@ -125,13 +125,13 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
return reset_uid_gid();
}
int fd_is_network_ns(int fd) {
int fd_is_ns(int fd, unsigned long nsflag) {
struct statfs s;
int r;
/* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
* way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
* this somewhat nicely.
/* Checks whether the specified file descriptor refers to a namespace created by specifying nsflag in clone().
* On old kernels there's no nice way to detect that, hence on those we'll return a recognizable error (EUCLEAN),
* so that callers can handle this somewhat nicely.
*
* This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
* refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
@ -168,7 +168,7 @@ int fd_is_network_ns(int fd) {
return -errno;
}
return r == CLONE_NEWNET;
return (unsigned long) r == nsflag;
}
int detach_mount_namespace(void) {

View File

@ -6,6 +6,6 @@
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
int fd_is_network_ns(int fd);
int fd_is_ns(int fd, unsigned long nsflag);
int detach_mount_namespace(void);

View File

@ -3918,7 +3918,7 @@ static int exec_child(
}
if (context->network_namespace_path && runtime && runtime->netns_storage_socket[0] >= 0) {
r = open_netns_path(runtime->netns_storage_socket, context->network_namespace_path);
r = open_shareable_ns_path(runtime->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
if (r < 0) {
*exit_status = EXIT_NETWORK;
return log_unit_error_errno(unit, r, "Failed to open network namespace path %s: %m", context->network_namespace_path);
@ -4195,7 +4195,7 @@ static int exec_child(
if ((context->private_network || context->network_namespace_path) && runtime && runtime->netns_storage_socket[0] >= 0) {
if (ns_type_supported(NAMESPACE_NET)) {
r = setup_netns(runtime->netns_storage_socket);
r = setup_shareable_ns(runtime->netns_storage_socket, CLONE_NEWNET);
if (r == -EPERM)
log_unit_warning_errno(unit, r,
"PrivateNetwork=yes is configured, but network namespace setup failed, ignoring: %m");

View File

@ -26,6 +26,7 @@
#include "mountpoint-util.h"
#include "namespace-util.h"
#include "namespace.h"
#include "nsflags.h"
#include "nulstr-util.h"
#include "os-util.h"
#include "path-util.h"
@ -2508,13 +2509,17 @@ int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
return 0;
}
int setup_netns(const int netns_storage_socket[static 2]) {
_cleanup_close_ int netns = -1;
int setup_shareable_ns(const int ns_storage_socket[static 2], unsigned long nsflag) {
_cleanup_close_ int ns = -1;
int r, q;
const char *ns_name, *ns_path;
assert(netns_storage_socket);
assert(netns_storage_socket[0] >= 0);
assert(netns_storage_socket[1] >= 0);
assert(ns_storage_socket);
assert(ns_storage_socket[0] >= 0);
assert(ns_storage_socket[1] >= 0);
ns_name = namespace_single_flag_to_string(nsflag);
assert(ns_name);
/* We use the passed socketpair as a storage buffer for our
* namespace reference fd. Whatever process runs this first
@ -2524,35 +2529,36 @@ int setup_netns(const int netns_storage_socket[static 2]) {
*
* It's a bit crazy, but hey, works great! */
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
if (lockf(ns_storage_socket[0], F_LOCK, 0) < 0)
return -errno;
netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
if (netns == -EAGAIN) {
ns = receive_one_fd(ns_storage_socket[0], MSG_DONTWAIT);
if (ns == -EAGAIN) {
/* Nothing stored yet, so let's create a new namespace. */
if (unshare(CLONE_NEWNET) < 0) {
if (unshare(nsflag) < 0) {
r = -errno;
goto fail;
}
(void) loopback_setup();
netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (netns < 0) {
ns_path = strjoina("/proc/self/ns/", ns_name);
ns = open(ns_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (ns < 0) {
r = -errno;
goto fail;
}
r = 1;
} else if (netns < 0) {
r = netns;
} else if (ns < 0) {
r = ns;
goto fail;
} else {
/* Yay, found something, so let's join the namespace */
if (setns(netns, CLONE_NEWNET) < 0) {
if (setns(ns, nsflag) < 0) {
r = -errno;
goto fail;
}
@ -2560,45 +2566,45 @@ int setup_netns(const int netns_storage_socket[static 2]) {
r = 0;
}
q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
q = send_one_fd(ns_storage_socket[1], ns, MSG_DONTWAIT);
if (q < 0) {
r = q;
goto fail;
}
fail:
(void) lockf(netns_storage_socket[0], F_ULOCK, 0);
(void) lockf(ns_storage_socket[0], F_ULOCK, 0);
return r;
}
int open_netns_path(const int netns_storage_socket[static 2], const char *path) {
_cleanup_close_ int netns = -1;
int open_shareable_ns_path(const int ns_storage_socket[static 2], const char *path, unsigned long nsflag) {
_cleanup_close_ int ns = -1;
int q, r;
assert(netns_storage_socket);
assert(netns_storage_socket[0] >= 0);
assert(netns_storage_socket[1] >= 0);
assert(ns_storage_socket);
assert(ns_storage_socket[0] >= 0);
assert(ns_storage_socket[1] >= 0);
assert(path);
/* If the storage socket doesn't contain a netns fd yet, open one via the file system and store it in
* it. This is supposed to be called ahead of time, i.e. before setup_netns() which will allocate a
* new anonymous netns if needed. */
/* If the storage socket doesn't contain a ns fd yet, open one via the file system and store it in
* it. This is supposed to be called ahead of time, i.e. before setup_shareable_ns() which will
* allocate a new anonymous ns if needed. */
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
if (lockf(ns_storage_socket[0], F_LOCK, 0) < 0)
return -errno;
netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
if (netns == -EAGAIN) {
ns = receive_one_fd(ns_storage_socket[0], MSG_DONTWAIT);
if (ns == -EAGAIN) {
/* Nothing stored yet. Open the file from the file system. */
netns = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (netns < 0) {
ns = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (ns < 0) {
r = -errno;
goto fail;
}
r = fd_is_network_ns(netns);
if (r == 0) { /* Not a netns? Refuse early. */
r = fd_is_ns(ns, nsflag);
if (r == 0) { /* Not a ns of our type? Refuse early. */
r = -EINVAL;
goto fail;
}
@ -2607,20 +2613,20 @@ int open_netns_path(const int netns_storage_socket[static 2], const char *path)
r = 1;
} else if (netns < 0) {
r = netns;
} else if (ns < 0) {
r = ns;
goto fail;
} else
r = 0; /* Already allocated */
q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
q = send_one_fd(ns_storage_socket[1], ns, MSG_DONTWAIT);
if (q < 0) {
r = q;
goto fail;
}
fail:
(void) lockf(netns_storage_socket[0], F_ULOCK, 0);
(void) lockf(ns_storage_socket[0], F_ULOCK, 0);
return r;
}

View File

@ -160,8 +160,8 @@ int setup_tmp_dirs(
char **tmp_dir,
char **var_tmp_dir);
int setup_netns(const int netns_storage_socket[static 2]);
int open_netns_path(const int netns_storage_socket[static 2], const char *path);
int setup_shareable_ns(const int ns_storage_socket[static 2], unsigned long nsflag);
int open_shareable_ns_path(const int netns_storage_socket[static 2], const char *path, unsigned long nsflag);
const char* protect_home_to_string(ProtectHome p) _const_;
ProtectHome protect_home_from_string(const char *s) _pure_;

View File

@ -1547,7 +1547,7 @@ static int socket_address_listen_in_cgroup(
if (s->exec_context.network_namespace_path &&
s->exec_runtime &&
s->exec_runtime->netns_storage_socket[0] >= 0) {
r = open_netns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path);
r = open_shareable_ns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path, CLONE_NEWNET);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path);
}
@ -1568,7 +1568,7 @@ static int socket_address_listen_in_cgroup(
s->exec_runtime->netns_storage_socket[0] >= 0) {
if (ns_type_supported(NAMESPACE_NET)) {
r = setup_netns(s->exec_runtime->netns_storage_socket);
r = setup_shareable_ns(s->exec_runtime->netns_storage_socket, CLONE_NEWNET);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to join network namespace: %m");
_exit(EXIT_NETWORK);

View File

@ -4563,7 +4563,7 @@ static int run_container(
if (child_netns_fd < 0)
return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path);
r = fd_is_network_ns(child_netns_fd);
r = fd_is_ns(child_netns_fd, CLONE_NEWNET);
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)

View File

@ -69,3 +69,11 @@ int namespace_flags_to_string(unsigned long flags, char **ret) {
return 0;
}
const char *namespace_single_flag_to_string(unsigned long flag) {
for (unsigned i = 0; namespace_flag_map[i].name; i++)
if (namespace_flag_map[i].flag == flag)
return namespace_flag_map[i].name;
return NULL;
}

View File

@ -20,6 +20,7 @@
int namespace_flags_from_string(const char *name, unsigned long *ret);
int namespace_flags_to_string(unsigned long flags, char **ret);
const char *namespace_single_flag_to_string(unsigned long flag);
struct namespace_flag_map {
unsigned long flag;

View File

@ -63,7 +63,7 @@ static void test_tmpdir(const char *id, const char *A, const char *B) {
}
}
static void test_netns(void) {
static void test_shareable_ns(unsigned long nsflag) {
_cleanup_close_pair_ int s[2] = { -1, -1 };
pid_t pid1, pid2, pid3;
int r, n = 0;
@ -80,7 +80,7 @@ static void test_netns(void) {
assert_se(pid1 >= 0);
if (pid1 == 0) {
r = setup_netns(s);
r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0);
_exit(r);
}
@ -89,7 +89,7 @@ static void test_netns(void) {
assert_se(pid2 >= 0);
if (pid2 == 0) {
r = setup_netns(s);
r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0);
exit(r);
}
@ -98,7 +98,7 @@ static void test_netns(void) {
assert_se(pid3 >= 0);
if (pid3 == 0) {
r = setup_netns(s);
r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0);
exit(r);
}
@ -121,6 +121,14 @@ static void test_netns(void) {
assert_se(n == 1);
}
static void test_netns(void) {
test_shareable_ns(CLONE_NEWNET);
}
static void test_ipcns(void) {
test_shareable_ns(CLONE_NEWIPC);
}
static void test_protect_kernel_logs(void) {
int r;
pid_t pid;
@ -224,6 +232,7 @@ int main(int argc, char *argv[]) {
test_tmpdir("sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device", z, zz);
test_netns();
test_ipcns();
test_protect_kernel_logs();
return EXIT_SUCCESS;

View File

@ -3,6 +3,7 @@
#include <fcntl.h>
#include <linux/magic.h>
#include <unistd.h>
#include <sched.h>
#include "alloc-util.h"
#include "fd-util.h"
@ -67,18 +68,22 @@ static void test_path_is_temporary_fs(void) {
assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
}
static void test_fd_is_network_ns(void) {
static void test_fd_is_ns(void) {
_cleanup_close_ int fd = -1;
assert_se(fd_is_network_ns(STDIN_FILENO) == 0);
assert_se(fd_is_network_ns(STDERR_FILENO) == 0);
assert_se(fd_is_network_ns(STDOUT_FILENO) == 0);
assert_se(fd_is_ns(STDIN_FILENO, CLONE_NEWNET) == 0);
assert_se(fd_is_ns(STDERR_FILENO, CLONE_NEWNET) == 0);
assert_se(fd_is_ns(STDOUT_FILENO, CLONE_NEWNET) == 0);
assert_se((fd = open("/proc/self/ns/mnt", O_CLOEXEC|O_RDONLY)) >= 0);
assert_se(IN_SET(fd_is_network_ns(fd), 0, -EUCLEAN));
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 0, -EUCLEAN));
fd = safe_close(fd);
assert_se((fd = open("/proc/self/ns/ipc", O_CLOEXEC|O_RDONLY)) >= 0);
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWIPC), 1, -EUCLEAN));
fd = safe_close(fd);
assert_se((fd = open("/proc/self/ns/net", O_CLOEXEC|O_RDONLY)) >= 0);
assert_se(IN_SET(fd_is_network_ns(fd), 1, -EUCLEAN));
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 1, -EUCLEAN));
}
static void test_device_major_minor_valid(void) {
@ -159,7 +164,7 @@ int main(int argc, char *argv[]) {
test_is_symlink();
test_path_is_fs_type();
test_path_is_temporary_fs();
test_fd_is_network_ns();
test_fd_is_ns();
test_device_major_minor_valid();
test_device_path_make_canonical();