diff options
author | Thomas Haller <thaller@redhat.com> | 2020-12-25 17:54:46 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2021-01-15 11:32:31 +0100 |
commit | 31dca65e04321c120c4166bf5036bb08aa809028 (patch) | |
tree | 8e5a6b95560b0123ab3afad4c2be8c6dac6654ad /shared | |
parent | 24c634bf573880786e709c7ddfab01ebaec7eaa7 (diff) | |
download | NetworkManager-31dca65e04321c120c4166bf5036bb08aa809028.tar.gz |
shared,platform: move "nmp-netns.[hc]" to shared/nm-platform
Diffstat (limited to 'shared')
-rw-r--r-- | shared/meson.build | 1 | ||||
-rw-r--r-- | shared/nm-platform/nmp-base.h | 8 | ||||
-rw-r--r-- | shared/nm-platform/nmp-netns.c | 766 | ||||
-rw-r--r-- | shared/nm-platform/nmp-netns.h | 56 | ||||
-rw-r--r-- | shared/nm-platform/tests/meson.build | 1 | ||||
-rw-r--r-- | shared/nm-platform/tests/test-nm-platform.c | 23 |
6 files changed, 854 insertions, 1 deletions
diff --git a/shared/meson.build b/shared/meson.build index fda44444e7..b19535f078 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -207,6 +207,7 @@ libnm_platform = static_library( 'nm-platform', sources: [ 'nm-platform/nm-netlink.c', + 'nm-platform/nmp-netns.c', ], dependencies: [ glib_nm_default_dep, diff --git a/shared/nm-platform/nmp-base.h b/shared/nm-platform/nmp-base.h new file mode 100644 index 0000000000..f4e50dd0b5 --- /dev/null +++ b/shared/nm-platform/nmp-base.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __NMP_FWD_H__ +#define __NMP_FWD_H__ + +typedef struct _NMPNetns NMPNetns; + +#endif /* __NMP_FWD_H__ */ diff --git a/shared/nm-platform/nmp-netns.c b/shared/nm-platform/nmp-netns.c new file mode 100644 index 0000000000..01aefde66a --- /dev/null +++ b/shared/nm-platform/nmp-netns.c @@ -0,0 +1,766 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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> + +#include "nm-log-core/nm-logging.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); +} diff --git a/shared/nm-platform/nmp-netns.h b/shared/nm-platform/nmp-netns.h new file mode 100644 index 0000000000..b18bd03e76 --- /dev/null +++ b/shared/nm-platform/nmp-netns.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NMP_NETNS_UTILS_H__ +#define __NMP_NETNS_UTILS_H__ + +#include "nmp-base.h" + +/*****************************************************************************/ + +#define NMP_TYPE_NETNS (nmp_netns_get_type()) +#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) +#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) +#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) +#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) +#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NMP_TYPE_NETNS, NMPNetnsClass)) + +#define NMP_NETNS_FD_NET "fd-net" +#define NMP_NETNS_FD_MNT "fd-mnt" + +typedef struct _NMPNetns NMPNetns; +typedef struct _NMPNetnsClass NMPNetnsClass; + +GType nmp_netns_get_type(void); + +NMPNetns *nmp_netns_new(void); + +gboolean nmp_netns_push(NMPNetns *self); +gboolean nmp_netns_push_type(NMPNetns *self, int ns_types); +gboolean nmp_netns_pop(NMPNetns *self); + +NMPNetns *nmp_netns_get_current(void); +NMPNetns *nmp_netns_get_initial(void); +gboolean nmp_netns_is_initial(void); + +int nmp_netns_get_fd_net(NMPNetns *self); +int nmp_netns_get_fd_mnt(NMPNetns *self); + +static inline void +_nm_auto_pop_netns(NMPNetns **p) +{ + if (*p) { + int errsv = errno; + + nmp_netns_pop(*p); + errno = errsv; + } +} +#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) + +gboolean nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd); +gboolean nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename); + +#endif /* __NMP_NETNS_UTILS_H__ */ diff --git a/shared/nm-platform/tests/meson.build b/shared/nm-platform/tests/meson.build index 47175331fe..8d2b9c0f6e 100644 --- a/shared/nm-platform/tests/meson.build +++ b/shared/nm-platform/tests/meson.build @@ -11,7 +11,6 @@ exe = executable( libnm_log_core_dep, libnm_platform_dep, ], - link_with: libnm_systemd_logging_stub, ) test( diff --git a/shared/nm-platform/tests/test-nm-platform.c b/shared/nm-platform/tests/test-nm-platform.c index 179a3b6bb9..0386a3a546 100644 --- a/shared/nm-platform/tests/test-nm-platform.c +++ b/shared/nm-platform/tests/test-nm-platform.c @@ -4,12 +4,22 @@ #include "nm-default.h" +#include "nm-log-core/nm-logging.h" #include "nm-platform/nm-netlink.h" +#include "nm-platform/nmp-netns.h" #include "nm-utils/nm-test-utils.h" /*****************************************************************************/ +void +_nm_logging_clear_platform_logging_cache(void) +{ + /* this symbols is required by nm-log-core library. */ +} + +/*****************************************************************************/ + static void test_use_symbols(void) { @@ -72,6 +82,19 @@ test_use_symbols(void) (void (*)(void)) nl_send, (void (*)(void)) nl_send_auto, (void (*)(void)) nl_recv, + + (void (*)(void)) nmp_netns_bind_to_path, + (void (*)(void)) nmp_netns_bind_to_path_destroy, + (void (*)(void)) nmp_netns_get_current, + (void (*)(void)) nmp_netns_get_fd_mnt, + (void (*)(void)) nmp_netns_get_fd_net, + (void (*)(void)) nmp_netns_get_initial, + (void (*)(void)) nmp_netns_is_initial, + (void (*)(void)) nmp_netns_new, + (void (*)(void)) nmp_netns_pop, + (void (*)(void)) nmp_netns_push, + (void (*)(void)) nmp_netns_push_type, + NULL, }; |