summary refs log tree commit diff
path: root/src/platform/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/tests')
-rw-r--r--src/platform/tests/test-address.c14
-rw-r--r--src/platform/tests/test-cleanup.c2
-rw-r--r--src/platform/tests/test-common.c122
-rw-r--r--src/platform/tests/test-common.h19
-rw-r--r--src/platform/tests/test-general.c4
-rw-r--r--src/platform/tests/test-link.c501
-rw-r--r--src/platform/tests/test-nmp-object.c4
-rw-r--r--src/platform/tests/test-route.c4
8 files changed, 563 insertions, 107 deletions
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
index 3b52a7be..83a0bfd3 100644
--- a/src/platform/tests/test-address.c
+++ b/src/platform/tests/test-address.c
@@ -94,12 +94,12 @@ test_ip4_address_general (void)
 
 	/* Add address */
 	g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	accept_signal (address_added);
 
 	/* Add address again (aka update) */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, 0, NULL);
 	accept_signals (address_changed, 0, 1);
 
 	/* Test address listing */
@@ -197,7 +197,7 @@ test_ip4_address_general_2 (void)
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL));
 
 	/* Add/delete notification */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr);
@@ -205,7 +205,7 @@ test_ip4_address_general_2 (void)
 	g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 
 	/* Add/delete conflict */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	accept_signal (address_added);
 
@@ -273,7 +273,7 @@ test_ip4_address_peer (void)
 	accept_signals (address_added, 0, G_MAXINT);
 
 	/* Add/delete notification */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer);
 	g_assert (a);
@@ -281,7 +281,7 @@ test_ip4_address_peer (void)
 
 	nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred);
 
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer));
 	a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2);
@@ -328,7 +328,7 @@ test_ip4_address_peer_zero (void)
 	for (i = 0; i < G_N_ELEMENTS (peers); i++) {
 		g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i]));
 
-		nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, label);
+		nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, 0, label);
 
 		addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
 		g_assert (addrs);
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index 6ad24478..4036daad 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -63,7 +63,7 @@ test_cleanup_internal (void)
 	g_assert (ifindex > 0);
 
 	/* Add routes and addresses */
-	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, NULL));
+	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, 0, NULL));
 	g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags));
 	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss));
 	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss));
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 268a5c92..e9fffe90 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -338,30 +338,32 @@ _wait_for_signal_timeout (gpointer user_data)
 }
 
 gboolean
-nmtstp_wait_for_signal (guint timeout_ms)
+nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms)
 {
 	WaitForSignalData data = { 0 };
-
 	gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route;
 
+	if (!platform)
+		platform = NM_PLATFORM_GET;
+
 	data.loop = g_main_loop_new (NULL, FALSE);
 
-	id_link        = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip4_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip6_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip4_route   = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip6_route   = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_link        = g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip4_address = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip6_address = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip4_route   = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip6_route   = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
 
 	if (timeout_ms != 0)
 		data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data);
 
 	g_main_loop_run (data.loop);
 
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_link));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_address));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_address));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_route));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_route));
+	g_assert (nm_clear_g_signal_handler (platform, &id_link));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip4_address));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip6_address));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip4_route));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip6_route));
 
 	if (nm_clear_g_source (&data.id))
 		g_assert (timeout_ms != 0 && !data.timeout);
@@ -372,7 +374,7 @@ nmtstp_wait_for_signal (guint timeout_ms)
 }
 
 gboolean
-nmtstp_wait_for_signal_until (gint64 until_ms)
+nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms)
 {
 	gint64 now;
 
@@ -382,19 +384,19 @@ nmtstp_wait_for_signal_until (gint64 until_ms)
 		if (until_ms < now)
 			return FALSE;
 
-		if (nmtstp_wait_for_signal (MAX (1, until_ms - now)))
+		if (nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)))
 			return TRUE;
 	}
 }
 
 const NMPlatformLink *
-nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
 {
-	return nmtstp_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
+	return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
 
 const NMPlatformLink *
-nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
+nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
 {
 	const NMPlatformLink *plink;
 	gint64 now;
@@ -402,7 +404,7 @@ nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, g
 	while (TRUE) {
 		now = nm_utils_get_monotonic_timestamp_ms ();
 
-		plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
+		plink = nm_platform_link_get_by_ifname (platform ?: NM_PLATFORM_GET, ifname);
 		if (   plink
 		    && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type))
 			return plink;
@@ -410,22 +412,22 @@ nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, g
 		if (until_ms < now)
 			return NULL;
 
-		nmtstp_wait_for_signal (MAX (1, until_ms - now));
+		nmtstp_wait_for_signal (platform, MAX (1, until_ms - now));
 	}
 }
 
 const NMPlatformLink *
-nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
 {
-	return nmtstp_assert_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
+	return nmtstp_assert_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
 
 const NMPlatformLink *
-nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
+nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
 {
 	const NMPlatformLink *plink;
 
-	plink = nmtstp_wait_for_link_until (ifname, expected_link_type, until_ms);
+	plink = nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms);
 	g_assert (plink);
 	return plink;
 }
@@ -563,8 +565,8 @@ _ip_address_add (gboolean external_command,
                  const NMIPAddr *peer_address,
                  guint32 lifetime,
                  guint32 preferred,
-                 const char *label,
-                 guint32 flags)
+                 guint32 flags,
+                 const char *label)
 {
 	gint64 end_time;
 
@@ -626,7 +628,6 @@ _ip_address_add (gboolean external_command,
 		gboolean success;
 
 		if (is_v4) {
-			g_assert (flags == 0);
 			success = nm_platform_ip4_address_add (NM_PLATFORM_GET,
 			                                       ifindex,
 			                                       address->addr4,
@@ -634,6 +635,7 @@ _ip_address_add (gboolean external_command,
 			                                       peer_address->addr4,
 			                                       lifetime,
 			                                       preferred,
+			                                       flags,
 			                                       label);
 		} else {
 			g_assert (label == NULL);
@@ -686,20 +688,20 @@ _ip_address_add (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
-#define _assert_pllink(success, pllink, name, type) \
+#define _assert_pllink(platform, success, pllink, name, type) \
 	G_STMT_START { \
 		const NMPlatformLink *_pllink = (pllink); \
 		\
 		if ((success)) { \
 			g_assert (_pllink); \
-			g_assert (_pllink == nmtstp_link_get_typed (_pllink->ifindex, (name), (type))); \
+			g_assert (_pllink == nmtstp_link_get_typed (platform, _pllink->ifindex, (name), (type))); \
 		} else { \
 			g_assert (!_pllink); \
-			g_assert (!nmtstp_link_get (0, (name))); \
+			g_assert (!nmtstp_link_get (platform, 0, (name))); \
 		} \
 	} G_STMT_END
 
@@ -718,12 +720,12 @@ nmtstp_link_dummy_add (gboolean external_command,
 		success = !nmtstp_run_command ("ip link add %s type dummy",
 		                                name);
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_DUMMY, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_DUMMY, 100);
 	} else
 		success = nm_platform_link_dummy_add (NM_PLATFORM_GET, name, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
 	g_assert (success);
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_DUMMY);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_DUMMY);
 	return pllink;
 }
 
@@ -755,11 +757,11 @@ nmtstp_link_gre_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_GRE, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_GRE, 100);
 	} else
 		success = nm_platform_link_gre_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_GRE);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_GRE);
 
 	return pllink;
 }
@@ -806,11 +808,11 @@ nmtstp_link_ip6tnl_add (gboolean external_command,
 		                                lnk->encap_limit,
 		                                lnk->flow_label);
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IP6TNL, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_IP6TNL, 100);
 	} else
 		success = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_IP6TNL);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_IP6TNL);
 
 	return pllink;
 }
@@ -843,11 +845,11 @@ nmtstp_link_ipip_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IPIP, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_IPIP, 100);
 	} else
 		success = nm_platform_link_ipip_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_IPIP);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_IPIP);
 
 	return pllink;
 }
@@ -888,11 +890,11 @@ nmtstp_link_macvlan_add (gboolean external_command,
 		                                modes[lnk->mode],
 		                                lnk->no_promisc ? "nopromisc" : "");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, link_type, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, link_type, 100);
 	} else
 		success = nm_platform_link_macvlan_add (NM_PLATFORM_GET, name, parent, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, link_type);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, link_type);
 
 	return pllink;
 }
@@ -930,11 +932,11 @@ nmtstp_link_sit_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_SIT, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_SIT, 100);
 	} else
 		success = nm_platform_link_sit_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_SIT);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_SIT);
 
 	return pllink;
 }
@@ -983,7 +985,7 @@ nmtstp_link_vxlan_add (gboolean external_command,
 		/* Older versions of iproute2 don't support adding vxlan devices.
 		 * On failure, fallback to using platform code. */
 		if (err == 0)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_VXLAN, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_VXLAN, 100);
 		else
 			_LOGI ("Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task.");
 	}
@@ -1006,6 +1008,7 @@ nmtstp_ip4_address_add (gboolean external_command,
                         in_addr_t peer_address,
                         guint32 lifetime,
                         guint32 preferred,
+                        guint32 flags,
                         const char *label)
 {
 	_ip_address_add (external_command,
@@ -1016,8 +1019,8 @@ nmtstp_ip4_address_add (gboolean external_command,
 	                 (NMIPAddr *) &peer_address,
 	                 lifetime,
 	                 preferred,
-	                 label,
-	                 0);
+	                 flags,
+	                 label);
 }
 
 void
@@ -1038,8 +1041,8 @@ nmtstp_ip6_address_add (gboolean external_command,
 	                 (NMIPAddr *) &peer_address,
 	                 lifetime,
 	                 preferred,
-	                 NULL,
-	                 flags);
+	                 flags,
+	                 NULL);
 }
 
 static void
@@ -1127,7 +1130,7 @@ _ip_address_del (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
@@ -1161,14 +1164,18 @@ nmtstp_ip6_address_del (gboolean external_command,
 }
 
 const NMPlatformLink *
-nmtstp_link_get_typed (int ifindex,
+nmtstp_link_get_typed (NMPlatform *platform,
+                       int ifindex,
                        const char *name,
                        NMLinkType link_type)
 {
 	const NMPlatformLink *pllink = NULL;
 
+	if (!platform)
+		platform = NM_PLATFORM_GET;
+
 	if (ifindex > 0) {
-		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+		pllink = nm_platform_link_get (platform, ifindex);
 
 		if (pllink) {
 			g_assert_cmpint (pllink->ifindex, ==, ifindex);
@@ -1176,12 +1183,12 @@ nmtstp_link_get_typed (int ifindex,
 				g_assert_cmpstr (name, ==, pllink->name);
 		} else {
 			if (name)
-				g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name));
+				g_assert (!nm_platform_link_get_by_ifname (platform, name));
 		}
 	} else {
 		g_assert (name);
 
-		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name);
+		pllink = nm_platform_link_get_by_ifname (platform, name);
 
 		if (pllink)
 			g_assert_cmpstr (name, ==, pllink->name);
@@ -1196,10 +1203,11 @@ nmtstp_link_get_typed (int ifindex,
 }
 
 const NMPlatformLink *
-nmtstp_link_get (int ifindex,
+nmtstp_link_get (NMPlatform *platform,
+                 int ifindex,
                  const char *name)
 {
-	return nmtstp_link_get_typed (ifindex, name, NM_LINK_TYPE_NONE);
+	return nmtstp_link_get_typed (platform, ifindex, name, NM_LINK_TYPE_NONE);
 }
 
 void
@@ -1212,7 +1220,7 @@ nmtstp_link_del (gboolean external_command,
 	gboolean success;
 	gs_free char *name_copy = NULL;
 
-	pllink = nmtstp_link_get (ifindex, name);
+	pllink = nmtstp_link_get (NM_PLATFORM_GET, ifindex, name);
 
 	g_assert (pllink);
 
@@ -1242,7 +1250,7 @@ nmtstp_link_del (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
@@ -1288,7 +1296,7 @@ nmtstp_link_set_updown (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 0281d3c5..82561193 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -91,13 +91,13 @@ void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, N
 int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2)));
 #define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0)
 
-gboolean nmtstp_wait_for_signal (guint timeout_ms);
-gboolean nmtstp_wait_for_signal_until (gint64 until_ms);
-const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
-const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
+gboolean nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms);
+gboolean nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms);
+const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
+const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
 
-const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
-const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
+const NMPlatformLink *nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
+const NMPlatformLink *nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
 
 int nmtstp_run_command_check_external_global (void);
 gboolean nmtstp_run_command_check_external (int external_command);
@@ -117,6 +117,7 @@ void nmtstp_ip4_address_add (gboolean external_command,
                              in_addr_t peer_address,
                              guint32 lifetime,
                              guint32 preferred,
+                             guint32 flags,
                              const char *label);
 void nmtstp_ip6_address_add (gboolean external_command,
                              int ifindex,
@@ -125,7 +126,7 @@ void nmtstp_ip6_address_add (gboolean external_command,
                              struct in6_addr peer_address,
                              guint32 lifetime,
                              guint32 preferred,
-                             guint flags);
+                             guint32 flags);
 void nmtstp_ip4_address_del (gboolean external_command,
                              int ifindex,
                              in_addr_t address,
@@ -136,8 +137,8 @@ void nmtstp_ip6_address_del (gboolean external_command,
                              struct in6_addr address,
                              int plen);
 
-const NMPlatformLink *nmtstp_link_get_typed (int ifindex, const char *name, NMLinkType link_type);
-const NMPlatformLink *nmtstp_link_get (int ifindex, const char *name);
+const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type);
+const NMPlatformLink *nmtstp_link_get (NMPlatform *platform, int ifindex, const char *name);
 
 void nmtstp_link_set_updown (gboolean external_command,
                              int ifindex,
diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c
index f342e565..66f72fa5 100644
--- a/src/platform/tests/test-general.c
+++ b/src/platform/tests/test-general.c
@@ -18,12 +18,12 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#include "nm-platform-utils.h"
+#include "nm-default.h"
 
 #include <linux/rtnetlink.h>
 
+#include "nm-platform-utils.h"
 #include "nm-linux-platform.h"
-#include "nm-default.h"
 
 #include "nm-test-utils.h"
 
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 3fa5ad50..94695edc 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -21,8 +21,13 @@
 #include "nm-default.h"
 
 #include <sched.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include "nmp-object.h"
+#include "nmp-netns.h"
+#include "nm-platform-utils.h"
 
 #include "test-common.h"
 #include "nm-test-utils.h"
@@ -40,6 +45,9 @@
 #define VLAN_FLAGS 0
 #define MTU 1357
 
+#define _ADD_DUMMY(platform, name) \
+	g_assert_cmpint (nm_platform_link_dummy_add ((platform), (name), NULL), ==, NM_PLATFORM_ERROR_SUCCESS)
+
 static void
 test_bogus(void)
 {
@@ -52,16 +60,16 @@ test_bogus(void)
 	g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -72,7 +80,7 @@ test_bogus(void)
 	g_assert (!addrlen);
 	g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU));
 
 	g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -690,7 +698,7 @@ test_software_detect (gconstpointer user_data)
 	const gboolean ext = test_data->external_command;
 
 	nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
-	ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	switch (test_data->link_type) {
 	case NM_LINK_TYPE_GRE: {
@@ -852,7 +860,7 @@ test_software_detect (gconstpointer user_data)
 		g_assert_not_reached ();
 	}
 
-	ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex;
+	ifindex = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, DEVICE_NAME, test_data->link_type, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_parent, TRUE);
 
@@ -1137,10 +1145,10 @@ test_vlan_set_xgress (void)
 	int ifindex, ifindex_parent;
 
 	nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
-	ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME);
-	ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex;
+	ifindex = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex;
 
 	/* ingress-qos-map */
 
@@ -1662,7 +1670,7 @@ test_create_many_links (gconstpointer user_data)
 	guint n_devices = GPOINTER_TO_UINT (user_data);
 
 	if (n_devices > 100 && nmtst_test_quick ()) {
-		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-link-linux"));
+		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", g_get_prgname () ?: "test-link-linux");
 		g_test_skip ("Skip long running test");
 		return;
 	}
@@ -1685,8 +1693,8 @@ test_nl_bugs_veth (void)
 
 	/* create veth pair. */
 	nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1);
-	ifindex_veth0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
-	ifindex_veth1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
+	ifindex_veth0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
+	ifindex_veth1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
 
 	/* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */
 	g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i));
@@ -1722,7 +1730,7 @@ test_nl_bugs_veth (void)
 
 	nmtstp_run_command_check ("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid (ns_handle));
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 		nm_platform_process_events (NM_PLATFORM_GET);
 
 		pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
@@ -1736,8 +1744,8 @@ test_nl_bugs_veth (void)
 
 out:
 	nmtstp_link_del (-1, ifindex_veth0, IFACE_VETH0);
-	g_assert (!nmtstp_link_get (ifindex_veth0, IFACE_VETH0));
-	g_assert (!nmtstp_link_get (ifindex_veth1, IFACE_VETH1));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth0, IFACE_VETH0));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth1, IFACE_VETH1));
 	nmtstp_namespace_handle_release (ns_handle);
 }
 
@@ -1755,16 +1763,16 @@ test_nl_bugs_spuroius_newlink (void)
 	/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
 
 	nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
-	ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_dummy0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add %s type bond", IFACE_BOND0);
-	ifindex_bond0 = nmtstp_assert_wait_for_link (IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex;
+	ifindex_bond0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_bond0, TRUE);
 
 	nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0);
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 
 		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
 		g_assert (pllink);
@@ -1775,7 +1783,7 @@ test_nl_bugs_spuroius_newlink (void)
 	nmtstp_run_command_check ("ip link del %s",  IFACE_BOND0);
 
 	wait_for_settle = TRUE;
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 again:
 	nm_platform_process_events (NM_PLATFORM_GET);
 	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0);
@@ -1783,11 +1791,11 @@ again:
 
 	if (wait_for_settle) {
 		wait_for_settle = FALSE;
-		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); });
 		goto again;
 	}
 
-	g_assert (!nmtstp_link_get (ifindex_bond0, IFACE_BOND0));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_bond0, IFACE_BOND0));
 	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
 }
 
@@ -1805,16 +1813,16 @@ test_nl_bugs_spuroius_dellink (void)
 	/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
 
 	nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
-	ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_dummy0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add %s type bridge", IFACE_BRIDGE0);
-	ifindex_bridge0 = nmtstp_assert_wait_for_link (IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex;
+	ifindex_bridge0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_bridge0, TRUE);
 
 	nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0);
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 
 		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
 		g_assert (pllink);
@@ -1827,7 +1835,7 @@ test_nl_bugs_spuroius_dellink (void)
 	nmtstp_run_command_check ("ip link set %s nomaster",  IFACE_DUMMY0);
 
 	wait_for_settle = TRUE;
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 again:
 	nm_platform_process_events (NM_PLATFORM_GET);
 	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0);
@@ -1838,7 +1846,7 @@ again:
 
 	if (wait_for_settle) {
 		wait_for_settle = FALSE;
-		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); });
 		goto again;
 	}
 
@@ -1846,6 +1854,442 @@ again:
 	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
 }
 
+/******************************************************************/
+
+static void
+_test_netns_setup (gpointer fixture, gconstpointer test_data)
+{
+	/* the singleton platform instance has netns support disabled.
+	 * Destroy the instance before the test and re-create it afterwards. */
+	g_object_unref (nm_platform_get ());
+}
+
+static void
+_test_netns_teardown (gpointer fixture, gconstpointer test_data)
+{
+	/* re-create platform instance */
+	SETUP ();
+}
+
+static NMPlatform *
+_test_netns_create_platform (void)
+{
+	NMPNetns *netns;
+	NMPlatform *platform;
+
+	netns = nmp_netns_new ();
+	g_assert (NMP_IS_NETNS (netns));
+
+	platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	g_assert (NM_IS_LINUX_PLATFORM (platform));
+
+	nmp_netns_pop (netns);
+	g_object_unref (netns);
+
+	return platform;
+}
+
+static gboolean
+_test_netns_check_skip (void)
+{
+	static int support = -1;
+	static int support_errsv = 0;
+	NMPNetns *netns;
+
+	netns = nmp_netns_get_current ();
+	if (!netns) {
+		g_test_skip ("No netns support");
+		return TRUE;
+	}
+
+	g_assert (nmp_netns_get_fd_net (netns) > 0);
+
+	if (support == -1) {
+		support = (setns (nmp_netns_get_fd_net (netns), CLONE_NEWNET) == 0);
+		if (!support)
+			support_errsv = errno;
+	}
+	if (!support) {
+			_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (support_errsv));
+			g_test_skip ("No netns support (setns failed)");
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/******************************************************************/
+
+static void
+test_netns_general (gpointer fixture, gconstpointer test_data)
+{
+	gs_unref_object NMPlatform *platform_1 = NULL;
+	gs_unref_object NMPlatform *platform_2 = NULL;
+	NMPNetns *netns_tmp;
+	char sbuf[100];
+	int i, j, k;
+	gboolean ethtool_support;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platform_1 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platform_2 = _test_netns_create_platform ();
+
+	/* add some dummy devices. The "other-*" devices are there to bump the ifindex */
+	for (k = 0; k < 2; k++) {
+		NMPlatform *p = (k == 0 ? platform_1 : platform_2);
+		const char *id = (k == 0 ? "a" : "b");
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-a-%s-%02d", id, i));
+
+		_ADD_DUMMY (p, "dummy1_");
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-b-%s-%02d", id, i));
+
+		_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "dummy2%s", id));
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_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", 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", 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", nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex));
+
+	for (i = 0; i < 10; i++) {
+		NMPlatform *pl;
+		const char *path;
+
+		j = nmtst_get_rand_int () % 2;
+
+		if (nmtst_get_rand_int () % 2) {
+			pl = platform_1;
+			if (nmtst_get_rand_int () % 2)
+				path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
+			else
+				path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6";
+		} else {
+			pl = platform_2;
+			if (nmtst_get_rand_int () % 2)
+				path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
+			else
+				path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6";
+		}
+		g_assert (nm_platform_sysctl_set (pl, path, nm_sprintf_buf (sbuf, "%d", j)));
+		g_assert_cmpstr (nm_platform_sysctl_get (pl, path), ==, nm_sprintf_buf (sbuf, "%d", j));
+	}
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"), ==, NULL);
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"), ==, NULL);
+
+	/* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and
+	 * skip asserts that are known to fail. */
+	ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0;
+	if (ethtool_support) {
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+		g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b 2> /dev/null"), !=, 0);
+	}
+
+	g_assert (nm_platform_netns_push (platform_2, &netns_tmp));
+
+	if (ethtool_support) {
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+		g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a 2> /dev/null"), !=, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b > /dev/null"), ==, 0);
+	}
+
+	nmp_netns_pop (netns_tmp);
+}
+
+/*****************************************************************************/
+
+static void
+test_netns_set_netns (gpointer fixture, gconstpointer test_data)
+{
+	NMPlatform *platforms[3];
+	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;
+	int i;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platforms[0] = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platforms[1] = platform_1 = _test_netns_create_platform ();
+	platforms[2] = platform_2 = _test_netns_create_platform ();
+
+	i = nmtst_get_rand_int () % 4;
+	if (i != 3)
+		g_assert (nm_platform_netns_push (platforms[i], &netns_pop));
+
+#define LINK_MOVE_NAME "link-move"
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	_ADD_DUMMY (platform_1, LINK_MOVE_NAME);
+	g_assert ( nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	g_assert (nm_platform_link_set_netns (platform_1,
+	                                      nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME)->ifindex,
+	                                      nmp_netns_get_fd_net (nm_platform_netns_get (platform_2))));
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	nmtstp_assert_wait_for_link (platform_2, LINK_MOVE_NAME, NM_LINK_TYPE_DUMMY, 100);
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert ( nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+}
+
+/*****************************************************************************/
+
+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));
+	}
+}
+
+/*****************************************************************************/
+
+static void
+test_netns_bind_to_path (gpointer fixture, gconstpointer test_data)
+{
+#define P_VAR_RUN                "/var/run"
+#define P_VAR_RUN_NETNS          "/var/run/netns"
+#define P_VAR_RUN_NETNS_BINDNAME "/var/run/netns/"P_NETNS_BINDNAME
+#define P_NETNS_BINDNAME         "nmtst-iproute2-netns"
+	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;
+	NMPlatform *platforms[3];
+	NMPNetns *netns;
+	int i;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platforms[0] = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platforms[1] = platform_1 = _test_netns_create_platform ();
+	platforms[2] = platform_2 = _test_netns_create_platform ();
+
+	i = nmtst_get_rand_int () % 4;
+	if (i != 3)
+		g_assert (nm_platform_netns_push (platforms[i], &netns_pop));
+
+	g_assert_cmpint (mount ("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0);
+	g_assert_cmpint (mkdir (P_VAR_RUN_NETNS, 755), ==, 0);
+
+	i = (nmtst_get_rand_int () % 2) + 1;
+	netns = nm_platform_netns_get (platforms[i]);
+
+	_ADD_DUMMY (platforms[i], "dummy2b");
+
+	g_assert (!g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true 2>/dev/null"), !=, 0);
+
+	g_assert (nmp_netns_bind_to_path (netns, P_VAR_RUN_NETNS_BINDNAME, NULL));
+
+	g_assert (g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true"), ==, 0);
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" ip link show dummy2b 1>/dev/null"), ==, 0);
+
+	g_assert (nmp_netns_bind_to_path_destroy (netns, P_VAR_RUN_NETNS_BINDNAME));
+
+	g_assert (!g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true 2>/dev/null"), !=, 0);
+
+	g_assert_cmpint (umount (P_VAR_RUN), ==, 0);
+}
+
 /*****************************************************************************/
 
 void
@@ -1894,5 +2338,10 @@ setup_tests (void)
 		g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth);
 		g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink);
 		g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink);
+
+		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);
+		g_test_add_vtable ("/general/netns/bind-to-path", 0, NULL, _test_netns_setup, test_netns_bind_to_path, _test_netns_teardown);
 	}
 }
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 3b44abb8..d77170b3 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -223,9 +223,7 @@ test_cache_link (void)
 	GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
 	NMPCacheOpsType ops_type;
 
-	cache = nmp_cache_new ();
-
-	nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2));
+	cache = nmp_cache_new (nmtst_get_rand_int () % 2);
 
 	/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
 	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index f5fa024b..85851854 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -314,7 +314,7 @@ test_ip4_zero_gateway (void)
 	nmtstp_run_command_check ("ip route add 1.2.3.2/32 dev %s", DEVICE_NAME);
 
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (10);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 10);
 		if (   nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0)
 		    && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0))
 			break;
@@ -322,7 +322,7 @@ test_ip4_zero_gateway (void)
 
 	nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME);
 
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 	nm_platform_process_events (NM_PLATFORM_GET);
 }