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:
parent
a959cd2812
commit
54c2459d56
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user