summary refs log tree commit diff
path: root/src/devices/tests/test-lldp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/tests/test-lldp.c')
-rw-r--r--src/devices/tests/test-lldp.c466
1 files changed, 347 insertions, 119 deletions
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index dfae58b1..85c3b510 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -27,18 +27,327 @@
 #include <sys/types.h>
 
 #include "nm-lldp-listener.h"
+#include "nm-sd.h"
+
+#include "nm-sd-adapt.h"
+#include "lldp.h"
+
 #include "test-common.h"
 
+#include "nm-test-utils.h"
+
+/*****************************************************************************/
+
+static GVariant *
+get_lldp_neighbor (GVariant *neighbors,
+                   int chassis_id_type,
+                   const char *chassis_id,
+                   int port_id_type,
+                   const char *port_id)
+{
+	GVariantIter iter;
+	GVariant *variant;
+	GVariant *result = NULL;
+
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+
+	g_assert (chassis_id_type >= -1 && chassis_id_type <= G_MAXUINT8);
+	g_assert (port_id_type >= -1 && port_id_type <= G_MAXUINT8);
+
+	g_variant_iter_init (&iter, neighbors);
+	while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
+		gs_unref_variant GVariant *v_chassis_id_type = NULL;
+		gs_unref_variant GVariant *v_chassis_id = NULL;
+		gs_unref_variant GVariant *v_port_id_type = NULL;
+		gs_unref_variant GVariant *v_port_id = NULL;
+
+		v_chassis_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID_TYPE, G_VARIANT_TYPE_UINT32);
+		g_assert (v_chassis_id_type);
+
+		v_chassis_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID, G_VARIANT_TYPE_STRING);
+		g_assert (v_chassis_id);
+
+		v_port_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID_TYPE, G_VARIANT_TYPE_UINT32);
+		g_assert (v_port_id_type);
+
+		v_port_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID, G_VARIANT_TYPE_STRING);
+		g_assert (v_port_id);
+
+		if (   nm_streq (g_variant_get_string (v_chassis_id, NULL), chassis_id)
+		    && nm_streq (g_variant_get_string (v_port_id, NULL), port_id)
+		    && NM_IN_SET (chassis_id_type, -1, g_variant_get_uint32 (v_chassis_id_type))
+		    && NM_IN_SET (port_id_type, -1, g_variant_get_uint32 (v_port_id_type))) {
+			g_assert (!result);
+			result = variant;
+		} else
+			g_variant_unref (variant);
+	}
+
+	return result;
+}
+
 typedef struct {
 	int ifindex;
 	int fd;
 	guint8 mac[ETH_ALEN];
-} test_fixture;
+} TestRecvFixture;
+
+typedef struct {
+	gsize frame_len;
+	const uint8_t *frame;
+} TestRecvFrame;
+#define TEST_RECV_FRAME_DEFINE(name, ...) \
+	static const guint8 _##name##_v[] = { __VA_ARGS__ }; \
+	static const TestRecvFrame name = { \
+		.frame_len = sizeof (_##name##_v), \
+		.frame = _##name##_v, \
+	}
+
+typedef struct {
+	guint expected_num_called;
+	gsize frames_len;
+	const TestRecvFrame *frames[10];
+	void (*check) (GMainLoop *loop, NMLldpListener *listener);
+} TestRecvData;
+#define TEST_RECV_DATA_DEFINE(name, _expected_num_called, _check, ...) \
+	static const TestRecvData name = { \
+		.expected_num_called = _expected_num_called, \
+		.check = _check, \
+		.frames_len = NM_NARG (__VA_ARGS__), \
+		.frames = { __VA_ARGS__ }, \
+	}
 
 #define TEST_IFNAME "nm-tap-test0"
 
+TEST_RECV_FRAME_DEFINE (_test_recv_data0_frame0,
+	/* Ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+	0x88, 0xcc,                             /* Ethertype */
+	/* LLDP mandatory TLVs */
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+	0x03, 0x04, 0x05,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+	0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
+	/* LLDP optional TLVs */
+	0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+	0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+	0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+	0x00, 0x00                              /* End Of LLDPDU */
+);
+
+static void
+_test_recv_data0_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	GVariant *neighbors, *attr;
+	gs_unref_variant GVariant *neighbor = NULL;
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
+
+	neighbor = get_lldp_neighbor (neighbors,
+	                              LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:02:03:04:05",
+	                              LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/3");
+	g_assert (neighbor);
+	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 4);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Port");
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "SYS");
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "foo");
+	nm_clear_g_variant (&attr);
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data0,       1, _test_recv_data0_check,  &_test_recv_data0_frame0);
+TEST_RECV_DATA_DEFINE (_test_recv_data0_twice, 1, _test_recv_data0_check,  &_test_recv_data0_frame0, &_test_recv_data0_frame0);
+
+
+TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0,
+	/* lldp.detailed.pcap from
+	 * https://wiki.wireshark.org/SampleCaptures#Link_Layer_Discovery_Protocol_.28LLDP.29 */
+
+	/* ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, /* destination mac */
+	0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, /* source mac */
+	0x88, 0xcc,                         /* ethernet type */
+
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x30, /* Chassis Subtype */
+	0xf9, 0xad, 0xa0,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x31, /* Port Subtype */
+	0x06, 0x02, 0x00, 0x78,             /* Time To Live */
+	0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, /* Port Description */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x2d, 0x50, 0x6f, 0x72,
+	0x74, 0x20, 0x31, 0x30, 0x30, 0x31,
+	0x00,
+	0x0a, 0x0d, 0x53, 0x75, 0x6d, 0x6d, /* System Name */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x00,
+	0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, /* System Description */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x20, 0x2d, 0x20, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e,
+	0x31, 0x20, 0x28, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x20, 0x35, 0x29, 0x20,
+	0x62, 0x79, 0x20, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d,
+	0x61, 0x73, 0x74, 0x65, 0x72, 0x20,
+	0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f,
+	0x30, 0x35, 0x20, 0x30, 0x34, 0x3a,
+	0x35, 0x33, 0x3a, 0x31, 0x31, 0x00,
+	0x0e, 0x04, 0x00, 0x14, 0x00, 0x14, /* Capabilities */
+	0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, /* Management Address */
+	0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00,
+	0x00, 0x03, 0xe9, 0x00,
+	0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* IEEE 802.3 - Power Via MDI */
+	0x07, 0x01, 0x00,
+	0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, /* IEEE 802.3 - MAC/PHY Configuration/Status */
+	0x03, 0x6c, 0x00, 0x00, 0x10,
+	0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, /* IEEE 802.3 - Link Aggregation */
+	0x01, 0x00, 0x00, 0x00, 0x00,
+	0xfe, 0x06, 0x00, 0x12, 0x0f, 0x04, /* IEEE 802.3 - Maximum Frame Size */
+	0x05, 0xf2,
+	0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* IEEE 802.1 - Port VLAN ID */
+	0x01, 0xe8,
+	0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* IEEE 802.1 - Port and Protocol VLAN ID */
+	0x01, 0x00, 0x00,
+	0xfe, 0x17, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */
+	0x01, 0xe8, 0x10, 0x76, 0x32, 0x2d,
+	0x30, 0x34, 0x38, 0x38, 0x2d, 0x30,
+	0x33, 0x2d, 0x30, 0x35, 0x30, 0x35,
+	0x00,
+	0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, /* IEEE 802.1 - Protocol Identity */
+	0x00,
+	0x00, 0x00                          /* End of LLDPDU */
+);
+
 static void
-fixture_setup (test_fixture *fixture, gconstpointer user_data)
+_test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	GVariant *neighbors, *attr;
+	gs_unref_variant GVariant *neighbor = NULL;
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
+
+	neighbor = get_lldp_neighbor (neighbors,
+	                              LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0",
+	                              LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1");
+	g_assert (neighbor);
+	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: Time To Live */
+
+	/* Port Description */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48-Port 1001");
+	nm_clear_g_variant (&attr);
+
+	/* System Name */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48");
+	nm_clear_g_variant (&attr);
+
+	/* System Description */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48 - Version 7.4e.1 (Build 5) by Release_Master 05/27/05 04:53:11");
+	nm_clear_g_variant (&attr);
+
+	/* Capabilities */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 20);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: Management Address */
+	/* unsupported: IEEE 802.3 - Power Via MDI */
+	/* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */
+	/* unsupported: IEEE 802.3 - Link Aggregation */
+	/* unsupported: IEEE 802.3 - Maximum Frame Size*/
+
+	/* IEEE 802.1 - Port VLAN ID */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PVID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 488);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.1 - Port and Protocol VLAN ID */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 0);
+	nm_clear_g_variant (&attr);
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 1);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.1 - VLAN Name */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "v2-0488-03-0505");
+	nm_clear_g_variant (&attr);
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 488);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: IEEE 802.1 - Protocol Identity */
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data1,       1, _test_recv_data1_check,  &_test_recv_data1_frame0);
+
+TEST_RECV_FRAME_DEFINE (_test_recv_data2_frame0_ttl1,
+	/* Ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+	0x88, 0xcc,                             /* Ethertype */
+	/* LLDP mandatory TLVs */
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+	0x03, 0x04, 0x05,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+	0x06, 0x02, 0x00, 0x01,                 /* TTL: 1 seconds */
+	/* LLDP optional TLVs */
+	0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+	0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+	0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+	0x00, 0x00                              /* End Of LLDPDU */
+);
+
+static void
+_test_recv_data2_ttl1_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	gulong notify_id;
+	GVariant *neighbors;
+
+	_test_recv_data0_check (loop, listener);
+
+	/* wait for signal. */
+	notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+	                              nmtst_main_loop_quit_on_notify, loop);
+	if (!nmtst_main_loop_run (loop, 5000))
+		g_assert_not_reached ();
+	nm_clear_g_signal_handler (listener, &notify_id);
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 0);
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data2_ttl1, 1, _test_recv_data2_ttl1_check,  &_test_recv_data2_frame0_ttl1);
+
+static void
+_test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 {
 	const NMPlatformLink *link;
 	struct ifreq ifr = { };
@@ -48,7 +357,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 	g_assert (fd >= 0);
 
 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-	strncpy (ifr.ifr_name, TEST_IFNAME, IFNAMSIZ);
+	nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
 	g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
 
 	/* Bring the interface up */
@@ -58,9 +367,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 	g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
 	close (s);
 
-	nm_platform_process_events (NM_PLATFORM_GET);
-	link = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, TEST_IFNAME);
-	g_assert (link);
+	link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TAP, 100);
 	fixture->ifindex = link->ifindex;
 	fixture->fd = fd;
 	memcpy (fixture->mac, link->addr.data, ETH_ALEN);
@@ -68,149 +375,66 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 
 typedef struct {
 	int num_called;
-} TestInfo;
-
-static gboolean
-loop_quit (gpointer user_data)
-{
-	g_main_loop_quit ((GMainLoop *) user_data);
-	return G_SOURCE_REMOVE;
-}
+} TestRecvCallbackInfo;
 
 static void
 lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
                         gpointer user_data)
 {
-	TestInfo *info = user_data;
+	TestRecvCallbackInfo *info = user_data;
 
 	info->num_called++;
 }
 
-static GVariant *
-get_lldp_neighbor_attribute (GVariant *neighbors,
-                             const char *chassis, const char *port,
-                             const char *name)
-{
-	GVariantIter iter, attrs_iter;
-	GVariant *variant, *attr_variant;
-	const char *attr_name;
-
-	g_return_val_if_fail (g_variant_is_of_type (neighbors,
-	                                            G_VARIANT_TYPE ("aa{sv}")),
-	                      NULL);
-	g_variant_iter_init (&iter, neighbors);
-
-	while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
-		gs_unref_variant GVariant *chassis_v = NULL;
-		gs_unref_variant GVariant *port_v = NULL;
-		gs_unref_variant GVariant *attr_v = NULL;
-
-		g_variant_iter_init (&attrs_iter, variant);
-		while (g_variant_iter_next (&attrs_iter, "{&sv}", &attr_name, &attr_variant)) {
-			if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_CHASSIS_ID)) {
-				g_assert (!chassis_v);
-				chassis_v = attr_variant;
-			} else if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_PORT_ID)) {
-				g_assert (!port_v);
-				port_v = attr_variant;
-			} else if (!g_strcmp0 (attr_name, name)) {
-				g_assert (!attr_v);
-				attr_v = attr_variant;
-			} else
-				g_variant_unref (attr_variant);
-		}
-
-		g_variant_unref (variant);
-
-		if (   chassis_v
-		    && port_v
-		    && g_variant_is_of_type (chassis_v, G_VARIANT_TYPE_STRING)
-		    && g_variant_is_of_type (port_v, G_VARIANT_TYPE_STRING)
-		    && !g_strcmp0 (chassis, g_variant_get_string (chassis_v, NULL))
-		    && !g_strcmp0 (port, g_variant_get_string (port_v, NULL)))
-			return g_variant_ref (attr_v);
-	}
-
-	/* neighbor not found */
-	return NULL;
-}
-
 static void
-test_receive_frame (test_fixture *fixture, gconstpointer user_data)
+test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 {
+	const TestRecvData *data = user_data;
 	gs_unref_object NMLldpListener *listener = NULL;
 	GMainLoop *loop;
-	TestInfo info = { };
-	GVariant *neighbors, *attr;
-	uint8_t frame[] = {
-		/* Ethernet header */
-		0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
-		0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
-		0x88, 0xcc,                             /* Ethertype */
-		/* LLDP mandatory TLVs */
-		0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
-		0x03, 0x04, 0x05,
-		0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
-		0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-		/* LLDP optional TLVs */
-		0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
-		0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
-		0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
-		0x00, 0x00                              /* End Of LLDPDU */
-	};
+	TestRecvCallbackInfo info = { };
+	gsize i_frames;
+	gulong notify_id;
+	GError *error = NULL;
+	guint sd_id;
 
 	listener = nm_lldp_listener_new ();
 	g_assert (listener != NULL);
-	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, TEST_IFNAME, fixture->mac, ETH_ALEN, NULL));
+	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, &error));
+	g_assert_no_error (error);
 
-	g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-	                  (GCallback) lldp_neighbors_changed, &info);
+	notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+	                              (GCallback) lldp_neighbors_changed, &info);
 	loop = g_main_loop_new (NULL, FALSE);
-	g_timeout_add_seconds (1, loop_quit, loop);
+	sd_id = nm_sd_event_attach_default ();
+
+	for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
+		const TestRecvFrame *f = data->frames[i_frames];
 
-	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
-	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
+		g_assert (write (fixture->fd, f->frame, f->frame_len) == f->frame_len);
+	}
 
-	g_main_loop_run (loop);
+	if (nmtst_main_loop_run (loop, 500))
+		g_assert_not_reached ();
 
-	g_assert_cmpint (info.num_called, ==, 1);
-	neighbors = nm_lldp_listener_get_neighbors (listener);
-	g_assert (neighbors != NULL);
-
-	/* Check port description */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_PORT_DESCRIPTION);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "Port");
-	nm_clear_g_variant (&attr);
+	g_assert_cmpint (info.num_called, ==, data->expected_num_called);
 
-	/* Check system name */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_SYSTEM_NAME);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "SYS");
-	nm_clear_g_variant (&attr);
+	nm_clear_g_signal_handler (listener, &notify_id);
 
-	/* Check destination */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_DESTINATION);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==,
-	                 NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
-	nm_clear_g_variant (&attr);
+	data->check (loop, listener);
 
+	nm_clear_g_source (&sd_id);
 	g_clear_pointer (&loop, g_main_loop_unref);
 }
 
 static void
-fixture_teardown (test_fixture *fixture, gconstpointer user_data)
+_test_recv_fixture_teardown (TestRecvFixture *fixture, gconstpointer user_data)
 {
 	nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
 }
 
+/*****************************************************************************/
+
 void
 init_tests (int *argc, char ***argv)
 {
@@ -220,6 +444,10 @@ init_tests (int *argc, char ***argv)
 void
 setup_tests (void)
 {
-	g_test_add ("/lldp/receive_frame", test_fixture, NULL, fixture_setup,
-	            test_receive_frame, fixture_teardown);
+#define _TEST_ADD_RECV(testpath, testdata) \
+	g_test_add (testpath, TestRecvFixture, testdata, _test_recv_fixture_setup, test_recv, _test_recv_fixture_teardown)
+	_TEST_ADD_RECV ("/lldp/recv/0",       &_test_recv_data0);
+	_TEST_ADD_RECV ("/lldp/recv/0_twice", &_test_recv_data0_twice);
+	_TEST_ADD_RECV ("/lldp/recv/1",       &_test_recv_data1);
+	_TEST_ADD_RECV ("/lldp/recv/2_ttl1",  &_test_recv_data2_ttl1);
 }