summary refs log tree commit diff
path: root/src/platform/tests/test-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/tests/test-common.c')
-rw-r--r--src/platform/tests/test-common.c445
1 files changed, 340 insertions, 105 deletions
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index a9d0694d..18076712 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -30,6 +30,9 @@
 #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)"
 #define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count
 
+int NMTSTP_ENV1_IFINDEX = -1;
+int NMTSTP_ENV1_EX = -1;
+
 /*****************************************************************************/
 
 void
@@ -67,6 +70,90 @@ _init_platform (NMPlatform **platform, gboolean external_command)
 
 /*****************************************************************************/
 
+static GArray *
+_ipx_address_get_all (NMPlatform *self, int ifindex, NMPObjectType obj_type)
+{
+	NMPLookup lookup;
+
+	g_assert (NM_IS_PLATFORM (self));
+	g_assert (ifindex > 0);
+	g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS));
+	nmp_lookup_init_addrroute (&lookup,
+	                           obj_type,
+	                           ifindex);
+	return nmp_cache_lookup_to_array (nm_platform_lookup (self, &lookup),
+	                                  obj_type,
+	                                  FALSE /*addresses are always visible. */);
+}
+
+GArray *
+nmtstp_platform_ip4_address_get_all (NMPlatform *self, int ifindex)
+{
+	return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS);
+}
+
+GArray *
+nmtstp_platform_ip6_address_get_all (NMPlatform *self, int ifindex)
+{
+	return _ipx_address_get_all (self, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS);
+}
+
+/*****************************************************************************/
+
+gboolean
+nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric)
+{
+	NMDedupMultiIter iter;
+
+	nm_platform_process_events (platform);
+
+	nm_dedup_multi_iter_for_each (&iter,
+	                              nm_platform_lookup_addrroute (platform,
+	                                                            NMP_OBJECT_TYPE_IP4_ROUTE,
+	                                                            ifindex)) {
+		const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (iter.current->obj);
+
+		if (   r->ifindex != ifindex
+		    || r->network != network
+		    || r->plen != plen
+		    || r->metric != metric) {
+			continue;
+		}
+
+		return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r));
+	}
+
+	return TRUE;
+}
+
+gboolean
+nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric)
+{
+	NMDedupMultiIter iter;
+
+	nm_platform_process_events (platform);
+
+	nm_dedup_multi_iter_for_each (&iter,
+	                              nm_platform_lookup_addrroute (platform,
+	                                                            NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                                            ifindex)) {
+		const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (iter.current->obj);
+
+		if (   r->ifindex != ifindex
+		    || !IN6_ARE_ADDR_EQUAL (&r->network, &network)
+		    || r->plen != plen
+		    || r->metric != metric) {
+			continue;
+		}
+
+		return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r));
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
 SignalData *
 add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname)
 {
@@ -153,9 +240,9 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink
 {
 	const NMPObjectType obj_type = obj_type_i;
 	const NMPlatformSignalChangeType change_type = change_type_i;
-	GArray *links;
-	NMPlatformLink *cached;
-	int i;
+	NMPLookup lookup;
+	NMDedupMultiIter iter;
+	const NMPlatformLink *cached;
 
 	g_assert_cmpint (obj_type, ==, NMP_OBJECT_TYPE_LINK);
 	g_assert (received);
@@ -185,19 +272,21 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink
 
 	/* Check the data */
 	g_assert (received->ifindex > 0);
-	links = nm_platform_link_get_all (NM_PLATFORM_GET, TRUE);
-	for (i = 0; i < links->len; i++) {
-		cached = &g_array_index (links, NMPlatformLink, i);
+
+	nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK);
+	nmp_cache_iter_for_each_link (&iter,
+	                              nm_platform_lookup (platform, &lookup),
+	                              &cached) {
+		if (!nmp_object_is_visible (NMP_OBJECT_UP_CAST (cached)))
+			continue;
 		if (cached->ifindex == received->ifindex) {
 			g_assert_cmpint (nm_platform_link_cmp (cached, received), ==, 0);
 			g_assert (!memcmp (cached, received, sizeof (*cached)));
 			if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED)
 				g_error ("Deleted link still found in the local cache.");
-			g_array_unref (links);
 			return;
 		}
 	}
-	g_array_unref (links);
 
 	if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED)
 		g_error ("Added/changed link not found in the local cache.");
@@ -205,113 +294,219 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink
 
 /*****************************************************************************/
 
-gboolean
-nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric)
+static const NMPlatformIP4Route *
+_ip4_route_get (NMPlatform *platform,
+                int ifindex,
+                guint32 network,
+                int plen,
+                guint32 metric,
+                guint8 tos,
+                guint *out_c_exists)
 {
-	gs_free char *arg_network = NULL;
-	const char *argv[] = {
-		NULL,
-		"route",
-		"list",
-		"dev",
-		ifname,
-		"exact",
-		NULL,
-		NULL,
-	};
-	int exit_status;
-	gs_free char *std_out = NULL, *std_err = NULL;
-	char *out;
-	gboolean success;
-	gs_free_error GError *error = NULL;
-	gs_free char *metric_pattern = NULL;
+	NMDedupMultiIter iter;
+	NMPLookup lookup;
+	const NMPObject *o = NULL;
+	guint c;
+	const NMPlatformIP4Route *r = NULL;
 
-	g_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL));
-	g_assert (!strstr (ifname, " metric "));
-	g_assert (plen >= 0 && plen <= 32);
+	_init_platform (&platform, FALSE);
 
-	if (!nmtstp_is_root_test ()) {
-		/* If we don't test against linux-platform, we don't actually configure any
-		 * routes in the system. */
-		return -1;
+	nmp_lookup_init_ip4_route_by_weak_id (&lookup,
+	                                      network,
+	                                      plen,
+	                                      metric,
+	                                      tos);
+
+	c = 0;
+	nmp_cache_iter_for_each (&iter,
+	                         nm_platform_lookup (platform, &lookup),
+	                         &o) {
+		if (   NMP_OBJECT_CAST_IP4_ROUTE (o)->ifindex != ifindex
+		    && ifindex > 0)
+			continue;
+		if (!r)
+			r = NMP_OBJECT_CAST_IP4_ROUTE (o);
+		c++;
 	}
 
-	argv[0] = nm_utils_file_search_in_paths ("ip", NULL,
-	                                         (const char *[]) { "/sbin", "/usr/sbin", NULL },
-	                                         G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL);
-	argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen);
+	NM_SET_OUT (out_c_exists, c);
+	return r;
+}
 
-	if (!argv[0]) {
-		/* Hm. There is no 'ip' binary. Return *unknown* */
-		return -1;
+const NMPlatformIP4Route *
+_nmtstp_assert_ip4_route_exists (const char *file,
+                                 guint line,
+                                 const char *func,
+                                 NMPlatform *platform,
+                                 int c_exists,
+                                 const char *ifname,
+                                 guint32 network,
+                                 int plen,
+                                 guint32 metric,
+                                 guint8 tos)
+{
+	int ifindex;
+	guint c;
+	const NMPlatformIP4Route *r = NULL;
+
+	_init_platform (&platform, FALSE);
+
+	ifindex = -1;
+	if (ifname) {
+		ifindex = nm_platform_link_get_ifindex (platform, ifname);
+		g_assert (ifindex > 0);
 	}
 
-	success = g_spawn_sync (NULL,
-	                        (char **) argv,
-	                        (char *[]) { NULL },
-	                        0,
-	                        NULL,
-	                        NULL,
-	                        &std_out,
-	                        &std_err,
-	                        &exit_status,
-	                        &error);
-	g_assert_no_error (error);
-	g_assert (success);
-	g_assert_cmpstr (std_err, ==, "");
-	g_assert (std_out);
-
-	metric_pattern = g_strdup_printf (" metric %u", metric);
-	out = std_out;
-	while (out) {
-		char *eol = strchr (out, '\n');
-		gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out);
-		const char *p;
-
-		out = eol ? &eol[1] : NULL;
-		if (!line[0])
-			continue;
+	r = _ip4_route_get (platform,
+	                    ifindex,
+	                    network,
+	                    plen,
+	                    metric,
+	                    tos,
+	                    &c);
 
-		if (metric == 0) {
-			if (!strstr (line, " metric "))
-				return TRUE;
-		}
-		p = strstr (line, metric_pattern);
-		if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0'))
-			return TRUE;
+	if (c != c_exists && c_exists != -1) {
+		g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but platform has it %u times",
+		         file, line, func,
+		         nm_utils_inet4_ntop (network, NULL), plen,
+		         metric,
+		         tos,
+		         c_exists,
+		         c);
 	}
-	return FALSE;
+
+	return r;
 }
 
-void
-_nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric)
+const NMPlatformIP4Route *
+nmtstp_ip4_route_get (NMPlatform *platform,
+                      int ifindex,
+                      guint32 network,
+                      int plen,
+                      guint32 metric,
+                      guint8 tos)
+{
+	return _ip4_route_get (platform,
+	                       ifindex,
+	                       network,
+	                       plen,
+	                       metric,
+	                       tos,
+	                       NULL);
+}
+
+/*****************************************************************************/
+
+static const NMPlatformIP6Route *
+_ip6_route_get (NMPlatform *platform,
+                int ifindex,
+                const struct in6_addr *network,
+                guint plen,
+                guint32 metric,
+                const struct in6_addr *src,
+                guint8 src_plen,
+                guint *out_c_exists)
+{
+	NMDedupMultiIter iter;
+	NMPLookup lookup;
+	const NMPObject *o = NULL;
+	guint c;
+	const NMPlatformIP6Route *r = NULL;
+
+	_init_platform (&platform, FALSE);
+
+	nmp_lookup_init_ip6_route_by_weak_id (&lookup,
+	                                      network,
+	                                      plen,
+	                                      metric,
+	                                      src,
+	                                      src_plen);
+
+	c = 0;
+	nmp_cache_iter_for_each (&iter,
+	                         nm_platform_lookup (platform, &lookup),
+	                         &o) {
+		if (   NMP_OBJECT_CAST_IP6_ROUTE (o)->ifindex != ifindex
+		    && ifindex > 0)
+			continue;
+		if (!r)
+			r = NMP_OBJECT_CAST_IP6_ROUTE (o);
+		c++;
+	}
+
+	NM_SET_OUT (out_c_exists, c);
+	return r;
+}
+
+const NMPlatformIP6Route *
+_nmtstp_assert_ip6_route_exists (const char *file,
+                                 guint line,
+                                 const char *func,
+                                 NMPlatform *platform,
+                                 int c_exists,
+                                 const char *ifname,
+                                 const struct in6_addr *network,
+                                 guint plen,
+                                 guint32 metric,
+                                 const struct in6_addr *src,
+                                 guint8 src_plen)
 {
 	int ifindex;
-	gboolean exists_checked;
+	guint c;
+	const NMPlatformIP6Route *r = NULL;
 
 	_init_platform (&platform, FALSE);
 
-	/* Check for existance of the route by spawning iproute2. Do this because platform
-	 * code might be entirely borked, but we expect ip-route to give a correct result.
-	 * If the ip command cannot be found, we accept this as success. */
-	exists_checked = nmtstp_ip4_route_exists (ifname, network, plen, metric);
-	if (exists_checked != -1 && !exists_checked != !exists) {
-		g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s",
-		         file, line, func,
-		         nm_utils_inet4_ntop (network, NULL), plen, metric,
-		         exists ? "to exist" : "not to exist",
-		         exists ? "doesn't" : "does");
+	ifindex = -1;
+	if (ifname) {
+		ifindex = nm_platform_link_get_ifindex (platform, ifname);
+		g_assert (ifindex > 0);
 	}
 
-	ifindex = nm_platform_link_get_ifindex (platform, ifname);
-	g_assert (ifindex > 0);
-	if (!nm_platform_ip4_route_get (platform, ifindex, network, plen, metric) != !exists) {
-		g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s",
+	r = _ip6_route_get (platform,
+	                    ifindex,
+	                    network,
+	                    plen,
+	                    metric,
+	                    src,
+	                    src_plen,
+	                    &c);
+
+	if (c != c_exists && c_exists != -1) {
+		char s_src[NM_UTILS_INET_ADDRSTRLEN];
+		char s_network[NM_UTILS_INET_ADDRSTRLEN];
+
+		g_error ("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but platform has it %u times",
 		         file, line, func,
-		         nm_utils_inet4_ntop (network, NULL), plen, metric,
-		         exists ? "exists" : "does not exist",
-		         exists ? "it doesn't" : "it does");
+		         nm_utils_inet6_ntop (network, s_network),
+		         plen,
+		         metric,
+		         nm_utils_inet6_ntop (src, s_src),
+		         src_plen,
+		         c_exists,
+		         c);
 	}
+
+	return r;
+}
+
+const NMPlatformIP6Route *
+nmtstp_ip6_route_get (NMPlatform *platform,
+                      int ifindex,
+                      const struct in6_addr *network,
+                      guint plen,
+                      guint32 metric,
+                      const struct in6_addr *src,
+                      guint8 src_plen)
+{
+	return _ip6_route_get (platform,
+	                       ifindex,
+	                       network,
+	                       plen,
+	                       metric,
+	                       src,
+	                       src_plen,
+	                       NULL);
 }
 
 /*****************************************************************************/
@@ -369,7 +564,7 @@ _wait_for_signal_timeout (gpointer user_data)
 }
 
 guint
-nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms)
+nmtstp_wait_for_signal (NMPlatform *platform, gint64 timeout_ms)
 {
 	WaitForSignalData data = { 0 };
 	gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route;
@@ -384,8 +579,18 @@ nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms)
 	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);
+	/* if timeout_ms is negative, it means the wait-time already expired.
+	 * Maybe, we should do nothing and return right away, without even
+	 * processing events from platform. However, that inconsistency (of not
+	 * processing events from mainloop) is inconvenient.
+	 *
+	 * It's better that on the return of nmtstp_wait_for_signal(), we always
+	 * have no events pending. So, a negative timeout is treated the same as
+	 * a zero timeout: we check whether there are any events pending in platform,
+	 * and quite the mainloop immediately afterwards. But we always check. */
+
+	data.id = g_timeout_add (CLAMP (timeout_ms, 0, G_MAXUINT32),
+	                         _wait_for_signal_timeout, &data);
 
 	g_main_loop_run (data.loop);
 
@@ -414,14 +619,14 @@ nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms)
 		if (until_ms < now)
 			return 0;
 
-		signal_counts = nmtstp_wait_for_signal (platform, MAX (1, until_ms - now));
+		signal_counts = nmtstp_wait_for_signal (platform, until_ms - now);
 		if (signal_counts)
 			return signal_counts;
 	}
 }
 
 const NMPlatformLink *
-nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms)
 {
 	return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
@@ -445,7 +650,7 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType
 		if (until_ms < now)
 			return NULL;
 
-		nmtstp_wait_for_signal (platform, MAX (1, until_ms - now));
+		nmtstp_wait_for_signal (platform, until_ms - now);
 	}
 }
 
@@ -717,7 +922,7 @@ _ip_address_add (NMPlatform *platform,
 			g_assert (label == NULL);
 			g_assert (flags == 0);
 
-			a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen);
+			a = nm_platform_ip6_address_get (platform, ifindex, address->addr6);
 			if (   a
 			    && !memcmp (nm_platform_ip6_address_get_peer (a),
 			                (IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) || IN6_ARE_ADDR_EQUAL (&address->addr6, &peer_address->addr6))
@@ -804,7 +1009,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform,
 	route.metric = metric;
 	route.mss = mss;
 
-	g_assert (nm_platform_ip4_route_add (platform, &route));
+	g_assert_cmpint (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS);
 }
 
 void nmtstp_ip6_route_add (NMPlatform *platform,
@@ -828,7 +1033,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform,
 	route.metric = metric;
 	route.mss = mss;
 
-	g_assert (nm_platform_ip6_route_add (platform, &route));
+	g_assert_cmpint (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS);
 }
 
 /*****************************************************************************/
@@ -861,7 +1066,7 @@ _ip_address_del (NMPlatform *platform,
 		if (is_v4)
 			had_address = !!nm_platform_ip4_address_get (platform, ifindex, address->addr4, plen, peer_address->addr4);
 		else
-			had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen);
+			had_address = !!nm_platform_ip6_address_get (platform, ifindex, address->addr6);
 
 		if (is_v4) {
 			success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s",
@@ -913,7 +1118,7 @@ _ip_address_del (NMPlatform *platform,
 		} else {
 			const NMPlatformIP6Address *a;
 
-			a = nm_platform_ip6_address_get (platform, ifindex, address->addr6, plen);
+			a = nm_platform_ip6_address_get (platform, ifindex, address->addr6);
 			if (!a)
 				break;
 		}
@@ -974,6 +1179,36 @@ nmtstp_ip6_address_del (NMPlatform *platform,
 	} G_STMT_END
 
 const NMPlatformLink *
+nmtstp_link_veth_add (NMPlatform *platform,
+                      gboolean external_command,
+                      const char *name,
+                      const char *peer)
+{
+	const NMPlatformLink *pllink = NULL;
+	gboolean success;
+
+	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+
+	external_command = nmtstp_run_command_check_external (external_command);
+
+	_init_platform (&platform, external_command);
+
+	if (external_command) {
+		success = !nmtstp_run_command ("ip link add dev %s type veth peer name %s",
+		                                name, peer);
+		if (success) {
+			pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_VETH, 100);
+			nmtstp_assert_wait_for_link (platform, peer, NM_LINK_TYPE_VETH, 10);
+		}
+	} else
+		success = nm_platform_link_veth_add (platform, name, peer, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
+
+	g_assert (success);
+	_assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_VETH);
+	return pllink;
+}
+
+const NMPlatformLink *
 nmtstp_link_dummy_add (NMPlatform *platform,
                        gboolean external_command,
                        const char *name)