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.
This commit is contained in:
@@ -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"
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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}')
|
||||
}
|
||||
|
Reference in New Issue
Block a user