From: Lorenzo Arena Date: Wed, 4 Jun 2025 11:21:21 +0000 (+0200) Subject: sd-lldp-rx: add VLAN ID parsing X-Git-Tag: v258-rc1~370^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=496b21ab8edf48ba2c502df2bfeaa7dffe2a5ae2;p=thirdparty%2Fsystemd.git sd-lldp-rx: add VLAN ID parsing Closes #28354. --- diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index e93cf566fa7..0955c8cb0b0 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -315,6 +315,16 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { if (r < 0) return r; } + + /* IEEE 802.1: VLAN ID */ + if (memcmp(p, SD_LLDP_OUI_802_1_VLAN_ID, sizeof(SD_LLDP_OUI_802_1_VLAN_ID)) == 0) { + if (length != (sizeof(SD_LLDP_OUI_802_1_VLAN_ID) + sizeof(uint16_t))) + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Found 802.1 VLAN ID TLV with wrong length, ignoring."); + + n->has_port_vlan_id = true; + n->port_vlan_id = unaligned_read_be16(p + sizeof(SD_LLDP_OUI_802_1_VLAN_ID)); + } break; } @@ -632,6 +642,17 @@ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret return 0; } +int sd_lldp_neighbor_get_port_vlan_id(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->has_port_vlan_id) + return -ENODATA; + + *ret = n->port_vlan_id; + return 0; +} + int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { assert_return(n, -EINVAL); @@ -769,6 +790,8 @@ int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) { *system_name = NULL, *system_description = NULL; uint16_t cc = 0; bool valid_cc; + uint16_t vlanid = 0; + bool valid_vlanid; assert(n); assert(ret); @@ -780,6 +803,7 @@ int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) { (void) sd_lldp_neighbor_get_system_description(n, &system_description); valid_cc = sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0; + valid_vlanid = sd_lldp_neighbor_get_port_vlan_id(n, &vlanid) >= 0; return sd_json_buildo( ret, @@ -790,5 +814,6 @@ int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) { JSON_BUILD_PAIR_STRING_NON_EMPTY("PortDescription", port_description), JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemName", system_name), JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemDescription", system_description), - SD_JSON_BUILD_PAIR_CONDITION(valid_cc, "EnabledCapabilities", SD_JSON_BUILD_UNSIGNED(cc))); + SD_JSON_BUILD_PAIR_CONDITION(valid_cc, "EnabledCapabilities", SD_JSON_BUILD_UNSIGNED(cc)), + SD_JSON_BUILD_PAIR_CONDITION(valid_vlanid, "VlanID", SD_JSON_BUILD_UNSIGNED(vlanid))); } diff --git a/src/libsystemd-network/test-lldp-rx.c b/src/libsystemd-network/test-lldp-rx.c index 7f497a4de43..aa4bfd9b03e 100644 --- a/src/libsystemd-network/test-lldp-rx.c +++ b/src/libsystemd-network/test-lldp-rx.c @@ -9,6 +9,8 @@ #include "sd-lldp-rx.h" #include "fd-util.h" +#include "json-util.h" +#include "lldp-neighbor.h" #include "lldp-network.h" #include "tests.h" @@ -358,6 +360,61 @@ static void test_multiple_neighbors_sorted(sd_event *e) { assert_se(stop_lldp_rx(lldp_rx) == 0); } +static void test_receive_oui_vlanid_packet(sd_event *e) { + sd_lldp_rx *lldp_rx; + sd_lldp_neighbor **neighbors; + sd_json_variant *v; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *neighbor_json = NULL; + static const 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 TLV: interface name, "1/3" */ + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ + /* LLDP optional TLVs */ + 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */ + 0x12, 0x34, + 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* Port and protocol: flag 1, PPVID 0x7788 */ + 0x01, 0x77, 0x88, + 0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03, /* VLAN Name: ID 0x1234, name "Vlan51" */ + 0x12, 0x34, 0x06, 0x56, 0x6c, 0x61, + 0x6e, 0x35, 0x31, + 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06, /* Management VID: 0x0102 */ + 0x01, 0x02, + 0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07, /* Link aggregation: status 1, ID 0x00140012 */ + 0x01, 0x00, 0x14, 0x00, 0x12, + 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* 802.3 Power via MDI: PSE, MDI enabled */ + 0x07, 0x01, 0x00, + 0x00, 0x00 /* End of LLDPDU */ + }; + uint16_t vlanid; + + lldp_rx_handler_calls = 0; + ASSERT_OK(start_lldp_rx(&lldp_rx, e, lldp_rx_handler, NULL)); + + ASSERT_OK_EQ_ERRNO(write(test_fd[1], frame, sizeof(frame)), (ssize_t)sizeof(frame)); + ASSERT_OK(sd_event_run(e, 0)); + ASSERT_EQ(lldp_rx_handler_calls, 1); + ASSERT_OK_EQ(sd_lldp_rx_get_neighbors(lldp_rx, &neighbors), 1); + + ASSERT_OK(sd_lldp_neighbor_get_port_vlan_id(neighbors[0], &vlanid)); + ASSERT_EQ(vlanid, 0x1234); + + ASSERT_OK(lldp_neighbor_build_json(neighbors[0], &neighbor_json)); + ASSERT_NOT_NULL(v = sd_json_variant_by_key(neighbor_json, "VlanID")); + ASSERT_EQ(sd_json_variant_type(v), SD_JSON_VARIANT_UNSIGNED); + ASSERT_EQ(sd_json_variant_unsigned(v), UINT64_C(0x1234)); + + sd_lldp_neighbor_unref(neighbors[0]); + free(neighbors); + + ASSERT_OK(stop_lldp_rx(lldp_rx)); +} + int main(int argc, char *argv[]) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; @@ -369,6 +426,7 @@ int main(int argc, char *argv[]) { test_receive_incomplete_packet(e); test_receive_oui_packet(e); test_multiple_neighbors_sorted(e); + test_receive_oui_vlanid_packet(e); return 0; } diff --git a/src/network/networkctl-lldp.c b/src/network/networkctl-lldp.c index 43727a1b82f..300f7b26df9 100644 --- a/src/network/networkctl-lldp.c +++ b/src/network/networkctl-lldp.c @@ -44,6 +44,7 @@ typedef struct LLDPNeighborInfo { const char *system_name; const char *system_description; uint16_t capabilities; + uint16_t vlan_id; } LLDPNeighborInfo; static const sd_json_dispatch_field lldp_neighbor_dispatch_table[] = { @@ -53,6 +54,7 @@ static const sd_json_dispatch_field lldp_neighbor_dispatch_table[] = { { "SystemName", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LLDPNeighborInfo, system_name), 0 }, { "SystemDescription", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LLDPNeighborInfo, system_description), 0 }, { "EnabledCapabilities", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LLDPNeighborInfo, capabilities), 0 }, + { "VlanID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LLDPNeighborInfo, vlan_id), 0 }, {}, }; diff --git a/src/shared/varlink-io.systemd.Network.c b/src/shared/varlink-io.systemd.Network.c index fe0aecf1f14..fd8ba3c085f 100644 --- a/src/shared/varlink-io.systemd.Network.c +++ b/src/shared/varlink-io.systemd.Network.c @@ -25,7 +25,8 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_DEFINE_FIELD(PortDescription, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(SystemName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(SystemDescription, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), - SD_VARLINK_DEFINE_FIELD(EnabledCapabilities, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); + SD_VARLINK_DEFINE_FIELD(EnabledCapabilities, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_DEFINE_FIELD(VlanID, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_STRUCT_TYPE( LLDPNeighborsByInterface, diff --git a/src/systemd/sd-lldp-rx.h b/src/systemd/sd-lldp-rx.h index 552ab813293..cb76781e1e6 100644 --- a/src/systemd/sd-lldp-rx.h +++ b/src/systemd/sd-lldp-rx.h @@ -84,6 +84,7 @@ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_port_vlan_id(sd_lldp_neighbor *n, uint16_t *ret); /* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index 7c68dc6e331..938e2d6402f 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -87,7 +87,13 @@ enum { SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \ SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) -#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 } +#define _SD_LLDP_OUI_802_1 0x00, 0x80, 0xc2 +#define SD_LLDP_OUI_802_1 (const uint8_t[]) { _SD_LLDP_OUI_802_1 } + +#define SD_LLDP_OUI_802_1_SUBTYPE_VLAN_ID 0x01 +#define SD_LLDP_OUI_802_1_VLAN_ID \ + (const uint8_t[]) { _SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_ID } + #define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f } #define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E