diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2018-12-20 17:03:16 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2019-03-27 10:47:24 +0100 |
commit | 6c52d946fc8cbe148cbf6d95e35e8b89084b8fe1 (patch) | |
tree | c05db04b5b96758c4aeec869d461c54da32b4f70 | |
parent | 937796f6ea78c320e90e909330799a56df56b911 (diff) | |
download | NetworkManager-6c52d946fc8cbe148cbf6d95e35e8b89084b8fe1.tar.gz |
lldp: add support for management address TLV
Support the management address TLV (IEEE 802.1AB-2009 clause
8.5.9). The TLV can appear multiple times and so it is exported on
D-Bus as an array of dictionaries.
-rw-r--r-- | libnm-core/nm-dbus-interface.h | 1 | ||||
-rw-r--r-- | src/devices/nm-lldp-listener.c | 147 | ||||
-rw-r--r-- | src/devices/tests/test-lldp.c | 21 | ||||
-rwxr-xr-x | tools/test-networkmanager-service.py | 15 |
4 files changed, 177 insertions, 7 deletions
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 038d2c9ee0..3465726c83 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -837,6 +837,7 @@ typedef enum /*< flags >*/ { #define NM_LLDP_ATTR_SYSTEM_NAME "system-name" #define NM_LLDP_ATTR_SYSTEM_DESCRIPTION "system-description" #define NM_LLDP_ATTR_SYSTEM_CAPABILITIES "system-capabilities" +#define NM_LLDP_ATTR_MANAGEMENT_ADDRESSES "management-addresses" #define NM_LLDP_ATTR_IEEE_802_1_PVID "ieee-802-1-pvid" #define NM_LLDP_ATTR_IEEE_802_1_PPVID "ieee-802-1-ppvid" #define NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS "ieee-802-1-ppvid-flags" diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index 96d8ea144f..55170985d9 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -26,6 +26,7 @@ #include "platform/nm-platform.h" #include "nm-utils/unaligned.h" +#include "nm-utils/nm-c-list.h" #include "nm-utils.h" #include "systemd/nm-sd.h" @@ -41,6 +42,7 @@ typedef enum { LLDP_ATTR_TYPE_NONE, LLDP_ATTR_TYPE_UINT32, LLDP_ATTR_TYPE_STRING, + LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS, } LldpAttrType; typedef enum { @@ -50,6 +52,7 @@ typedef enum { LLDP_ATTR_ID_SYSTEM_NAME, LLDP_ATTR_ID_SYSTEM_DESCRIPTION, LLDP_ATTR_ID_SYSTEM_CAPABILITIES, + LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, LLDP_ATTR_ID_IEEE_802_1_PVID, LLDP_ATTR_ID_IEEE_802_1_PPVID, LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, @@ -63,6 +66,7 @@ typedef struct { union { guint32 v_uint32; char *v_string; + CList v_variant_list; }; } LldpAttrData; @@ -164,6 +168,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_lldp_attr_id_to_name, LldpAttrId, NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME, NM_LLDP_ATTR_SYSTEM_NAME), NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION, NM_LLDP_ATTR_SYSTEM_DESCRIPTION), NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES, NM_LLDP_ATTR_SYSTEM_CAPABILITIES), + NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, NM_LLDP_ATTR_MANAGEMENT_ADDRESSES), NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID, NM_LLDP_ATTR_IEEE_802_1_PVID), NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID, NM_LLDP_ATTR_IEEE_802_1_PPVID), NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS), @@ -178,6 +183,7 @@ _NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_NAME, LLDP_ATTR_TYPE_STRING), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION, LLDP_ATTR_TYPE_STRING), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES, LLDP_ATTR_TYPE_UINT32), + NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID, LLDP_ATTR_TYPE_UINT32), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID, LLDP_ATTR_TYPE_UINT32), NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS, LLDP_ATTR_TYPE_UINT32), @@ -253,6 +259,24 @@ _lldp_attr_set_uint32 (LldpAttrData *pdata, LldpAttrId attr_id, guint32 v_uint32 pdata->v_uint32 = v_uint32; } +static void +_lldp_attr_add_vardict (LldpAttrData *pdata, LldpAttrId attr_id, GVariant *variant) +{ + nm_assert (pdata); + nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS); + + g_variant_ref_sink (variant); + pdata = &pdata[attr_id]; + + if (pdata->attr_type == LLDP_ATTR_TYPE_NONE) { + c_list_init (&pdata->v_variant_list); + pdata->attr_type = LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS; + } else + nm_assert (pdata->attr_type == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS); + + c_list_link_tail (&pdata->v_variant_list, &nm_c_list_elem_new_stale (variant)->lst); +} + /*****************************************************************************/ static guint @@ -298,13 +322,25 @@ static void lldp_neighbor_free (LldpNeighbor *neighbor) { LldpAttrId attr_id; + LldpAttrType attr_type; if (neighbor) { g_free (neighbor->chassis_id); g_free (neighbor->port_id); for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) { - if (neighbor->attrs[attr_id].attr_type == LLDP_ATTR_TYPE_STRING) + attr_type = neighbor->attrs[attr_id].attr_type; + + switch (attr_type) { + case LLDP_ATTR_TYPE_STRING: g_free (neighbor->attrs[attr_id].v_string); + break; + case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS: + nm_c_list_elem_free_all (&neighbor->attrs[attr_id].v_variant_list, + (GDestroyNotify) g_variant_unref); + break; + default: + ; + } } g_clear_pointer (&neighbor->variant, g_variant_unref); g_slice_free (LldpNeighbor, neighbor); @@ -353,6 +389,75 @@ lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b) return TRUE; } +static GVariant * +parse_management_address_tlv (uint8_t *data, gsize len) +{ + GVariantDict dict; + GVariant *variant; + gsize addr_len, oid_len; + + /* 802.1AB-2009 - Figure 8-11 + * + * - TLV type / length (2 bytes) + * - address string length (1 byte) + * - address subtype (1 byte) + * - address (1 to 31 bytes) + * - interface number subtype (1 byte) + * - interface number (4 bytes) + * - OID string length (1 byte) + * - OID (0 to 128 bytes) + */ + + if (len < 11) + goto err; + + nm_assert ((data[0] >> 1) == SD_LLDP_TYPE_MGMT_ADDRESS); + nm_assert ((((data[0] & 1) << 8) + data[1]) + 2 == len); + + data += 2; + len -= 2; + addr_len = *data; /* length of (address subtype + address) */ + + if (addr_len < 2 || addr_len > 32) + goto err; + if (len < ( 1 /* address stringth length */ + + addr_len /* address subtype + address */ + + 5 /* interface */ + + 1)) /* oid */ + goto err; + + g_variant_dict_init (&dict, NULL); + + data++; + len--; + g_variant_dict_insert (&dict, "address-subtype", "u", (guint32) *data); + variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data + 1, addr_len - 1, 1); + g_variant_dict_insert_value (&dict, "address", variant); + + data += addr_len; + len -= addr_len; + g_variant_dict_insert (&dict, "interface-number-subtype", "u", (guint32) *data); + + data++; + len--; + g_variant_dict_insert (&dict, "interface-number", "u", unaligned_read_be32 (data)); + + data += 4; + len -= 4; + oid_len = *data; + + if (len < (1 + oid_len)) + goto err; + + data++; + variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, oid_len, 1); + g_variant_dict_insert_value (&dict, "object-id", variant); + return g_variant_dict_end (&dict); +err: + g_variant_dict_clear (&dict); + return NULL; +} + static LldpNeighbor * lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) { @@ -454,7 +559,29 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) } do { guint8 oui[3]; - guint8 subtype; + guint8 type, subtype; + GVariant *variant; + + if (sd_lldp_neighbor_tlv_get_type (neighbor_sd, &type) < 0) + continue; + + if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0) + continue; + + switch (type) { + case SD_LLDP_TYPE_MGMT_ADDRESS: + variant = parse_management_address_tlv (data8, len); + if (variant) { + _lldp_attr_add_vardict (neigh->attrs, + LLDP_ATTR_ID_MANAGEMENT_ADDRESSES, + variant); + } + continue; + case SD_LLDP_TYPE_PRIVATE: + break; + default: + continue; + } r = sd_lldp_neighbor_tlv_get_oui (neighbor_sd, oui, &subtype); if (r < 0) { @@ -472,8 +599,6 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME))) continue; - if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0) - continue; /* skip over leading TLV, OUI and subtype */ #ifdef WITH_MORE_ASSERTS @@ -594,6 +719,20 @@ lldp_neighbor_to_variant (LldpNeighbor *neigh) _lldp_attr_id_to_name (attr_id), g_variant_new_string (data->v_string)); break; + case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS: { + NMCListElem *elem; + GVariantBuilder builder2; + + g_variant_builder_init (&builder2, G_VARIANT_TYPE ("aa{sv}")); + + c_list_for_each_entry (elem, &data->v_variant_list, lst) + g_variant_builder_add_value (&builder2, elem->data); + + g_variant_builder_add (&builder, "{sv}", + _lldp_attr_id_to_name (attr_id), + g_variant_builder_end (&builder2)); + break; + } default: break; } diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index 7227d08264..e045d29928 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -231,8 +231,9 @@ TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0, static void _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) { - GVariant *neighbors, *attr; + GVariant *neighbors, *attr, *child; gs_unref_variant GVariant *neighbor = NULL; + guint v_uint = 0; neighbors = nm_lldp_listener_get_neighbors (listener); nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}")); @@ -242,7 +243,7 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0", SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1"); g_assert (neighbor); - g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10); + g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 11); attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING); nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE); @@ -270,7 +271,21 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener) nmtst_assert_variant_uint32 (attr, 20); nm_clear_g_variant (&attr); - /* unsupported: Management Address */ + /* 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_variant_lookup (child, "interface-number", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 1001); + g_variant_lookup (child, "interface-number-subtype", "u", &v_uint); + g_assert_cmpint (v_uint, ==, 2); + 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); + /* unsupported: IEEE 802.3 - Power Via MDI */ /* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */ /* unsupported: IEEE 802.3 - Link Aggregation */ diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 323e052688..fe680fa215 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -753,6 +753,21 @@ class Device(ExportedObj): 'system-description': dbus.String('Test system #3'), 'system-capabilities': dbus.UInt32(40), 'destination': dbus.String('nearest-customer-bridge'), + 'management-addresses': dbus.Array([ + dbus.Dictionary({ + 'address-subtype': dbus.UInt32(1), + 'address': dbus.ByteArray(b'\xc0\xa8\x01\x01'), + 'interface-number': dbus.UInt32(4), + 'interface-number-subtype': dbus.UInt32(3), + 'object-id': dbus.ByteArray(b'\x01\x02\x03\x04') + }, signature = 'sv'), + dbus.Dictionary({ + 'address-subtype': dbus.UInt32(2), + 'address': dbus.ByteArray(b'\xfd\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x34\x56\x78'), + 'interface-number': dbus.UInt32(1), + 'interface-number-subtype': dbus.UInt32(2), + }, signature = 'sv'), + ]) }) ], 'a{sv}') } |