summary refs log tree commit diff
path: root/src/platform/tests/test-link.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/tests/test-link.c')
-rw-r--r--src/platform/tests/test-link.c1351
1 files changed, 1308 insertions, 43 deletions
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 47cbcec1..8b496262 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1,5 +1,9 @@
 #include "config.h"
 
+#include <sched.h>
+
+#include "nmp-object.h"
+
 #include "test-common.h"
 #include "nm-test-utils.h"
 
@@ -28,10 +32,18 @@ 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_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_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX));
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*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_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX));
+
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -39,15 +51,18 @@ test_bogus(void)
 	g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen));
 	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_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU));
+
 	g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
 	g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL));
-	g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0));
-	g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0));
+	g_assert (!nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
+	g_assert (!nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0));
+	g_assert (!nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0));
 }
 
 static void
@@ -68,15 +83,15 @@ software_add (NMLinkType link_type, const char *name)
 {
 	switch (link_type) {
 	case NM_LINK_TYPE_DUMMY:
-		return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+		return nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS;
 	case NM_LINK_TYPE_BRIDGE:
-		return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+		return nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS;
 	case NM_LINK_TYPE_BOND:
 		{
 			gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0");
 			NMPlatformError plerr;
 
-			plerr = nm_platform_bond_add (NM_PLATFORM_GET, name, NULL);
+			plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL);
 
 			/* Check that bond0 is *not* automatically created. */
 			if (!bond0_exists)
@@ -84,14 +99,14 @@ software_add (NMLinkType link_type, const char *name)
 			return plerr == NM_PLATFORM_ERROR_SUCCESS;
 		}
 	case NM_LINK_TYPE_TEAM:
-		return nm_platform_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+		return nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS;
 	case NM_LINK_TYPE_VLAN: {
 		SignalData *parent_added;
 		SignalData *parent_changed;
 
 		/* Don't call link_callback for the bridge interface */
 		parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME);
-		if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS)
+		if (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS)
 			accept_signal (parent_added);
 		free_signal (parent_added);
 
@@ -108,7 +123,7 @@ software_add (NMLinkType link_type, const char *name)
 				accept_signal (parent_changed);
 			free_signal (parent_changed);
 
-			return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+			return nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS;
 		}
 	}
 	default:
@@ -123,7 +138,6 @@ test_link_changed_signal_cb (NMPlatform *platform,
                              int ifindex,
                              const NMPlatformIP4Route *route,
                              NMPlatformSignalChangeType change_type,
-                             NMPlatformReason reason,
                              gboolean *p_test_link_changed_signal_arg)
 {
 	/* test invocation of platform signals with multiple listeners
@@ -146,9 +160,6 @@ test_link_changed_signal_cb (NMPlatform *platform,
 	g_assert_cmpint ((gint64) change_type, !=, (gint64) 0);
 	g_assert_cmpint (change_type, !=, NM_PLATFORM_SIGNAL_NONE);
 
-	g_assert_cmpint ((gint64) reason, !=, (gint64) 0);
-	g_assert_cmpint (reason, !=, NM_PLATFORM_REASON_NONE);
-
 	*p_test_link_changed_signal_arg = TRUE;
 }
 
@@ -262,9 +273,9 @@ test_slave (int master, int type, SignalData *master_changed)
 	/* Set slave option */
 	switch (type) {
 	case NM_LINK_TYPE_BRIDGE:
-		if (nmtst_platform_is_sysfs_writable ()) {
-			g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789"));
-			value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority");
+		if (nmtstp_is_sysfs_writable ()) {
+			g_assert (nm_platform_sysctl_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789"));
+			value = nm_platform_sysctl_slave_get_option (NM_PLATFORM_GET, ifindex, "priority");
 			g_assert_cmpstr (value, ==, "789");
 			g_free (value);
 		}
@@ -296,7 +307,7 @@ test_slave (int master, int type, SignalData *master_changed)
 	ensure_no_signal (link_added);
 	ensure_no_signal (link_changed);
 	ensure_no_signal (link_removed);
-	g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex));
+	nmtstp_link_del (-1, ifindex, NULL);
 	accept_signals (master_changed, 0, 1);
 	accept_signals (link_changed, 0, 1);
 	accept_signal (link_removed);
@@ -311,7 +322,7 @@ test_software (NMLinkType link_type, const char *link_typename)
 {
 	int ifindex;
 	char *value;
-	int vlan_parent, vlan_id;
+	int vlan_parent = -1, vlan_id;
 
 	SignalData *link_added, *link_changed, *link_removed;
 
@@ -327,7 +338,15 @@ test_software (NMLinkType link_type, const char *link_typename)
 	link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex);
 	link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex);
 	if (link_type == NM_LINK_TYPE_VLAN) {
-		g_assert (nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id));
+		const NMPlatformLink *plink;
+		const NMPlatformLnkVlan *plnk;
+
+		plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink);
+		g_assert (plnk);
+		g_assert (plink);
+
+		vlan_parent = plink->parent;
+		vlan_id = plnk->id;
 		g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME));
 		g_assert_cmpint (vlan_id, ==, VLAN_ID);
 	}
@@ -347,17 +366,17 @@ test_software (NMLinkType link_type, const char *link_typename)
 	/* Set master option */
 	switch (link_type) {
 	case NM_LINK_TYPE_BRIDGE:
-		if (nmtst_platform_is_sysfs_writable ()) {
-			g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789"));
-			value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay");
+		if (nmtstp_is_sysfs_writable ()) {
+			g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789"));
+			value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay");
 			g_assert_cmpstr (value, ==, "789");
 			g_free (value);
 		}
 		break;
 	case NM_LINK_TYPE_BOND:
-		if (nmtst_platform_is_sysfs_writable ()) {
-			g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup"));
-			value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode");
+		if (nmtstp_is_sysfs_writable ()) {
+			g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup"));
+			value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "mode");
 			/* When reading back, the output looks slightly different. */
 			g_assert (g_str_has_prefix (value, "active-backup"));
 			g_free (value);
@@ -382,20 +401,18 @@ test_software (NMLinkType link_type, const char *link_typename)
 	free_signal (link_changed);
 
 	/* Delete */
-	g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex));
-	g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME));
-	g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_NONE);
-	g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, ifindex));
+	nmtstp_link_del (-1, ifindex, DEVICE_NAME);
 	accept_signal (link_removed);
 
 	/* Delete again */
 	g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)));
+	g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex));
 
 	/* VLAN: Delete parent */
 	if (link_type == NM_LINK_TYPE_VLAN) {
 		SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent);
 
-		g_assert (nm_platform_link_delete (NM_PLATFORM_GET, vlan_parent));
+		nmtstp_link_del (-1, vlan_parent, NULL);
 		accept_signal (link_removed_parent);
 		free_signal (link_removed_parent);
 	}
@@ -414,7 +431,7 @@ test_bridge (void)
 static void
 test_bond (void)
 {
-	if (nmtst_platform_is_root_test () &&
+	if (nmtstp_is_root_test () &&
 	    !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) &&
 	    system("modprobe --show bonding") != 0) {
 		g_test_skip ("Skipping test for bonding: bonding module not available");
@@ -436,6 +453,53 @@ test_vlan (void)
 	test_software (NM_LINK_TYPE_VLAN, "vlan");
 }
 
+/*****************************************************************************/
+
+static void
+test_bridge_addr (void)
+{
+	char addr[ETH_ALEN];
+	NMPlatformLink link;
+	const NMPlatformLink *plink = NULL;
+
+	nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr));
+
+	g_assert_cmpint (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink), ==, NM_PLATFORM_ERROR_SUCCESS);
+	g_assert (plink);
+	link = *plink;
+	g_assert_cmpstr (link.name, ==, DEVICE_NAME);
+
+	g_assert_cmpint (link.addr.len, ==, sizeof (addr));
+	g_assert (!memcmp (link.addr.data, addr, sizeof (addr)));
+
+	plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex);
+	g_assert (plink);
+
+	if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) {
+		g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
+		g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64);
+
+		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE));
+		g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
+		plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex);
+		g_assert (plink);
+		g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE);
+
+		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE));
+		g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
+		plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex);
+		g_assert (plink);
+		g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64);
+	}
+
+	g_assert_cmpint (plink->addr.len, ==, sizeof (addr));
+	g_assert (!memcmp (plink->addr.data, addr, sizeof (addr)));
+
+	nmtstp_link_del (-1, link.ifindex, link.name);
+}
+
+/*****************************************************************************/
+
 static void
 test_internal (void)
 {
@@ -451,11 +515,11 @@ test_internal (void)
 	g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME));
 
 	/* Add device */
-	g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS);
+	g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS);
 	accept_signal (link_added);
 
 	/* Try to add again */
-	g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS);
+	g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS);
 
 	/* Check device index, name and type */
 	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
@@ -506,7 +570,7 @@ test_internal (void)
 	accept_signal (link_changed);
 
 	/* Delete device */
-	g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex));
+	nmtstp_link_del (-1, ifindex, DEVICE_NAME);
 	accept_signal (link_removed);
 
 	/* Try to delete again */
@@ -517,15 +581,18 @@ test_internal (void)
 	free_signal (link_removed);
 }
 
+/*****************************************************************************/
+
 static void
 test_external (void)
 {
 	const NMPlatformLink *pllink;
-	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
-	SignalData *link_changed, *link_removed;
+	SignalData *link_added, *link_changed, *link_removed;
 	int ifindex;
 
-	run_command ("ip link add %s type %s", DEVICE_NAME, "dummy");
+	link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+
+	nmtstp_run_command_check ("ip link add %s type %s", DEVICE_NAME, "dummy");
 	wait_signal (link_added);
 
 	g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME));
@@ -549,24 +616,24 @@ test_external (void)
 	g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
 	g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex));
 
-	run_command ("ip link set %s up", DEVICE_NAME);
+	nmtstp_run_command_check ("ip link set %s up", DEVICE_NAME);
 	wait_signal (link_changed);
 
 	g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex));
 	g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
-	run_command ("ip link set %s down", DEVICE_NAME);
+	nmtstp_run_command_check ("ip link set %s down", DEVICE_NAME);
 	wait_signal (link_changed);
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex));
 	g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
 
-	run_command ("ip link set %s arp on", DEVICE_NAME);
+	nmtstp_run_command_check ("ip link set %s arp on", DEVICE_NAME);
 	wait_signal (link_changed);
 	g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex));
-	run_command ("ip link set %s arp off", DEVICE_NAME);
+	nmtstp_run_command_check ("ip link set %s arp off", DEVICE_NAME);
 	wait_signal (link_changed);
 	g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex));
 
-	run_command ("ip link del %s", DEVICE_NAME);
+	nmtstp_run_command_check ("ip link del %s", DEVICE_NAME);
 	wait_signal (link_removed);
 	accept_signals (link_changed, 0, 1);
 	g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME));
@@ -576,6 +643,1183 @@ test_external (void)
 	free_signal (link_removed);
 }
 
+/*****************************************************************************/
+
+typedef struct {
+	NMLinkType link_type;
+	int test_mode;
+	gboolean external_command;
+} TestAddSoftwareDetectData;
+
+static void
+test_software_detect (gconstpointer user_data)
+{
+	const TestAddSoftwareDetectData *test_data = user_data;
+	int ifindex, ifindex_parent;
+	const NMPlatformLink *plink;
+	const NMPObject *lnk;
+	guint i_step;
+	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;
+
+	switch (test_data->link_type) {
+	case NM_LINK_TYPE_GRE: {
+		NMPlatformLnkGre lnk_gre = { };
+		gboolean gracefully_skip = FALSE;
+
+		lnk_gre.local = nmtst_inet4_from_string ("192.168.233.204");
+		lnk_gre.remote = nmtst_inet4_from_string ("172.168.10.25");
+		lnk_gre.parent_ifindex = ifindex_parent;
+		lnk_gre.ttl = 174;
+		lnk_gre.tos = 37;
+		lnk_gre.path_mtu_discovery = TRUE;
+
+		if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gre0")) {
+			/* Seems that the ip_gre module is not loaded... try to load it. */
+			gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0;
+		}
+
+		if (!nmtstp_link_gre_add (ext, DEVICE_NAME, &lnk_gre)) {
+			if (gracefully_skip) {
+				g_test_skip ("Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)");
+				goto out_delete_parent;
+			}
+			g_error ("Failed adding GRE tunnel");
+		}
+		break;
+	}
+	case NM_LINK_TYPE_IPIP: {
+		NMPlatformLnkIpIp lnk_ipip = { };
+		gboolean gracefully_skip = FALSE;
+
+		if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "tunl0")) {
+			/* Seems that the ipip module is not loaded... try to load it. */
+			gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ipip", NULL) != 0;
+		}
+
+		lnk_ipip.local = nmtst_inet4_from_string ("1.2.3.4");
+		lnk_ipip.remote = nmtst_inet4_from_string ("5.6.7.8");
+		lnk_ipip.parent_ifindex = ifindex_parent;
+		lnk_ipip.tos = 32;
+		lnk_ipip.path_mtu_discovery = FALSE;
+
+		if (!nmtstp_link_ipip_add (ext, DEVICE_NAME, &lnk_ipip)) {
+			if (gracefully_skip) {
+				g_test_skip ("Cannot create ipip tunnel because of missing ipip module (modprobe ipip)");
+				goto out_delete_parent;
+			}
+			g_error ("Failed adding IPIP tunnel");
+		}
+		break;
+	}
+	case NM_LINK_TYPE_IP6TNL: {
+		NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
+		gboolean gracefully_skip = FALSE;
+
+		if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6tnl0")) {
+			/* Seems that the ip6_tunnel module is not loaded... try to load it. */
+			gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 0;
+		}
+
+		lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15");
+		lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16");
+		lnk_ip6tnl.parent_ifindex = ifindex_parent;
+		lnk_ip6tnl.tclass = 20;
+		lnk_ip6tnl.encap_limit = 6;
+		lnk_ip6tnl.flow_label = 1337;
+		lnk_ip6tnl.proto = IPPROTO_IPV6;
+
+		if (!nmtstp_link_ip6tnl_add (ext, DEVICE_NAME, &lnk_ip6tnl)) {
+			if (gracefully_skip) {
+				g_test_skip ("Cannot create ip6tnl tunnel because of missing ip6_tunnel module (modprobe ip6_tunnel)");
+				goto out_delete_parent;
+			}
+			g_error ("Failed adding IP6TNL tunnel");
+		}
+		break;
+	}
+	case NM_LINK_TYPE_MACVLAN: {
+		NMPlatformLnkMacvlan lnk_macvlan = { };
+
+		lnk_macvlan.mode = MACVLAN_MODE_BRIDGE;
+		lnk_macvlan.no_promisc = FALSE;
+		lnk_macvlan.tap = FALSE;
+
+		if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan))
+			g_error ("Failed adding MACVLAN interface");
+		break;
+	}
+	case NM_LINK_TYPE_MACVTAP: {
+		NMPlatformLnkMacvtap lnk_macvtap = { };
+
+		lnk_macvtap.mode = MACVLAN_MODE_PRIVATE;
+		lnk_macvtap.no_promisc = FALSE;
+		lnk_macvtap.tap = TRUE;
+
+		if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvtap))
+			g_error ("Failed adding MACVTAP interface");
+		break;
+	}
+	case NM_LINK_TYPE_SIT: {
+		NMPlatformLnkSit lnk_sit = { };
+		gboolean gracefully_skip = FALSE;
+
+		lnk_sit.local = nmtst_inet4_from_string ("192.168.200.1");
+		lnk_sit.remote = nmtst_inet4_from_string ("172.25.100.14");
+		lnk_sit.parent_ifindex = ifindex_parent;
+		lnk_sit.ttl = 0;
+		lnk_sit.tos = 31;
+		lnk_sit.path_mtu_discovery = FALSE;
+
+		if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "sit0")) {
+			/* Seems that the sit module is not loaded... try to load it. */
+			gracefully_skip = nm_utils_modprobe (NULL, TRUE, "sit", NULL) != 0;
+		}
+
+		if (!nmtstp_link_sit_add (ext, DEVICE_NAME, &lnk_sit)) {
+			if (gracefully_skip) {
+				g_test_skip ("Cannot create sit tunnel because of missing sit module (modprobe sit)");
+				goto out_delete_parent;
+			}
+			g_error ("Failed adding SIT tunnel");
+		}
+		break;
+	}
+	case NM_LINK_TYPE_VLAN:
+		nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1242", DEVICE_NAME, PARENT_NAME);
+		break;
+	case NM_LINK_TYPE_VXLAN: {
+		NMPlatformLnkVxlan lnk_vxlan = { };
+
+		switch (test_data->test_mode) {
+		case 0:
+			lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME);
+			lnk_vxlan.id = 42;
+			lnk_vxlan.local = nmtst_inet4_from_string ("23.1.2.164");
+			lnk_vxlan.group = nmtst_inet4_from_string ("239.1.2.134");
+			lnk_vxlan.dst_port = 4789;
+			lnk_vxlan.learning = TRUE;
+			lnk_vxlan.ageing = 1245;
+			break;
+		case 1:
+			lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME);
+			lnk_vxlan.id = 11214423;
+			lnk_vxlan.local6 = *nmtst_inet6_from_string ("1:2:3:4:334:23::23");
+			lnk_vxlan.group6 = *nmtst_inet6_from_string ("ff0e::115");
+			lnk_vxlan.ttl = 32;
+			lnk_vxlan.dst_port = 57412;
+			lnk_vxlan.src_port_min = 1000;
+			lnk_vxlan.src_port_max = 1003;
+			lnk_vxlan.learning = TRUE;
+			lnk_vxlan.ageing = 3245;
+			break;
+		}
+
+		g_assert (nmtstp_link_vxlan_add (ext, DEVICE_NAME, &lnk_vxlan));
+		break;
+	}
+	default:
+		g_assert_not_reached ();
+	}
+
+	ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex;
+
+	nmtstp_link_set_updown (-1, ifindex_parent, TRUE);
+
+	for (i_step = 0; i_step < 5; i_step++) {
+
+		_LOGD ("test-software-detect: step %u", i_step);
+		if (nmtst_is_debug ())
+			nmtstp_run_command_check ("ip -d link show %s", DEVICE_NAME);
+
+		if (i_step > 0) {
+			gboolean set_up = (i_step % 2) == 1;
+
+			if (   test_data->link_type == NM_LINK_TYPE_VXLAN
+			    && set_up) {
+				/* On RHEL-7, we need to add a tiny sleep here, otherwise,
+				 * upping the vxlan device fails with EADDRINUSE.
+				 * https://bugzilla.redhat.com/show_bug.cgi?id=1277131 */
+				g_usleep (1);
+			}
+			nmtstp_link_set_updown (-1, ifindex, set_up);
+		}
+
+		lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink);
+		g_assert (plink);
+		g_assert_cmpint (plink->ifindex, ==, ifindex);
+		g_assert (lnk);
+
+		switch (test_data->link_type) {
+		case NM_LINK_TYPE_GRE: {
+			const NMPlatformLnkGre *plnk = &lnk->lnk_gre;
+
+			g_assert (plnk == nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
+			g_assert_cmpint (plnk->input_flags, ==, 0);
+			g_assert_cmpint (plnk->output_flags, ==, 0);
+			g_assert_cmpint (plnk->input_key, ==, 0);
+			g_assert_cmpint (plnk->output_key, ==, 0);
+			nmtst_assert_ip4_address (plnk->local, "192.168.233.204");
+			nmtst_assert_ip4_address (plnk->remote, "172.168.10.25");
+			g_assert_cmpint (plnk->ttl, ==, 174);
+			g_assert_cmpint (plnk->tos, ==, 37);
+			g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE);
+			break;
+		}
+		case NM_LINK_TYPE_IP6TNL: {
+			const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl;
+
+			g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
+			nmtst_assert_ip6_address (&plnk->local, "fd01::15");
+			nmtst_assert_ip6_address (&plnk->remote, "fd01::16");
+			g_assert_cmpint (plnk->ttl, ==, 0);
+			g_assert_cmpint (plnk->tclass, ==, 20);
+			g_assert_cmpint (plnk->encap_limit, ==, 6);
+			g_assert_cmpint (plnk->flow_label, ==, 1337);
+			g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6);
+			break;
+		}
+		case NM_LINK_TYPE_IPIP: {
+			const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip;
+
+			g_assert (plnk == nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
+			nmtst_assert_ip4_address (plnk->local, "1.2.3.4");
+			nmtst_assert_ip4_address (plnk->remote, "5.6.7.8");
+			g_assert_cmpint (plnk->ttl, ==, 0);
+			g_assert_cmpint (plnk->tos, ==, 32);
+			g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE);
+			break;
+		}
+		case NM_LINK_TYPE_MACVLAN: {
+			const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan;
+
+			g_assert (plnk == nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->no_promisc, ==, FALSE);
+			g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_BRIDGE);
+			break;
+		}
+		case NM_LINK_TYPE_MACVTAP: {
+			const NMPlatformLnkMacvtap *plnk = &lnk->lnk_macvlan;
+
+			g_assert (plnk == nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->no_promisc, ==, FALSE);
+			g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_PRIVATE);
+			break;
+		}
+		case NM_LINK_TYPE_SIT: {
+			const NMPlatformLnkSit *plnk = &lnk->lnk_sit;
+
+			g_assert (plnk == nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
+			nmtst_assert_ip4_address (plnk->local, "192.168.200.1");
+			nmtst_assert_ip4_address (plnk->remote, "172.25.100.14");
+			g_assert_cmpint (plnk->ttl, ==, 0);
+			g_assert_cmpint (plnk->tos, ==, 31);
+			g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE);
+			break;
+		}
+		case NM_LINK_TYPE_VLAN: {
+			const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan;
+
+			g_assert (plnk == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->id, ==, 1242);
+			break;
+		}
+		case NM_LINK_TYPE_VXLAN: {
+			const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan;
+
+			g_assert (plnk == nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, !=, 0);
+			g_assert_cmpint (plnk->tos, ==, 0);
+			g_assert_cmpint (plnk->learning, ==, TRUE);
+			g_assert_cmpint (plnk->limit, ==, 0);
+			g_assert_cmpint (plnk->proxy, ==, FALSE);
+			g_assert_cmpint (plnk->rsc, ==, FALSE);
+			g_assert_cmpint (plnk->l2miss, ==, FALSE);
+			g_assert_cmpint (plnk->l3miss, ==, FALSE);
+
+			switch (test_data->test_mode) {
+			case 0:
+				g_assert_cmpint (plnk->id, ==, 42);
+				nmtst_assert_ip4_address (plnk->local, "23.1.2.164");
+				nmtst_assert_ip4_address (plnk->group, "239.1.2.134");
+				nmtst_assert_ip6_address (&plnk->group6, "::");
+				nmtst_assert_ip6_address (&plnk->local6, "::");
+				g_assert_cmpint (plnk->ttl, ==, 0);
+				g_assert_cmpint (plnk->ageing, ==, 1245);
+				g_assert_cmpint (plnk->dst_port, ==, 4789);
+				if (   plnk->src_port_min != 0
+				    || plnk->src_port_max != 0) {
+					/* on some kernels, omiting the port range results in setting
+					 * following default port range. */
+					g_assert_cmpint (plnk->src_port_min, ==, 32768);
+					g_assert_cmpint (plnk->src_port_max, ==, 61000);
+				}
+				break;
+			case 1:
+				g_assert_cmpint (plnk->id, ==, 11214423);
+				nmtst_assert_ip4_address (plnk->local, "0.0.0.0");
+				nmtst_assert_ip4_address (plnk->group, "0.0.0.0");
+				nmtst_assert_ip6_address (&plnk->group6, "ff0e::115");
+				nmtst_assert_ip6_address (&plnk->local6, "1:2:3:4:334:23::23");
+				g_assert_cmpint (plnk->ageing, ==, 3245);
+				g_assert_cmpint (plnk->dst_port, ==, 57412);
+				g_assert_cmpint (plnk->ttl, ==, 32);
+				g_assert_cmpint (plnk->src_port_min, ==, 1000);
+				g_assert_cmpint (plnk->src_port_max, ==, 1003);
+				break;
+			}
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	nmtstp_link_del (-1, ifindex, DEVICE_NAME);
+out_delete_parent:
+	nmtstp_link_del (-1, ifindex_parent, PARENT_NAME);
+}
+
+static void
+test_software_detect_add (const char *testpath,
+                          NMLinkType link_type,
+                          int test_mode)
+{
+	TestAddSoftwareDetectData *test_data;
+	char *path;
+
+	test_data = g_new0 (TestAddSoftwareDetectData, 1);
+	test_data->link_type = link_type;
+	test_data->test_mode = test_mode;
+	test_data->external_command = TRUE;
+
+	path = g_strdup_printf ("%s/external", testpath);
+	g_test_add_data_func_full (path, test_data, test_software_detect, g_free);
+	g_free (path);
+
+	test_data = g_new0 (TestAddSoftwareDetectData, 1);
+	test_data->link_type = link_type;
+	test_data->test_mode = test_mode;
+	test_data->external_command = FALSE;
+
+	path = g_strdup_printf ("%s/platform", testpath);
+	g_test_add_data_func_full (path, test_data, test_software_detect, g_free);
+	g_free (path);
+
+	test_data = g_new0 (TestAddSoftwareDetectData, 1);
+	test_data->link_type = link_type;
+	test_data->test_mode = test_mode;
+	test_data->external_command = -1;
+
+	path = g_strdup_printf ("%s/random", testpath);
+	g_test_add_data_func_full (path, test_data, test_software_detect, g_free);
+	g_free (path);
+}
+
+/*****************************************************************************/
+
+static void
+_assert_xgress_qos_mappings_impl (int ifindex,
+                                  gboolean is_ingress_map ,
+                                  int n_entries,
+                                  int n,
+                                  ...)
+{
+	const NMPlatformLink *plink;
+	const NMPObject *lnk;
+	guint n_map;
+	const NMVlanQosMapping *map;
+	va_list ap;
+	guint i;
+
+	lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink);
+
+	g_assert (plink);
+	g_assert_cmpint (plink->ifindex, ==, ifindex);
+	g_assert (lnk);
+	g_assert (&lnk->lnk_vlan == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL));
+
+	if (nmtst_is_debug ())
+		nmtstp_run_command_check ("ip -d link show %s", plink->name);
+
+	if (is_ingress_map) {
+		map = lnk->_lnk_vlan.ingress_qos_map;
+		n_map = lnk->_lnk_vlan.n_ingress_qos_map;
+	} else {
+		map = lnk->_lnk_vlan.egress_qos_map;
+		n_map = lnk->_lnk_vlan.n_egress_qos_map;
+	}
+
+	if (n_entries != -1)
+		g_assert_cmpint (n_map, ==, n_entries);
+
+	for (i = 0; i < n_map; i++) {
+		if (is_ingress_map) {
+			g_assert_cmpint (map[i].from, >=, 0);
+			g_assert_cmpint (map[i].from, <=, 7);
+		}
+		if (i > 0)
+			g_assert_cmpint (map[i - 1].from, <, map[i].from);
+	}
+
+	va_start (ap, n);
+	for (; n > 0; n--) {
+		gboolean found = FALSE;
+		guint from = va_arg (ap, guint);
+		guint to = va_arg (ap, guint);
+
+		for (i = 0; i < n_map; i++) {
+			if (map[i].from == from) {
+				g_assert (!found);
+				found = TRUE;
+
+				g_assert (map[i].to == to);
+			}
+		}
+		g_assert (found);
+	}
+	va_end (ap);
+}
+#define _assert_xgress_qos_mappings(ifindex, is_ingress_map, n_entries, ...) \
+	_assert_xgress_qos_mappings_impl ((ifindex), (is_ingress_map), (n_entries), \
+	                                  (G_STATIC_ASSERT_EXPR ((NM_NARG (__VA_ARGS__) % 2) == 0), NM_NARG (__VA_ARGS__) / 2), \
+	                                  __VA_ARGS__)
+#define _assert_ingress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, TRUE, n_entries, __VA_ARGS__)
+#define _assert_egress_qos_mappings(ifindex, n_entries, ...)  _assert_xgress_qos_mappings (ifindex, FALSE, n_entries, __VA_ARGS__)
+
+static void
+_assert_vlan_flags (int ifindex, NMVlanFlags flags)
+{
+	const NMPlatformLnkVlan *plnk;
+
+	plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL);
+	g_assert (plnk);
+	g_assert_cmpint (plnk->flags, ==, flags);
+}
+
+static void
+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;
+
+	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;
+
+	/* ingress-qos-map */
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 4, 5));
+	_assert_ingress_qos_mappings (ifindex, 1,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 7));
+	_assert_ingress_qos_mappings (ifindex, 2,
+	                              3, 7,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 8));
+	_assert_ingress_qos_mappings (ifindex, 2,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 4));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, 4,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, G_MAXUINT32,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32 - 1));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, G_MAXUINT32 - 1,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, 5,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, 5,
+	                              3, 8,
+	                              4, 5);
+
+	/* Set invalid values: */
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 8, 3));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, 5,
+	                              3, 8,
+	                              4, 5);
+
+	g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 9, 4));
+	_assert_ingress_qos_mappings (ifindex, 3,
+	                              0, 5,
+	                              3, 8,
+	                              4, 5);
+
+	/* egress-qos-map */
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 7, 3));
+	_assert_egress_qos_mappings (ifindex, 1,
+	                             7, 3);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 4));
+	_assert_egress_qos_mappings (ifindex, 2,
+	                             7, 3,
+	                             8, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 4));
+	_assert_egress_qos_mappings (ifindex, 3,
+	                             0, 4,
+	                             7, 3,
+	                             8, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 4));
+	_assert_egress_qos_mappings (ifindex, 4,
+	                             0, 4,
+	                             1, 4,
+	                             7, 3,
+	                             8, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 5));
+	_assert_egress_qos_mappings (ifindex, 4,
+	                             0, 4,
+	                             1, 5,
+	                             7, 3,
+	                             8, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 9, 5));
+	_assert_egress_qos_mappings (ifindex, 5,
+	                             0, 4,
+	                             1, 5,
+	                             7, 3,
+	                             8, 4,
+	                             9, 5);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 5));
+	_assert_egress_qos_mappings (ifindex, 5,
+	                             0, 4,
+	                             1, 5,
+	                             7, 3,
+	                             8, 5,
+	                             9, 5);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 0));
+	_assert_egress_qos_mappings (ifindex, 4,
+	                             0, 4,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 0));
+	_assert_egress_qos_mappings (ifindex, 3,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 4));
+	_assert_egress_qos_mappings (ifindex, 4,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5,
+	                             100, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 4));
+	_assert_egress_qos_mappings (ifindex, 5,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5,
+	                             100, 4,
+	                             G_MAXUINT32, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 8));
+	_assert_egress_qos_mappings (ifindex, 5,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5,
+	                             100, 4,
+	                             G_MAXUINT32, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 0));
+	_assert_egress_qos_mappings (ifindex, 4,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5,
+	                             100, 4);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 0));
+	_assert_egress_qos_mappings (ifindex, 3,
+	                             1, 5,
+	                             7, 3,
+	                             9, 5);
+
+	g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 0));
+	_assert_egress_qos_mappings (ifindex, 2,
+	                             7, 3,
+	                             9, 5);
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 1, .to = 5 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        TRUE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        NULL,
+		                                        0));
+		_assert_ingress_qos_mappings (ifindex, 1,
+		                              1, 5);
+	}
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 3, .to = 5 },
+			{ .from = 7, .to = 1655 },
+			{ .from = 7, .to = 17655 },
+			{ .from = 5, .to = 754 },
+			{ .from = 4, .to = 12 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        TRUE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        NULL,
+		                                        0));
+		_assert_ingress_qos_mappings (ifindex, 4,
+		                              3, 5,
+		                              4, 12,
+		                              7, 17655,
+		                              5, 754);
+	}
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 3, .to = 18 },
+			{ .from = 6, .to = 121 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        NULL,
+		                                        0));
+		_assert_ingress_qos_mappings (ifindex, 5,
+		                              3, 18,
+		                              4, 12,
+		                              6, 121,
+		                              7, 17655,
+		                              5, 754);
+	}
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 3, .to = 0 },
+			{ .from = 6, .to = 7 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        TRUE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        NULL,
+		                                        0));
+		_assert_ingress_qos_mappings (ifindex, 1,
+		                              6, 7);
+	}
+
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 1, .to = 5 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        TRUE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        NULL,
+		                                        0));
+		_assert_ingress_qos_mappings (ifindex, 1,
+		                              1, 5);
+	}
+
+	{
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 5, .to = 1 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        NULL,
+		                                        0,
+		                                        TRUE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_egress_qos_mappings (ifindex, 1,
+		                             5, 1);
+	}
+
+	{
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 5, .to = 3 },
+			{ .from = 1655, .to = 5 },
+			{ .from = 1655, .to = 7 },
+			{ .from = G_MAXUINT32, .to = 6 },
+			{ .from = G_MAXUINT32, .to = 8 },
+			{ .from = 754, .to = 4 },
+			{ .from = 3, .to = 2 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        NULL,
+		                                        0,
+		                                        TRUE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_egress_qos_mappings (ifindex, 5,
+		                             3, 2,
+		                             5, 3,
+		                             754, 4,
+		                             1655, 7,
+		                             G_MAXUINT32, 6);
+	}
+
+	{
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 754, .to = 3 },
+			{ .from = 755, .to = 8 },
+			{ .from = 1655, .to = 0 },
+			{ .from = 6, .to = 1 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        NULL,
+		                                        0,
+		                                        FALSE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_egress_qos_mappings (ifindex, 5,
+		                             3, 2,
+		                             5, 3,
+		                             6, 1,
+		                             754, 3,
+		                             G_MAXUINT32, 6);
+	}
+
+	{
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 6, .to = 0 },
+			{ .from = 3, .to = 4 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        NULL,
+		                                        0,
+		                                        TRUE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_egress_qos_mappings (ifindex, 1,
+		                             3, 4);
+	}
+
+	{
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 1, .to = 5 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        0,
+		                                        0,
+		                                        FALSE,
+		                                        NULL,
+		                                        0,
+		                                        TRUE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_egress_qos_mappings (ifindex, 1,
+		                             1, 5);
+	}
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 6, .to = 145 },
+			{ .from = 4, .to = 1 },
+			{ .from = 6, .to = 12 },
+		};
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 1, .to = 5 },
+			{ .from = 3232, .to = 7 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP,
+		                                        NM_VLAN_FLAG_REORDER_HEADERS,
+		                                        TRUE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        TRUE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_ingress_qos_mappings (ifindex, 2,
+		                             4, 1,
+		                             6, 12);
+		_assert_egress_qos_mappings (ifindex, 2,
+		                             1, 5,
+		                             3232, 7);
+		_assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS);
+	}
+
+	{
+		const NMVlanQosMapping ingress_map[] = {
+			{ .from = 6, .to = 145 },
+			{ .from = 4, .to = 1 },
+			{ .from = 6, .to = 12 },
+		};
+		const NMVlanQosMapping egress_map[] = {
+			{ .from = 1, .to = 7 },
+			{ .from = 64, .to = 10 },
+			{ .from = 64, .to = 10 },
+			{ .from = 64, .to = 10 },
+			{ .from = 64, .to = 10 },
+			{ .from = 3232, .to = 0 },
+			{ .from = 64, .to = 4 },
+		};
+
+		g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                                        ifindex,
+		                                        NM_VLAN_FLAG_GVRP,
+		                                        NM_VLAN_FLAG_GVRP,
+		                                        FALSE,
+		                                        ingress_map,
+		                                        G_N_ELEMENTS (ingress_map),
+		                                        FALSE,
+		                                        egress_map,
+		                                        G_N_ELEMENTS (egress_map)));
+		_assert_ingress_qos_mappings (ifindex, 2,
+		                             4, 1,
+		                             6, 12);
+		_assert_egress_qos_mappings (ifindex, 2,
+		                             1, 7,
+		                             64, 4);
+		_assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP);
+	}
+
+	nmtstp_link_del (-1, ifindex, DEVICE_NAME);
+	nmtstp_link_del (-1, ifindex_parent, PARENT_NAME);
+}
+
+/*****************************************************************************/
+
+static void
+test_create_many_links_do (guint n_devices)
+{
+	gint64 time, start_time = nm_utils_get_monotonic_timestamp_ns ();
+	guint i;
+	char name[64];
+	const NMPlatformLink *pllink;
+	gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices);
+	const gint EX = ((int) (nmtst_get_rand_int () % 4)) - 1;
+
+	g_assert (EX >= -1 && EX <= 2);
+
+	_LOGI (">>> create devices (EX=%d)...", EX);
+
+	for (i = 0; i < n_devices; i++) {
+		nm_sprintf_buf (name, "t-%05u", i);
+		if (EX == 2) {
+			/* This mode is different from letting nmtstp_link_dummy_add()
+			 * because in this case we don't process any platform events
+			 * while adding all the links. */
+			nmtstp_run_command_check ("ip link add %s type dummy", name);
+		} else
+			nmtstp_link_dummy_add (EX, name);
+	}
+
+	_LOGI (">>> process events after creating devices...");
+
+	nm_platform_process_events (NM_PLATFORM_GET);
+
+	_LOGI (">>> check devices...");
+
+	for (i = 0; i < n_devices; i++) {
+		nm_sprintf_buf (name, "t-%05u", i);
+
+		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name);
+		g_assert (pllink);
+		g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_DUMMY);
+		g_assert_cmpstr (pllink->name, ==, name);
+
+		g_array_append_val (ifindexes, pllink->ifindex);
+	}
+
+	_LOGI (">>> delete devices...");
+
+	g_assert_cmpint (ifindexes->len, ==, n_devices);
+	for (i = 0; i < n_devices; i++) {
+		nm_sprintf_buf (name, "t-%05u", i);
+
+		if (EX == 2)
+			nmtstp_run_command_check ("ip link delete %s", name);
+		else
+			nmtstp_link_del (EX, g_array_index (ifindexes, int, i), name);
+	}
+
+	_LOGI (">>> process events after deleting devices...");
+	nm_platform_process_events (NM_PLATFORM_GET);
+
+	time = nm_utils_get_monotonic_timestamp_ns () - start_time;
+	_LOGI (">>> finished in %ld.%09ld seconds", (long) (time / NM_UTILS_NS_PER_SECOND), (long) (time % NM_UTILS_NS_PER_SECOND));
+}
+
+static void
+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_test_skip ("Skip long running test");
+		return;
+	}
+
+	test_create_many_links_do (n_devices);
+}
+
+/*****************************************************************************/
+
+static void
+test_nl_bugs_veth (void)
+{
+	const char *IFACE_VETH0 = "nm-test-veth0";
+	const char *IFACE_VETH1 = "nm-test-veth1";
+	int ifindex_veth0, ifindex_veth1;
+	int i;
+	const NMPlatformLink *pllink_veth0, *pllink_veth1;
+	gs_free_error GError *error = NULL;
+	NMTstpNamespaceHandle *ns_handle = NULL;
+
+	/* 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;
+
+	/* 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));
+	g_assert_cmpint (i, ==, ifindex_veth1);
+
+	g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth1, &i));
+	g_assert_cmpint (i, ==, ifindex_veth0);
+
+	/* assert that NMPlatformLink.parent is the peer-ifindex. */
+	pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0);
+	g_assert (pllink_veth0);
+	if (pllink_veth0->parent == 0) {
+		/* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder
+		 * of the test. */
+		goto out;
+	}
+	g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1);
+
+
+	/* The following tests whether we have a workaround for kernel bug
+	 * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */
+	pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
+	g_assert (pllink_veth1);
+	g_assert_cmpint (pllink_veth1->parent, ==, ifindex_veth0);
+
+
+	/* move one veth peer to another namespace and check that the
+	 * parent/IFLA_LINK of the remaining peer properly updates
+	 * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */
+	ns_handle = nmtstp_namespace_create (CLONE_NEWNET, &error);
+	g_assert_no_error (error);
+	g_assert (ns_handle);
+
+	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);
+		nm_platform_process_events (NM_PLATFORM_GET);
+
+		pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
+		pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0);
+		if (   !pllink_veth1
+		    && pllink_veth0
+		    && pllink_veth0->parent == NM_PLATFORM_LINK_OTHER_NETNS) {
+			break;
+		}
+	});
+
+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));
+	nmtstp_namespace_handle_release (ns_handle);
+}
+
+/*****************************************************************************/
+
+static void
+test_nl_bugs_spuroius_newlink (void)
+{
+	const char *IFACE_BOND0 = "nm-test-bond0";
+	const char *IFACE_DUMMY0 = "nm-test-dummy0";
+	int ifindex_bond0, ifindex_dummy0;
+	const NMPlatformLink *pllink;
+	gboolean wait_for_settle;
+
+	/* 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;
+
+	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;
+
+	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);
+
+		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
+		g_assert (pllink);
+		if (pllink->master == ifindex_bond0)
+			break;
+	});
+
+	nmtstp_run_command_check ("ip link del %s",  IFACE_BOND0);
+
+	wait_for_settle = TRUE;
+	nmtstp_wait_for_signal (50);
+again:
+	nm_platform_process_events (NM_PLATFORM_GET);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0);
+	g_assert (!pllink);
+
+	if (wait_for_settle) {
+		wait_for_settle = FALSE;
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		goto again;
+	}
+
+	g_assert (!nmtstp_link_get (ifindex_bond0, IFACE_BOND0));
+	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
+}
+
+/*****************************************************************************/
+
+static void
+test_nl_bugs_spuroius_dellink (void)
+{
+	const char *IFACE_BRIDGE0 = "nm-test-bridge0";
+	const char *IFACE_DUMMY0 = "nm-test-dummy0";
+	int ifindex_bridge0, ifindex_dummy0;
+	const NMPlatformLink *pllink;
+	gboolean wait_for_settle;
+
+	/* 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;
+
+	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;
+
+	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);
+
+		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
+		g_assert (pllink);
+		if (pllink->master == ifindex_bridge0)
+			break;
+	});
+
+	nm_platform_process_events (NM_PLATFORM_GET);
+
+	nmtstp_run_command_check ("ip link set %s nomaster",  IFACE_DUMMY0);
+
+	wait_for_settle = TRUE;
+	nmtstp_wait_for_signal (50);
+again:
+	nm_platform_process_events (NM_PLATFORM_GET);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0);
+	g_assert (pllink);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
+	g_assert (pllink);
+	g_assert_cmpint (pllink->parent, ==, 0);
+
+	if (wait_for_settle) {
+		wait_for_settle = FALSE;
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		goto again;
+	}
+
+	nmtstp_link_del (-1, ifindex_bridge0, IFACE_BRIDGE0);
+	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
+}
+
+/*****************************************************************************/
+
 void
 init_tests (int *argc, char ***argv)
 {
@@ -599,7 +1843,28 @@ setup_tests (void)
 	g_test_add_func ("/link/software/bond", test_bond);
 	g_test_add_func ("/link/software/team", test_team);
 	g_test_add_func ("/link/software/vlan", test_vlan);
+	g_test_add_func ("/link/software/bridge/addr", test_bridge_addr);
 
-	if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform"))
+	if (nmtstp_is_root_test ()) {
 		g_test_add_func ("/link/external", test_external);
+
+		test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0);
+		test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0);
+		test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0);
+		test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0);
+		test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
+		test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0);
+		test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0);
+		test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0);
+		test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1);
+
+		g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress);
+
+		g_test_add_data_func ("/link/create-many-links/20", GUINT_TO_POINTER (20), test_create_many_links);
+		g_test_add_data_func ("/link/create-many-links/1000", GUINT_TO_POINTER (1000), test_create_many_links);
+
+		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);
+	}
 }