summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2016-03-14 14:20:33 +0100
committerThomas Haller <thaller@redhat.com>2016-03-15 12:56:58 +0100
commit3428d8607d8462223e09ff703ac9c86fd7a102cc (patch)
tree1eb78bf7c0306c482bcdfe6cd20cb16f98b6fbfa
parenta0cce2b1956ae6292bffb102c0c9a4c33cdfbdbc (diff)
downloadNetworkManager-3428d8607d8462223e09ff703ac9c86fd7a102cc.tar.gz
platform: support switching partial namespaces
Previously, the push/pop API to switch between namespaces would always switch both the net and mount namespace together. There are situations, where we want to only switch one namespace. For example, the function nmp_netns_bind_to_path() introduced next only wants to switch the net namespace to get /proc/self/ns/net, but must not switch the mount namespace as it bind-mounds in the namespace of the caller.
-rw-r--r--src/platform/nmp-netns.c233
-rw-r--r--src/platform/nmp-netns.h1
-rw-r--r--src/platform/tests/test-link.c194
3 files changed, 386 insertions, 42 deletions
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index d24faafc9f..5b1d6ad7e5 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -30,6 +30,37 @@
#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
+
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_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
@@ -67,9 +98,10 @@ struct _NMPNetnsPrivate {
typedef struct {
NMPNetns *netns;
int count;
+ int ns_types;
} NetnsInfo;
-static void _stack_push (NMPNetns *netns);
+static void _stack_push (NMPNetns *netns, int ns_types);
static NMPNetns *_netns_new (GError **error);
/*********************************************************************************************/
@@ -98,7 +130,7 @@ _stack_ensure_init_impl (void)
return;
}
- _stack_push (netns);
+ _stack_push (netns, _CLONE_NS_ALL);
/* we leak this instance inside netns_stack. It cannot be popped. */
g_object_unref (netns);
@@ -110,23 +142,67 @@ _stack_ensure_init_impl (void)
} \
} G_STMT_END
-static NetnsInfo *
-_stack_peek (void)
+static NMPNetns *
+_stack_current_netns (int ns_types)
{
- nm_assert (netns_stack);
+ guint j;
- if (netns_stack->len > 0)
- return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
- return NULL;
+ 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 (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_HAS (ns_types, ns_types_check[i])
+ && NM_FLAGS_HAS (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_peek2 (void)
+_stack_peek (void)
{
nm_assert (netns_stack);
- if (netns_stack->len > 1)
- return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 2));
+ if (netns_stack->len > 0)
+ return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
return NULL;
}
@@ -141,17 +217,20 @@ _stack_bottom (void)
}
static void
-_stack_push (NMPNetns *netns)
+_stack_push (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->netns = g_object_ref (netns);
+ info->ns_types = ns_types;
info->count = 1;
}
@@ -225,25 +304,47 @@ _netns_new (GError **error)
return self;
}
+static int
+_setns (NMPNetns *self, int type)
+{
+ char buf[100];
+ int fd;
+
+ nm_assert (NM_IN_SET (type, _CLONE_NS_ALL_V));
+
+ fd = (type == CLONE_NEWNET) ? self->priv->fd_net : self->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 (NMPNetns *self, NMPNetns *netns_fail)
+_netns_switch_push (NMPNetns *self, int ns_types)
{
int errsv;
- if (setns (self->priv->fd_net, CLONE_NEWNET) != 0) {
+ if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
+ && !_stack_current_ns_types (self, CLONE_NEWNET)
+ && _setns (self, CLONE_NEWNET) != 0) {
errsv = errno;
_LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
return FALSE;
}
- if (setns (self->priv->fd_mnt, CLONE_NEWNS) != 0) {
+ if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
+ && !_stack_current_ns_types (self, CLONE_NEWNS)
+ && _setns (self, CLONE_NEWNS) != 0) {
errsv = errno;
_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
/* try to fix the mess by returning to the previous netns. */
- if (netns_fail) {
- if (setns (netns_fail->priv->fd_net, CLONE_NEWNET) != 0) {
+ if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
+ && !_stack_current_ns_types (self, CLONE_NEWNET)) {
+ self = _stack_current_netns (CLONE_NEWNET);
+ if ( self
+ && _setns (self, CLONE_NEWNET) != 0) {
errsv = errno;
- _LOGE (netns_fail, "failed to restore netns: %s", g_strerror (errsv));
+ _LOGE (self, "failed to restore netns: %s", g_strerror (errsv));
}
}
return FALSE;
@@ -252,6 +353,41 @@ _netns_switch (NMPNetns *self, NMPNetns *netns_fail)
return TRUE;
}
+static gboolean
+_netns_switch_pop (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 (self, CLONE_NEWNET))) {
+ current = _stack_current_netns (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", g_strerror (errsv));
+ success = FALSE;
+ }
+ }
+ if ( NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
+ && (!self || !_stack_current_ns_types (self, CLONE_NEWNS))) {
+ current = _stack_current_netns (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", g_strerror (errsv));
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
/*********************************************************************************************/
int
@@ -272,37 +408,58 @@ nmp_netns_get_fd_mnt (NMPNetns *self)
/*********************************************************************************************/
-gboolean
-nmp_netns_push (NMPNetns *self)
+static gboolean
+_nmp_netns_push_type (NMPNetns *self, int ns_types)
{
NetnsInfo *info;
-
- g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+ char sbuf[100];
_stack_ensure_init ();
info = _stack_peek ();
g_return_val_if_fail (info, FALSE);
- if (info->netns == self) {
+ if (info->netns == self && info->ns_types == ns_types) {
info->count++;
- _LOGt (self, "push (increase count to %d)", info->count);
+ _LOGt (self, "push#%u* %s (increase count to %d)",
+ _stack_size () - 1,
+ _ns_types_to_str (ns_types, ns_types, sbuf), info->count);
return TRUE;
}
- _LOGD (self, "push (was %p)", info->netns);
+ _LOGD (self, "push#%u %s",
+ _stack_size (),
+ _ns_types_to_str (ns_types,
+ _stack_current_ns_types (self, ns_types),
+ sbuf));
- if (!_netns_switch (self, info->netns))
+ if (!_netns_switch_push (self, ns_types))
return FALSE;
- _stack_push (self);
+ _stack_push (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)
{
- NetnsInfo *info;
NMPNetns *self;
int errsv;
GError *error = NULL;
@@ -315,7 +472,7 @@ nmp_netns_new (void)
return NULL;
}
- if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) {
+ if (unshare (_CLONE_NS_ALL) != 0) {
errsv = errno;
_LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
return NULL;
@@ -346,12 +503,11 @@ nmp_netns_new (void)
goto err_out;
}
- _stack_push (self);
+ _stack_push (self, _CLONE_NS_ALL);
return self;
err_out:
- info = _stack_peek ();
- _netns_switch (info->netns, NULL);
+ _netns_switch_pop (NULL, _CLONE_NS_ALL);
return NULL;
}
@@ -359,6 +515,7 @@ gboolean
nmp_netns_pop (NMPNetns *self)
{
NetnsInfo *info;
+ int ns_types;
g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
@@ -371,7 +528,8 @@ nmp_netns_pop (NMPNetns *self)
if (info->count > 1) {
info->count--;
- _LOGt (self, "pop (decrease count to %d)", info->count);
+ _LOGt (self, "pop#%u* (decrease count to %d)",
+ _stack_size () - 1, info->count);
return TRUE;
}
g_return_val_if_fail (info->count == 1, FALSE);
@@ -379,14 +537,13 @@ nmp_netns_pop (NMPNetns *self)
/* cannot pop the original netns. */
g_return_val_if_fail (_stack_size () > 1, FALSE);
- _LOGD (self, "pop (restore %p)", _stack_peek2 ());
+ _LOGD (self, "pop#%u", _stack_size () - 1);
- _stack_pop ();
- info = _stack_peek ();
+ ns_types = info->ns_types;
- nm_assert (info);
+ _stack_pop ();
- return _netns_switch (info->netns, NULL);
+ return _netns_switch_pop (self, ns_types);
}
NMPNetns *
diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h
index bdd797daca..1625116f2e 100644
--- a/src/platform/nmp-netns.h
+++ b/src/platform/nmp-netns.h
@@ -49,6 +49,7 @@ 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);
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 9da2404ddc..f7b9f6fb64 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1951,13 +1951,13 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i));
}
- g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_1, "dummy1_")->ifindex));
- g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_1, "dummy2a")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex));
g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, NULL);
- g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_2, "dummy1_")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, NULL);
- g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nm_platform_link_get_by_ifname (platform_2, "dummy2b")->ifindex));
+ g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex));
for (i = 0; i < 10; i++) {
NMPlatform *pl;
@@ -2051,6 +2051,191 @@ test_netns_set_netns (gpointer fixture, gconstpointer test_data)
/*****************************************************************************/
+static char *
+_get_current_namespace_id (int ns_type)
+{
+ const char *p;
+ GError *error = NULL;
+ char *id;
+
+ switch (ns_type) {
+ case CLONE_NEWNET:
+ p = "/proc/self/ns/net";
+ break;
+ case CLONE_NEWNS:
+ p = "/proc/self/ns/mnt";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ id = g_file_read_link (p, &error);
+ g_assert_no_error (error);
+ g_assert (id);
+ return id;
+}
+
+static char *
+_get_sysctl_value (const char *path)
+{
+ char *data = NULL;
+ gs_free_error GError *error = NULL;
+
+ if (!g_file_get_contents (path, &data, NULL, &error)) {
+ nmtst_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, NULL);
+ g_assert (!data);
+ } else {
+ g_assert_no_error (error);
+ g_assert (data);
+ g_strstrip (data);
+ }
+ return data;
+}
+
+static void
+test_netns_push (gpointer fixture, gconstpointer test_data)
+{
+ gs_unref_object NMPlatform *platform_0 = NULL;
+ gs_unref_object NMPlatform *platform_1 = NULL;
+ gs_unref_object NMPlatform *platform_2 = NULL;
+ nm_auto_pop_netns NMPNetns *netns_pop = NULL;
+ gs_unref_ptrarray GPtrArray *device_names = g_ptr_array_new_with_free_func (g_free);
+ int i, j;
+ const int ns_types_list[] = { CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWNET | CLONE_NEWNS };
+ const int ns_types_test[] = { CLONE_NEWNET, CLONE_NEWNS };
+ typedef struct {
+ NMPlatform *platform;
+ const char *device_name;
+ const char *sysctl_path;
+ const char *sysctl_value;
+ const char *ns_net;
+ const char *ns_mnt;
+ } PlatformData;
+ PlatformData pl[3] = { };
+ PlatformData *pl_base;
+ struct {
+ PlatformData *pl;
+ int ns_types;
+ } stack[6] = { };
+ int nstack;
+
+ if (_test_netns_check_skip ())
+ return;
+
+ pl[0].platform = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+ pl[1].platform = platform_1 = _test_netns_create_platform ();
+ pl[2].platform = platform_2 = _test_netns_create_platform ();
+
+ pl_base = &pl[0];
+ i = nmtst_get_rand_int () % (G_N_ELEMENTS (pl) + 1);
+ if (i < G_N_ELEMENTS (pl)) {
+ pl_base = &pl[i];
+ g_assert (nm_platform_netns_push (pl[i].platform, &netns_pop));
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (pl); i++) {
+ nm_auto_pop_netns NMPNetns *netns_free = NULL;
+ char *tmp;
+
+ g_assert (nm_platform_netns_push (pl[i].platform, &netns_free));
+
+ tmp = g_strdup_printf ("nmtst-dev-%d", i);
+ g_ptr_array_add (device_names, tmp);
+ pl[i].device_name = tmp;
+
+ tmp = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6", pl[i].device_name);
+ g_ptr_array_add (device_names, tmp);
+ pl[i].sysctl_path = tmp;
+
+ pl[i].sysctl_value = nmtst_get_rand_int () % 2 ? "1" : "0";
+
+ _ADD_DUMMY (pl[i].platform, pl[i].device_name);
+
+ g_assert (nm_platform_sysctl_set (pl[i].platform, pl[i].sysctl_path, pl[i].sysctl_value));
+
+ tmp = _get_current_namespace_id (CLONE_NEWNET);
+ g_ptr_array_add (device_names, tmp);
+ pl[i].ns_net = tmp;
+
+ tmp = _get_current_namespace_id (CLONE_NEWNS);
+ g_ptr_array_add (device_names, tmp);
+ pl[i].ns_mnt = tmp;
+ }
+
+ nstack = nmtst_get_rand_int () % (G_N_ELEMENTS (stack) + 1);
+ for (i = 0; i < nstack; i++) {
+ stack[i].pl = &pl[nmtst_get_rand_int () % G_N_ELEMENTS (pl)];
+ stack[i].ns_types = ns_types_list[nmtst_get_rand_int () % G_N_ELEMENTS (ns_types_list)];
+
+ nmp_netns_push_type (nm_platform_netns_get (stack[i].pl->platform), stack[i].ns_types);
+ }
+
+ /* pop some again. */
+ for (i = nmtst_get_rand_int () % (nstack + 1); i > 0; i--) {
+ g_assert (nstack > 0);
+ nstack--;
+ nmp_netns_pop (nm_platform_netns_get (stack[nstack].pl->platform));
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (ns_types_test); i++) {
+ int ns_type = ns_types_test[i];
+ PlatformData *p;
+ gs_free char *current_namespace_id = NULL;
+
+ p = pl_base;
+ for (j = nstack; j >= 1; ) {
+ j--;
+ if (NM_FLAGS_HAS (stack[j].ns_types, ns_type)) {
+ p = stack[j].pl;
+ break;
+ }
+ }
+
+ current_namespace_id = _get_current_namespace_id (ns_type);
+
+ if (ns_type == CLONE_NEWNET) {
+ g_assert_cmpstr (current_namespace_id, ==, p->ns_net);
+ for (j = 0; j < G_N_ELEMENTS (pl); j++) {
+ gs_free char *data = NULL;
+
+ if (p == &pl[j])
+ g_assert_cmpint (nmtstp_run_command ("ip link show %s 1>/dev/null", pl[j].device_name), ==, 0);
+ else
+ g_assert_cmpint (nmtstp_run_command ("ip link show %s 2>/dev/null", pl[j].device_name), !=, 0);
+
+ data = _get_sysctl_value (pl[j].sysctl_path);
+ if (p == &pl[j])
+ g_assert_cmpstr (data, ==, pl[j].sysctl_value);
+ else
+ g_assert (!data);
+ }
+ } else if (ns_type == CLONE_NEWNS) {
+ g_assert_cmpstr (current_namespace_id, ==, p->ns_mnt);
+ for (j = 0; j < G_N_ELEMENTS (pl); j++) {
+ char path[600];
+ gs_free char *data = NULL;
+
+ nm_sprintf_buf (path, "/sys/devices/virtual/net/%s/ifindex", pl[j].device_name);
+
+ data = _get_sysctl_value (path);
+ if (p == &pl[j])
+ g_assert_cmpstr (data, ==, nm_sprintf_buf (path, "%d", nmtstp_link_get_typed (p->platform, 0, p->device_name, NM_LINK_TYPE_DUMMY)->ifindex));
+ else
+ g_assert (!data);
+ }
+ } else
+ g_assert_not_reached ();
+ }
+
+
+ for (i = nstack; i >= 1; ) {
+ i--;
+ nmp_netns_pop (nm_platform_netns_get (stack[i].pl->platform));
+ }
+}
+
+/*****************************************************************************/
+
void
init_tests (int *argc, char ***argv)
{
@@ -2100,5 +2285,6 @@ setup_tests (void)
g_test_add_vtable ("/general/netns/general", 0, NULL, _test_netns_setup, test_netns_general, _test_netns_teardown);
g_test_add_vtable ("/general/netns/set-netns", 0, NULL, _test_netns_setup, test_netns_set_netns, _test_netns_teardown);
+ g_test_add_vtable ("/general/netns/push", 0, NULL, _test_netns_setup, test_netns_push, _test_netns_teardown);
}
}