diff options
Diffstat (limited to 'src/platform/nmp-netns.c')
| -rw-r--r-- | src/platform/nmp-netns.c | 764 |
1 files changed, 0 insertions, 764 deletions
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c deleted file mode 100644 index 07f7a7c9..00000000 --- a/src/platform/nmp-netns.c +++ /dev/null @@ -1,764 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nmp-netns.h" - -#include <fcntl.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <pthread.h> - -/*****************************************************************************/ - -/* NOTE: NMPNetns and all code used here must be thread-safe! */ - -/* we may not call logging functions from the main-thread alone. Hence, we - * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD - * to zero. */ -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - -/*****************************************************************************/ - -#define PROC_SELF_NS_MNT "/proc/self/ns/mnt" -#define PROC_SELF_NS_NET "/proc/self/ns/net" - -#define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET)) -#define _CLONE_NS_ALL_V CLONE_NEWNS, CLONE_NEWNET - -static NM_UTILS_FLAGS2STR_DEFINE(_clone_ns_to_str, - int, - NM_UTILS_FLAGS2STR(CLONE_NEWNS, "mnt"), - NM_UTILS_FLAGS2STR(CLONE_NEWNET, "net"), ); - -static const char * -__ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) -{ - const char *b = buf; - char bb[200]; - - nm_utils_strbuf_append_c(&buf, &len, '['); - if (ns_types & ~ns_types_already_set) { - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ~ns_types_already_set, bb, sizeof(bb))); - } - if (ns_types & ns_types_already_set) { - if (ns_types & ~ns_types_already_set) - nm_utils_strbuf_append_c(&buf, &len, '/'); - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ns_types_already_set, bb, sizeof(bb))); - } - nm_utils_strbuf_append_c(&buf, &len, ']'); - return b; -} -#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \ - __ns_types_to_str(ns_types, ns_types_already_set, buf, sizeof(buf)) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FD_NET, PROP_FD_MNT, ); - -typedef struct { - int fd_net; - int fd_mnt; -} NMPNetnsPrivate; - -struct _NMPNetns { - GObject parent; - NMPNetnsPrivate _priv; -}; - -struct _NMPNetnsClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMPNetns, nmp_netns, G_TYPE_OBJECT); - -#define NMP_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPNetns, NMP_IS_NETNS) - -/*****************************************************************************/ - -typedef struct { - NMPNetns *netns; - int count; - int ns_types; -} NetnsInfo; - -static void _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types); -static NMPNetns *_netns_new(GError **error); - -/*****************************************************************************/ - -static NMPNetns * -_netns_get(NetnsInfo *info) -{ - nm_assert(!info || NMP_IS_NETNS(info->netns)); - return info ? info->netns : NULL; -} - -/*****************************************************************************/ - -static _nm_thread_local GArray *_netns_stack = NULL; - -static void -_netns_stack_clear_cb(gpointer data) -{ - NetnsInfo *info = data; - - nm_assert(NMP_IS_NETNS(info->netns)); - g_object_unref(info->netns); -} - -static GArray * -_netns_stack_get_impl(void) -{ - gs_unref_object NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - pthread_key_t key; - GArray * s; - - s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); - g_array_set_clear_func(s, _netns_stack_clear_cb); - _netns_stack = s; - - /* at the bottom of the stack we must try to create a netns instance - * that we never pop. It's the base to which we need to return. */ - netns = _netns_new(&error); - if (!netns) { - _LOGE(NULL, "failed to create initial netns: %s", error->message); - return s; - } - - /* we leak this instance inside the stack. */ - _stack_push(s, netns, _CLONE_NS_ALL); - - /* finally, register a destructor function to cleanup the array. If we fail - * to do so, we will leak NMPNetns instances (and their file descriptor) when the - * thread exits. */ - if (pthread_key_create(&key, (void (*)(void *)) g_array_unref) != 0) - _LOGE(NULL, "failure to initialize thread-local storage"); - else if (pthread_setspecific(key, s) != 0) - _LOGE(NULL, "failure to set thread-local storage"); - - return s; -} - -#define _netns_stack_get() \ - ({ \ - GArray *_s = _netns_stack; \ - \ - if (G_UNLIKELY(!_s)) \ - _s = _netns_stack_get_impl(); \ - _s; \ - }) - -/*****************************************************************************/ - -static NMPNetns * -_stack_current_netns(GArray *netns_stack, int ns_types) -{ - guint j; - - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to find the netns that has - * all @ns_types set. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - - if (NM_FLAGS_ALL(info->ns_types, ns_types)) - return info->netns; - } - - g_return_val_if_reached(NULL); -} - -static int -_stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - const int ns_types_check[] = {_CLONE_NS_ALL_V}; - guint i, j; - int res = 0; - - nm_assert(netns); - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to check which of @ns_types - * are already set to @netns. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - if (info->netns != netns) { - ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); - continue; - } - - for (i = 0; i < G_N_ELEMENTS(ns_types_check); i++) { - if (NM_FLAGS_ANY(ns_types, ns_types_check[i]) - && NM_FLAGS_ANY(info->ns_types, ns_types_check[i])) { - res = NM_FLAGS_SET(res, ns_types_check[i]); - ns_types = NM_FLAGS_UNSET(ns_types, ns_types_check[i]); - } - } - } - - return res; -} - -static NetnsInfo * -_stack_peek(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - return NULL; -} - -static NetnsInfo * -_stack_bottom(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); - return NULL; -} - -static void -_stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(NMP_IS_NETNS(netns)); - nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); - nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - *info = (NetnsInfo){ - .netns = g_object_ref(netns), - .ns_types = ns_types, - .count = 1, - }; -} - -static void -_stack_pop(GArray *netns_stack) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(netns_stack->len > 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - - nm_assert(NMP_IS_NETNS(info->netns)); - nm_assert(info->count == 1); - - g_array_set_size(netns_stack, netns_stack->len - 1); -} - -static guint -_stack_size(GArray *netns_stack) -{ - nm_assert(netns_stack); - - return netns_stack->len; -} - -/*****************************************************************************/ - -static NMPNetns * -_netns_new(GError **error) -{ - NMPNetns *self; - int fd_net, fd_mnt; - int errsv; - - fd_net = open(PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); - if (fd_net == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening netns: %s", - nm_strerror_native(errsv)); - errno = errsv; - return NULL; - } - - fd_mnt = open(PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); - if (fd_mnt == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening mntns: %s", - nm_strerror_native(errsv)); - nm_close(fd_net); - errno = errsv; - return NULL; - } - - self = g_object_new(NMP_TYPE_NETNS, NMP_NETNS_FD_NET, fd_net, NMP_NETNS_FD_MNT, fd_mnt, NULL); - - _LOGD(self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt); - - return self; -} - -static int -_setns(NMPNetns *self, int type) -{ - char buf[100]; - int fd; - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_assert(NM_IN_SET(type, _CLONE_NS_ALL_V)); - - fd = (type == CLONE_NEWNET) ? priv->fd_net : priv->fd_mnt; - - _LOGt(self, "set netns(%s, %d)", _ns_types_to_str(type, 0, buf), fd); - - return setns(fd, type); -} - -static gboolean -_netns_switch_push(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET) - && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - return FALSE; - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS) - && _setns(self, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - - /* try to fix the mess by returning to the previous netns. */ - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET)) { - self = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (self && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to restore netns: %s", nm_strerror_native(errsv)); - } - } - return FALSE; - } - - return TRUE; -} - -static gboolean -_netns_switch_pop(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - NMPNetns *current; - int success = TRUE; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNS); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - - return success; -} - -/*****************************************************************************/ - -int -nmp_netns_get_fd_net(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_net; -} - -int -nmp_netns_get_fd_mnt(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_mnt; -} - -/*****************************************************************************/ - -static gboolean -_nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - char sbuf[100]; - - info = _stack_peek(netns_stack); - g_return_val_if_fail(info, FALSE); - - if (info->netns == self && info->ns_types == ns_types) { - info->count++; - _LOGt(self, - "push#%u* %s (increase count to %d)", - _stack_size(netns_stack) - 1, - _ns_types_to_str(ns_types, ns_types, sbuf), - info->count); - return TRUE; - } - - _LOGD(self, - "push#%u %s", - _stack_size(netns_stack), - _ns_types_to_str(ns_types, _stack_current_ns_types(netns_stack, self, ns_types), sbuf)); - - if (!_netns_switch_push(netns_stack, self, ns_types)) - return FALSE; - - _stack_push(netns_stack, self, ns_types); - return TRUE; -} - -gboolean -nmp_netns_push(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - return _nmp_netns_push_type(self, _CLONE_NS_ALL); -} - -gboolean -nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL), FALSE); - - return _nmp_netns_push_type(self, ns_types == 0 ? _CLONE_NS_ALL : ns_types); -} - -NMPNetns * -nmp_netns_new(void) -{ - GArray * netns_stack = _netns_stack_get(); - NMPNetns * self; - int errsv; - GError * error = NULL; - unsigned long mountflags = 0; - - if (!_stack_peek(netns_stack)) { - /* there are no netns instances. We cannot create a new one - * (because after unshare we couldn't return to the original one). */ - errno = ENOTSUP; - return NULL; - } - - if (unshare(_CLONE_NS_ALL) != 0) { - errsv = errno; - _LOGE(NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native(errsv)); - return NULL; - } - - if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount --make-rslave: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (umount2("/sys", MNT_DETACH) != 0) { - errsv = errno; - _LOGE(NULL, "failed umount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (access("/sys", W_OK) == -1) - mountflags = MS_RDONLY; - - if (mount("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - self = _netns_new(&error); - if (!self) { - errsv = errno; - _LOGE(NULL, "failed to create netns after unshare: %s", error->message); - g_clear_error(&error); - goto err_out; - } - - _stack_push(netns_stack, self, _CLONE_NS_ALL); - - return self; -err_out: - _netns_switch_pop(netns_stack, NULL, _CLONE_NS_ALL); - errno = errsv; - return NULL; -} - -gboolean -nmp_netns_pop(NMPNetns *self) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - int ns_types; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - info = _stack_peek(netns_stack); - - g_return_val_if_fail(info, FALSE); - g_return_val_if_fail(info->netns == self, FALSE); - - if (info->count > 1) { - info->count--; - _LOGt(self, "pop#%u* (decrease count to %d)", _stack_size(netns_stack) - 1, info->count); - return TRUE; - } - g_return_val_if_fail(info->count == 1, FALSE); - - /* cannot pop the original netns. */ - g_return_val_if_fail(_stack_size(netns_stack) > 1, FALSE); - - _LOGD(self, "pop#%u", _stack_size(netns_stack) - 1); - - ns_types = info->ns_types; - - _stack_pop(netns_stack); - - return _netns_switch_pop(netns_stack, self, ns_types); -} - -NMPNetns * -nmp_netns_get_current(void) -{ - return _netns_get(_stack_peek(_netns_stack_get())); -} - -NMPNetns * -nmp_netns_get_initial(void) -{ - return _netns_get(_stack_bottom(_netns_stack_get())); -} - -gboolean -nmp_netns_is_initial(void) -{ - GArray *netns_stack = _netns_stack_get(); - - return (_netns_get(_stack_peek(netns_stack)) == _netns_get(_stack_bottom(netns_stack))); -} - -/*****************************************************************************/ - -gboolean -nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd) -{ - gs_free char * dirname = NULL; - int errsv; - int fd; - nm_auto_pop_netns NMPNetns *netns_pop = NULL; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (!nmp_netns_push_type(self, CLONE_NEWNET)) - return FALSE; - netns_pop = self; - - dirname = g_path_get_dirname(filename); - if (mkdir(dirname, 0) != 0) { - errsv = errno; - if (errsv != EEXIST) { - _LOGE(self, - "bind: failed to create directory %s: %s", - dirname, - nm_strerror_native(errsv)); - return FALSE; - } - } - - if ((fd = creat(filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to create %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - nm_close(fd); - - if (mount(PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { - errsv = errno; - _LOGE(self, - "bind: failed to mount %s to %s: %s", - PROC_SELF_NS_NET, - filename, - nm_strerror_native(errsv)); - unlink(filename); - return FALSE; - } - - if (out_fd) { - if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to open %s: %s", filename, nm_strerror_native(errsv)); - umount2(filename, MNT_DETACH); - unlink(filename); - return FALSE; - } - *out_fd = fd; - } - - return TRUE; -} - -gboolean -nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename) -{ - int errsv; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (umount2(filename, MNT_DETACH) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - if (unlink(filename) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unlink %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_FD_NET: - /* construct-only */ - priv->fd_net = g_value_get_int(value); - g_return_if_fail(priv->fd_net > 0); - break; - case PROP_FD_MNT: - /* construct-only */ - priv->fd_mnt = g_value_get_int(value); - g_return_if_fail(priv->fd_mnt > 0); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nmp_netns_init(NMPNetns *self) -{} - -static void -dispose(GObject *object) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_close(priv->fd_net); - priv->fd_net = -1; - - nm_close(priv->fd_mnt); - priv->fd_mnt = -1; - - G_OBJECT_CLASS(nmp_netns_parent_class)->dispose(object); -} - -static void -nmp_netns_class_init(NMPNetnsClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - - obj_properties[PROP_FD_NET] = - g_param_spec_int(NMP_NETNS_FD_NET, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_FD_MNT] = - g_param_spec_int(NMP_NETNS_FD_MNT, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} |