/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2015 Red Hat, Inc. */ #include "src/core/nm-default-daemon.h" #include #include #include #include #include #include #include "libnm-lldp/nm-lldp.h" #include "devices/nm-lldp-listener.h" #include "platform/tests/test-common.h" #include "nm-test-utils-core.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]; } TestRecvFixture; typedef struct { gsize frame_len; const uint8_t *frame; const char *as_variant; } TestRecvFrame; #define TEST_RECV_FRAME_DEFINE(name, _as_variant, ...) \ static const guint8 _##name##_v[] = {__VA_ARGS__}; \ static const TestRecvFrame name = { \ .as_variant = _as_variant, \ .frame_len = sizeof(_##name##_v), \ .frame = _##name##_v, \ } typedef struct { int num_called; GMainLoop *loop_to_quit; } TestRecvCallbackInfo; typedef struct { guint expected_num_called; gsize frames_len; const TestRecvFrame *frames[10]; void (*check)(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info); } 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, "{'raw': <[byte 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x88, " "0xcc, 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x04, 0x05, 0x31, 0x2f, " "0x33, 0x06, 0x02, 0x00, 0x78, 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x0a, 0x03, 0x53, 0x59, " "0x53, 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00]>, 'chassis-id-type': , " "'chassis-id': <'00:01:02:03:04:05'>, 'port-id-type': , 'port-id': <'1/3'>, " "'destination': <'nearest-non-tpmr-bridge'>, 'port-description': <'Port'>, 'system-name': " "<'SYS'>, 'system-description': <'foo'>}", /* 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_do(GMainLoop *loop, NMLldpListener *listener, const TestRecvFrame *frame) { 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, NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:02:03:04:05", NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/3"); g_assert(neighbor); g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 4); attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_RAW, G_VARIANT_TYPE_BYTESTRING); nmtst_assert_variant_bytestring(attr, frame->frame, frame->frame_len); nm_clear_g_variant(&attr); 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); } static void _test_recv_data0_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info) { _test_recv_data0_check_do(loop, listener, &_test_recv_data0_frame0); } 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 */ "{'raw': <[byte 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x88, " "0xcc, 0x02, 0x07, 0x04, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x04, 0x04, 0x05, 0x31, 0x2f, " "0x31, 0x06, 0x02, 0x00, 0x78, 0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, 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, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x00, " "0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, 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, 0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, " "0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00, 0xfe, 0x07, 0x00, 0x12, 0x0f, " "0x02, 0x07, 0x01, 0x00, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0x6c, 0x00, 0x00, 0x10, " "0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x00, 0x12, " "0x0f, 0x04, 0x05, 0xf2, 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, 0x01, 0xe8, 0xfe, 0x07, 0x00, " "0x80, 0xc2, 0x02, 0x01, 0x00, 0x00, 0xfe, 0x16, 0x00, 0x80, 0xc2, 0x03, 0x01, 0xe8, 0x0f, " "0x76, 0x32, 0x2d, 0x30, 0x34, 0x38, 0x38, 0x2d, 0x30, 0x33, 0x2d, 0x30, 0x35, 0x30, 0x35, " "0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, 0x00, 0x00, 0x00]>, 'chassis-id-type': , " "'chassis-id': <'00:01:30:F9:AD:A0'>, 'port-id-type': , 'port-id': <'1/1'>, " "'destination': <'nearest-bridge'>, 'port-description': <'Summit300-48-Port 1001'>, " "'system-name': <'Summit300-48'>, 'system-description': <'Summit300-48 - Version 7.4e.1 (Build " "5) by Release_Master 05/27/05 04:53:11'>, 'system-capabilities': , " "'management-addresses': <[{'address-subtype': , 'address': <[byte 0x00, 0x01, 0x30, " "0xf9, 0xad, 0xa0]>, 'interface-number-subtype': , 'interface-number': }]>, 'ieee-802-1-pvid': , 'ieee-802-1-ppvid': , " "'ieee-802-1-ppvid-flags': , 'ieee-802-1-ppvids': <[{'ppvid': , 'flags': " "}]>, 'ieee-802-1-vid': , 'ieee-802-1-vlan-name': <'v2-0488-03-0505'>, " "'ieee-802-1-vlans': <[{'vid': , 'name': <'v2-0488-03-0505'>}]>, " "'ieee-802-3-mac-phy-conf': <{'autoneg': , 'pmd-autoneg-cap': , " "'operational-mau-type': }>, 'ieee-802-3-power-via-mdi': <{'mdi-power-support': " ", 'pse-power-pair': , 'power-class': }>, " "'ieee-802-3-max-frame-size': }", /* 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, 0x16, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */ 0x01, 0xe8, 0x0f, 0x76, 0x32, 0x2d, 0x30, 0x34, 0x38, 0x38, 0x2d, 0x30, 0x33, 0x2d, 0x30, 0x35, 0x30, 0x35, 0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, /* IEEE 802.1 - Protocol Identity */ 0x00, 0x00, 0x00 /* End of LLDPDU */ ); static void _test_recv_data1_check(GMainLoop *loop, NMLldpListener *listener, TestRecvCallbackInfo *info) { GVariant *neighbors, *attr, *child; gs_unref_variant GVariant *neighbor = NULL; guint v_uint = 0; const char *v_str = 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, NM_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0", NM_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1"); g_assert(neighbor); g_assert_cmpint(g_variant_n_children(neighbor), ==, 1 + 4 + 16); attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_RAW, G_VARIANT_TYPE_BYTESTRING); nmtst_assert_variant_bytestring(attr, _test_recv_data1_frame0.frame, _test_recv_data1_frame0.frame_len); 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_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); /* Management Address */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_MANAGEMENT_ADDRESSES, G_VARIANT_TYPE("aa{sv}")); g_assert(attr); g_assert_cmpuint(g_variant_n_children(attr), ==, 1); child = g_variant_get_child_value(attr, 0); g_assert(child); g_assert(g_variant_lookup(child, "interface-number", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 1001); g_assert(g_variant_lookup(child, "interface-number-subtype", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 2); g_assert(g_variant_lookup(child, "address-subtype", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 6); nm_clear_g_variant(&child); nm_clear_g_variant(&attr); /* IEEE 802.3 - Power Via MDI */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_IEEE_802_3_POWER_VIA_MDI, G_VARIANT_TYPE_VARDICT); g_assert(attr); g_assert(g_variant_lookup(attr, "mdi-power-support", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 7); g_assert(g_variant_lookup(attr, "pse-power-pair", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 1); g_assert(g_variant_lookup(attr, "power-class", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 0); nm_clear_g_variant(&attr); /* IEEE 802.3 - MAC/PHY Configuration/Status */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_IEEE_802_3_MAC_PHY_CONF, G_VARIANT_TYPE_VARDICT); g_assert(attr); g_assert(g_variant_lookup(attr, "autoneg", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 3); g_assert(g_variant_lookup(attr, "pmd-autoneg-cap", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 0x6c00); g_assert(g_variant_lookup(attr, "operational-mau-type", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 16); nm_clear_g_variant(&attr); /* unsupported: IEEE 802.3 - Link Aggregation */ /* Maximum Frame Size */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_IEEE_802_3_MAX_FRAME_SIZE, G_VARIANT_TYPE_UINT32); nmtst_assert_variant_uint32(attr, 1522); nm_clear_g_variant(&attr); /* 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); /* new PPVID attributes */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVIDS, G_VARIANT_TYPE("aa{sv}")); g_assert_cmpuint(g_variant_n_children(attr), ==, 1); child = g_variant_get_child_value(attr, 0); g_assert(child); g_assert(g_variant_lookup(child, "ppvid", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 0); g_assert(g_variant_lookup(child, "flags", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 1); nm_clear_g_variant(&child); 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); /* new VLAN attributes */ attr = g_variant_lookup_value(neighbor, NM_LLDP_ATTR_IEEE_802_1_VLANS, G_VARIANT_TYPE("aa{sv}")); g_assert_cmpuint(g_variant_n_children(attr), ==, 1); child = g_variant_get_child_value(attr, 0); g_assert(child); g_assert(g_variant_lookup(child, "vid", "u", &v_uint)); g_assert_cmpint(v_uint, ==, 488); g_assert(g_variant_lookup(child, "name", "&s", &v_str)); g_assert_cmpstr(v_str, ==, "v2-0488-03-0505"); nm_clear_g_variant(&child); 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, "{'raw': <[byte 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x88, " "0xcc, 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x04, 0x05, 0x31, 0x2f, " "0x33, 0x06, 0x02, 0x00, 0x01, 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x0a, 0x03, 0x53, 0x59, " "0x53, 0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00]>, 'chassis-id-type': , " "'chassis-id': <'00:01:02:03:04:05'>, 'port-id-type': , 'port-id': <'1/3'>, " "'destination': <'nearest-non-tpmr-bridge'>, 'port-description': <'Port'>, 'system-name': " "<'SYS'>, 'system-description': <'foo'>}", /* 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, TestRecvCallbackInfo *info) { GVariant *neighbors; _test_recv_data0_check_do(loop, listener, &_test_recv_data2_frame0_ttl1); /* wait for signal. */ info->loop_to_quit = loop; if (!nmtst_main_loop_run(loop, 5000)) g_assert_not_reached(); info->loop_to_quit = 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), ==, 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; nm_auto_close int fd = -1; fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd == -1) { g_test_skip("Unable to open /dev/net/tun"); fixture->ifindex = 0; return; } if (nmtst_get_rand_bool()) { const NMPlatformLnkTun lnk = { .type = IFF_TAP, .pi = FALSE, .vnet_hdr = FALSE, .multi_queue = FALSE, .persist = FALSE, }; nm_close(nm_steal_fd(&fd)); link = nmtstp_link_tun_add(NM_PLATFORM_GET, FALSE, TEST_IFNAME, &lnk, &fd); g_assert(link); nmtstp_link_set_updown(NM_PLATFORM_GET, -1, link->ifindex, TRUE); link = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 0); } else { int s; struct ifreq ifr; int r; int try_cnt = 0; again: memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; nm_utils_ifname_cpy(ifr.ifr_name, TEST_IFNAME); r = ioctl(fd, TUNSETIFF, &ifr); if (r != 0) { if (errno == EPERM && try_cnt++ < 10) { g_usleep(2); goto again; } g_assert_cmpint(errno, ==, 0); g_assert_cmpint(r, ==, 0); } /* Bring the interface up */ s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); g_assert(s >= 0); ifr.ifr_flags |= IFF_UP; r = ioctl(s, SIOCSIFFLAGS, &ifr); if (r != 0) { g_assert_cmpint(errno, ==, 0); g_assert_cmpint(r, ==, 0); } nm_close(s); link = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 100); } fixture->ifindex = link->ifindex; fixture->fd = nm_steal_fd(&fd); memcpy(fixture->mac, link->l_address.data, ETH_ALEN); } static void lldp_neighbors_changed(NMLldpListener *lldp_listener, gpointer user_data) { TestRecvCallbackInfo *info = user_data; info->num_called++; if (info->loop_to_quit) g_main_loop_quit(info->loop_to_quit); } static void test_recv(TestRecvFixture *fixture, gconstpointer user_data) { const TestRecvData *data = user_data; NMLldpListener *listener; GMainLoop *loop; TestRecvCallbackInfo info = {}; gsize i_frames; GError *error = NULL; if (fixture->ifindex == 0) { g_test_skip("Tun device not available"); return; } listener = nm_lldp_listener_new(fixture->ifindex, lldp_neighbors_changed, &info, &error); nmtst_assert_success(listener, error); loop = g_main_loop_new(NULL, FALSE); for (i_frames = 0; i_frames < data->frames_len; i_frames++) { const TestRecvFrame *f = data->frames[i_frames]; g_assert(write(fixture->fd, f->frame, f->frame_len) == f->frame_len); } if (nmtst_main_loop_run(loop, 500)) g_assert_not_reached(); g_assert_cmpint(info.num_called, ==, data->expected_num_called); data->check(loop, listener, &info); nm_clear_pointer(&listener, nm_lldp_listener_destroy); nm_clear_pointer(&loop, g_main_loop_unref); } static void _test_recv_fixture_teardown(TestRecvFixture *fixture, gconstpointer user_data) { if (fixture->ifindex) nm_platform_link_delete(NM_PLATFORM_GET, fixture->ifindex); } /*****************************************************************************/ static void test_parse_frames(gconstpointer test_data) { const TestRecvFrame *frame = test_data; gs_unref_variant GVariant *v_neighbor = NULL; gs_unref_variant GVariant *attr = NULL; gs_free char *as_variant = NULL; v_neighbor = nmtst_lldp_parse_from_raw(frame->frame, frame->frame_len); g_assert(v_neighbor); attr = g_variant_lookup_value(v_neighbor, NM_LLDP_ATTR_RAW, G_VARIANT_TYPE_BYTESTRING); nmtst_assert_variant_bytestring(attr, frame->frame, frame->frame_len); nm_clear_g_variant(&attr); as_variant = g_variant_print(v_neighbor, TRUE); g_assert(as_variant); g_assert_cmpstr(frame->as_variant, ==, as_variant); } /*****************************************************************************/ TEST_RECV_FRAME_DEFINE( _test_parse_frames_3, /* https://github.com/the-tcpdump-group/tcpdump/blob/c4f8796bf8bec740621a360eded236d8991ea00f/tests/lldp_mudurl.pcap */ "{'raw': <[byte 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x54, 0xc2, 0x57, 0x02, 0x88, " "0xcc, 0x02, 0x07, 0x04, 0x00, 0x23, 0x54, 0xc2, 0x57, 0x02, 0x04, 0x07, 0x03, 0x00, 0x23, " "0x54, 0xc2, 0x57, 0x02, 0x06, 0x02, 0x00, 0x78, 0x0a, 0x1c, 0x75, 0x70, 0x73, 0x74, 0x61, " "0x69, 0x72, 0x73, 0x2e, 0x6f, 0x66, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x69, 0x6d, 0x72, " "0x69, 0x67, 0x68, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x5c, 0x55, 0x62, 0x75, 0x6e, 0x74, " "0x75, 0x20, 0x31, 0x34, 0x2e, 0x30, 0x34, 0x2e, 0x35, 0x20, 0x4c, 0x54, 0x53, 0x20, 0x4c, " "0x69, 0x6e, 0x75, 0x78, 0x20, 0x33, 0x2e, 0x31, 0x33, 0x2e, 0x30, 0x2d, 0x31, 0x30, 0x36, " "0x2d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, 0x23, 0x31, 0x35, 0x33, 0x2d, 0x55, " "0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x53, 0x4d, 0x50, 0x20, 0x54, 0x75, 0x65, 0x20, 0x44, " "0x65, 0x63, 0x20, 0x36, 0x20, 0x31, 0x35, 0x3a, 0x34, 0x35, 0x3a, 0x31, 0x33, 0x20, 0x55, " "0x54, 0x43, 0x20, 0x32, 0x30, 0x31, 0x36, 0x20, 0x69, 0x36, 0x38, 0x36, 0x0e, 0x04, 0x00, " "0x9c, 0x00, 0x08, 0x10, 0x0c, 0x05, 0x01, 0x3e, 0x0c, 0xad, 0x72, 0x02, 0x00, 0x00, 0x00, " "0x02, 0x00, 0x10, 0x18, 0x11, 0x02, 0x20, 0x01, 0x08, 0xa8, 0x10, 0x06, 0x00, 0x04, 0x02, " "0x23, 0x54, 0xff, 0xfe, 0xc2, 0x57, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x04, " "0x65, 0x74, 0x68, 0x30, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, " "0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xec, 0xc3, 0x00, 0x10, 0xfe, 0x40, 0x00, 0x00, " "0x5e, 0x01, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x69, 0x6d, 0x72, 0x69, 0x67, " "0x68, 0x74, 0x2e, 0x6d, 0x75, 0x64, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, " "0x63, 0x6f, 0x6d, 0x2f, 0x2e, 0x77, 0x65, 0x6c, 0x6c, 0x2d, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, " "0x2f, 0x6d, 0x75, 0x64, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x6f, 0x6d, 0x69, 0x74, 0x76, 0x32, " "0x2e, 0x30, 0x00, 0x00]>, 'chassis-id-type': , 'chassis-id': <'00:23:54:C2:57:02'>, " "'port-id-type': , 'port-id': <'00:23:54:C2:57:02'>, 'destination': " "<'nearest-bridge'>, 'port-description': <'eth0'>, 'system-name': " "<'upstairs.ofcourseimright.com'>, 'system-description': <'Ubuntu 14.04.5 LTS Linux " "3.13.0-106-generic #153-Ubuntu SMP Tue Dec 6 15:45:13 UTC 2016 i686'>, 'system-capabilities': " ", 'management-addresses': <[{'address-subtype': , 'address': <[byte " "0x3e, 0x0c, 0xad, 0x72]>, 'interface-number-subtype': , 'interface-number': }, {'address-subtype': , 'address': <[byte 0x20, 0x01, 0x08, 0xa8, 0x10, 0x06, " "0x00, 0x04, 0x02, 0x23, 0x54, 0xff, 0xfe, 0xc2, 0x57, 0x02]>, 'interface-number-subtype': " ", 'interface-number': }]>, 'ieee-802-3-mac-phy-conf': <{'autoneg': " ", 'pmd-autoneg-cap': , 'operational-mau-type': }>, " "'mud-url': <'https://imright.mud.example.com/.well-known/mud/v1/vomitv2.0'>}", 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, /* ethernet destination */ 0x00, 0x23, 0x54, 0xc2, 0x57, 0x02, /* ethernet source */ 0x88, 0xcc, /* ethernet type */ 0x02, 0x07, 0x04, 0x00, 0x23, 0x54, 0xc2, 0x57, 0x02, 0x04, 0x07, 0x03, 0x00, 0x23, 0x54, 0xc2, 0x57, 0x02, 0x06, 0x02, 0x00, 0x78, 0x0a, 0x1c, 0x75, 0x70, 0x73, 0x74, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x6f, 0x66, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x69, 0x6d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x5c, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x34, 0x2e, 0x30, 0x34, 0x2e, 0x35, 0x20, 0x4c, 0x54, 0x53, 0x20, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x33, 0x2e, 0x31, 0x33, 0x2e, 0x30, 0x2d, 0x31, 0x30, 0x36, 0x2d, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, 0x23, 0x31, 0x35, 0x33, 0x2d, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x53, 0x4d, 0x50, 0x20, 0x54, 0x75, 0x65, 0x20, 0x44, 0x65, 0x63, 0x20, 0x36, 0x20, 0x31, 0x35, 0x3a, 0x34, 0x35, 0x3a, 0x31, 0x33, 0x20, 0x55, 0x54, 0x43, 0x20, 0x32, 0x30, 0x31, 0x36, 0x20, 0x69, 0x36, 0x38, 0x36, 0x0e, 0x04, 0x00, 0x9c, 0x00, 0x08, 0x10, 0x0c, 0x05, 0x01, 0x3e, 0x0c, 0xad, 0x72, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x18, 0x11, 0x02, 0x20, 0x01, 0x08, 0xa8, 0x10, 0x06, 0x00, 0x04, 0x02, 0x23, 0x54, 0xff, 0xfe, 0xc2, 0x57, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x04, 0x65, 0x74, 0x68, 0x30, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0xec, 0xc3, 0x00, 0x10, 0xfe, 0x40, 0x00, 0x00, 0x5e, 0x01, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x69, 0x6d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x6d, 0x75, 0x64, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x2e, 0x77, 0x65, 0x6c, 0x6c, 0x2d, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x6d, 0x75, 0x64, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x6f, 0x6d, 0x69, 0x74, 0x76, 0x32, 0x2e, 0x30, 0x00, 0x00, /* ethernet trailer */ ); /*****************************************************************************/ NMTstpSetupFunc const _nmtstp_setup_platform_func = nm_linux_platform_setup; void _nmtstp_init_tests(int *argc, char ***argv) { nmtst_init_assert_logging(argc, argv, "WARN", "ALL"); } void _nmtstp_setup_tests(void) { #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); g_test_add_data_func("/lldp/parse-frames/0", &_test_recv_data0_frame0, test_parse_frames); g_test_add_data_func("/lldp/parse-frames/1", &_test_recv_data1_frame0, test_parse_frames); g_test_add_data_func("/lldp/parse-frames/2", &_test_recv_data2_frame0_ttl1, test_parse_frames); g_test_add_data_func("/lldp/parse-frames/3", &_test_parse_frames_3, test_parse_frames); }