From 27546b769c6f1896a3fbbd3453a3eb65693e23ae Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 04:03:26 +0900 Subject: [PATCH 1/8] sd-lldp-tx: do not expose machine ID by default Previously, systemd-networkd sent machine ID as chassis ID. Let's use application specific machine ID. This is a kind of backward compat breaking. Hence, this also introduces the support of $SD_LLDP_SEND_MACHINE_ID environment variable. Closes #37613. --- NEWS | 7 +++++++ docs/ENVIRONMENT.md | 3 +++ src/libsystemd-network/sd-lldp-tx.c | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 54c7cdb487..dd6f21dc33 100644 --- a/NEWS +++ b/NEWS @@ -101,6 +101,13 @@ CHANGES WITH 258 in spe: IPv4DuplicateAddressDetectionTimeoutSec=. The default timeout value has been changed from 7 seconds to 200 milliseconds. + * systemd-networkd previously emitted the machine ID as chassis ID + through LLDP protocol, but now emits a deterministic ID, + cryptographically derived from the machine ID as chassis ID. If you + want to use the previous behavior, please set + SYSTEMD_LLDP_SEND_MACHINE_ID=1 environment variable to + systemd-networkd. + * Support for the !! command line prefix on ExecStart= lines (and related) has been removed, and if specified will be ignored. The concept was supposed to provide compatibility with kernels that diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index c2ea147738..87fb67502a 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -688,6 +688,9 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \ work, ProtectSystem=strict in systemd-networkd.service needs to be downgraded or disabled. +* `$SYSTEMD_LLDP_SEND_MACHINE_ID` - takes a boolean, If true, systemd-networkd + sends machine ID as chassis ID through LLDP protocol. + `systemd-storagetm`: * `$SYSTEMD_NVME_MODEL`, `$SYSTEMD_NVME_FIRMWARE`, `$SYSTEMD_NVME_SERIAL`, diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index 9e93a77cd1..3f73d881d4 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -5,6 +5,7 @@ #include "sd-lldp-tx.h" #include "alloc-util.h" +#include "env-util.h" #include "ether-addr-util.h" #include "fd-util.h" #include "hostname-setup.h" @@ -17,6 +18,8 @@ #include "unaligned.h" #include "web-util.h" +#define LLDP_APP_ID SD_ID128_MAKE(07,3a,43,bf,54,de,40,8d,8e,c4,96,ed,fd,94,72,dc) + /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ #define LLDP_FAST_TX_INIT 4U @@ -325,6 +328,22 @@ static int packet_append_string( return packet_append_prefixed_string(packet, packet_size, offset, type, 0, NULL, str); } +static int lldp_tx_get_machine_id(sd_id128_t *ret) { + int r; + + assert(ret); + + /* Unfortunately we previously exposed machine ID. If the environment variable is set, then + * use the machine ID as is. Otherwise, use application specific one. */ + r = secure_getenv_bool("SYSTEMD_LLDP_SEND_MACHINE_ID"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_LLDP_SEND_MACHINE_ID, ignoring: %m"); + if (r > 0) + return sd_id128_get_machine(ret); + + return sd_id128_get_machine_app_specific(LLDP_APP_ID, ret); +} + static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, uint8_t **ret_packet) { _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; _cleanup_free_ uint8_t *packet = NULL; @@ -343,7 +362,7 @@ static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, u if (r < 0) return r; - r = sd_id128_get_machine(&machine_id); + r = lldp_tx_get_machine_id(&machine_id); if (r < 0) return r; From 0597109ddf62ba26446cb81418fd6e2e0d90c5f0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:20:12 +0900 Subject: [PATCH 2/8] sd-lldp-tx: append capabilities before MUD URL No functional change, but let's append vendor specific informations after others to sort the TLVs with their type. --- src/libsystemd-network/sd-lldp-tx.c | 22 +++++++++++----------- src/systemd/sd-lldp-tx.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index 3f73d881d4..92b8ac5161 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -238,10 +238,10 @@ static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const c 2 + strlen_ptr(hostname) + /* System description */ 2 + strlen_ptr(pretty_hostname) + - /* MUD URL */ - 2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) + /* System Capabilities */ 2 + 4 + + /* MUD URL */ + 2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) + /* End */ 2; } @@ -428,6 +428,15 @@ static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, u if (r < 0) return r; + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + if (r < 0) + return r; + + unaligned_write_be16(packet + offset, lldp_tx->supported_capabilities); + offset += 2; + unaligned_write_be16(packet + offset, lldp_tx->enabled_capabilities); + offset += 2; + /* See section 12 of RFC 8520. * +--------+--------+----------+---------+-------------- * |TLV Type| len | OUI |subtype | MUDString @@ -448,15 +457,6 @@ static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, u if (r < 0) return r; - r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); - if (r < 0) - return r; - - unaligned_write_be16(packet + offset, lldp_tx->supported_capabilities); - offset += 2; - unaligned_write_be16(packet + offset, lldp_tx->enabled_capabilities); - offset += 2; - r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_END, 0); if (r < 0) return r; diff --git a/src/systemd/sd-lldp-tx.h b/src/systemd/sd-lldp-tx.h index 107292027d..520f5a2083 100644 --- a/src/systemd/sd-lldp-tx.h +++ b/src/systemd/sd-lldp-tx.h @@ -56,8 +56,8 @@ int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr); int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description); int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname); int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname); -int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url); int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled); +int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_tx, sd_lldp_tx_unref); From 898513783ee3b973514ba9dbf89f4a05fb450652 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:35:18 +0900 Subject: [PATCH 3/8] sd-lldp-tx: allow to emit VLAN ID --- src/libsystemd-network/sd-lldp-tx.c | 23 +++++++++++++++++++++++ src/systemd/sd-lldp-tx.h | 1 + 2 files changed, 24 insertions(+) diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index 92b8ac5161..4c6f263f8d 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -64,6 +64,7 @@ struct sd_lldp_tx { char *mud_url; uint16_t supported_capabilities; uint16_t enabled_capabilities; + uint16_t vlan_id; }; #define log_lldp_tx_errno(lldp_tx, error, fmt, ...) \ @@ -221,6 +222,13 @@ int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url) { return free_and_strdup(&lldp_tx->mud_url, empty_to_null(mud_url)); } +int sd_lldp_tx_set_vlan_id(sd_lldp_tx *lldp_tx, uint16_t vlan_id) { + assert_return(lldp_tx, -EINVAL); + + lldp_tx->vlan_id = vlan_id; + return 0; +} + static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const char *hostname, const char *pretty_hostname) { assert(lldp_tx); assert(lldp_tx->ifindex > 0); @@ -242,6 +250,8 @@ static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const c 2 + 4 + /* MUD URL */ 2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) + + /* VLAN ID */ + 2 + sizeof(SD_LLDP_OUI_802_1_VLAN_ID) + sizeof(uint16_t) + /* End */ 2; } @@ -457,6 +467,19 @@ static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, u if (r < 0) return r; + /* VLAN ID */ + if (lldp_tx->vlan_id > 0) { + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_PRIVATE, + sizeof(SD_LLDP_OUI_802_1_VLAN_ID) + sizeof(uint16_t)); + if (r < 0) + return r; + + memcpy_safe(packet + offset, SD_LLDP_OUI_802_1_VLAN_ID, sizeof(SD_LLDP_OUI_802_1_VLAN_ID)); + offset += sizeof(SD_LLDP_OUI_802_1_VLAN_ID); + unaligned_write_be16(packet + offset, lldp_tx->vlan_id); + offset += 2; + } + r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_END, 0); if (r < 0) return r; diff --git a/src/systemd/sd-lldp-tx.h b/src/systemd/sd-lldp-tx.h index 520f5a2083..4d92ca5ebe 100644 --- a/src/systemd/sd-lldp-tx.h +++ b/src/systemd/sd-lldp-tx.h @@ -58,6 +58,7 @@ int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname); int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname); int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled); int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url); +int sd_lldp_tx_set_vlan_id(sd_lldp_tx *lldp_tx, uint16_t vlan_id); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_tx, sd_lldp_tx_unref); From 2da3957b635c58eb662bf027bda2880640c0c120 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:38:38 +0900 Subject: [PATCH 4/8] network/lldp-tx: also emit VLAN ID --- src/network/networkd-lldp-tx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index cc22fbf2d6..0c108b875c 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -13,6 +13,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "vlan.h" static bool link_lldp_tx_enabled(Link *link) { assert(link); @@ -83,6 +84,12 @@ int link_lldp_tx_configure(Link *link) { if (r < 0) return r; + if (link->netdev && link->netdev->kind == NETDEV_KIND_VLAN) { + r = sd_lldp_tx_set_vlan_id(link->lldp_tx, VLAN(link->netdev)->id); + if (r < 0) + return r; + } + return 0; } From 4c45437b60c88525a42aaeb631946589280f1a77 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:52:16 +0900 Subject: [PATCH 5/8] sd-lldp-rx: also dump received MUD URL --- src/libsystemd-network/lldp-neighbor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index 0955c8cb0b..e35df5e145 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -815,5 +815,6 @@ int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) { 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)), + JSON_BUILD_PAIR_STRING_NON_EMPTY("MUDURL", n->mud_url), SD_JSON_BUILD_PAIR_CONDITION(valid_vlanid, "VlanID", SD_JSON_BUILD_UNSIGNED(vlanid))); } From b6f96f635fd7b650465af80566bd614fc87792a8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:04:37 +0900 Subject: [PATCH 6/8] sd-lldp-tx: introduce sd_lldp_tx_describe() to dump current settings --- src/libsystemd-network/sd-lldp-tx.c | 60 ++++++++++++++++++++++++++++- src/systemd/sd-lldp-tx.h | 3 ++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index 4c6f263f8d..9b86558eba 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -10,6 +10,7 @@ #include "fd-util.h" #include "hostname-setup.h" #include "hostname-util.h" +#include "json-util.h" #include "network-common.h" #include "random-util.h" #include "socket-util.h" @@ -368,7 +369,7 @@ static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, u assert(ret_packet); /* If ifname is not set yet, set ifname from ifindex. */ - r = sd_lldp_tx_get_ifname(lldp_tx, NULL); + r = sd_lldp_tx_get_ifname(lldp_tx, /* ret = */ NULL); if (r < 0) return r; @@ -666,3 +667,60 @@ int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) { return 0; } + +int sd_lldp_tx_describe(sd_lldp_tx *lldp_tx, sd_json_variant **ret) { + int r; + + assert(lldp_tx); + assert(ret); + + /* If ifname is not set yet, set ifname from ifindex. */ + const char *ifname; + r = sd_lldp_tx_get_ifname(lldp_tx, &ifname); + if (r < 0) + return r; + + size_t port_id_len = strlen(ifname) + 1; /* +1 for subtype */ + _cleanup_free_ uint8_t *port_id = new(uint8_t, port_id_len + 1); /* +1 for unused zero suffix for safety */ + if (!port_id) + return -ENOMEM; + + port_id[0] = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; + memcpy(port_id + 1, ifname, port_id_len); /* also copy the final NUL for safety */ + assert(port_id[port_id_len] == 0); + + sd_id128_t machine_id; + r = lldp_tx_get_machine_id(&machine_id); + if (r < 0) + return r; + + size_t chassis_id_len = (SD_ID128_STRING_MAX - 1) + 1; + _cleanup_free_ uint8_t *chassis_id = new(uint8_t, chassis_id_len + 1); + if (!chassis_id) + return -ENOMEM; + + chassis_id[0] = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; + memcpy(chassis_id + 1, SD_ID128_TO_STRING(machine_id), chassis_id_len); + assert(chassis_id[chassis_id_len] == 0); + + _cleanup_free_ char *hostname = NULL; + if (!lldp_tx->hostname) + (void) gethostname_strict(&hostname); + + _cleanup_free_ char *pretty_hostname = NULL; + if (!lldp_tx->pretty_hostname) + (void) get_pretty_hostname(&pretty_hostname); + + return sd_json_buildo( + ret, + SD_JSON_BUILD_PAIR_STRING("ChassisID", SD_ID128_TO_STRING(machine_id)), + SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawChassisID", chassis_id, chassis_id_len), + SD_JSON_BUILD_PAIR_STRING("PortID", lldp_tx->ifname), + SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawPortID", port_id, port_id_len), + JSON_BUILD_PAIR_STRING_NON_EMPTY("PortDescription", lldp_tx->port_description), + JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemName", lldp_tx->hostname ?: hostname), + JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemDescription", lldp_tx->pretty_hostname ?: pretty_hostname), + SD_JSON_BUILD_PAIR_UNSIGNED("EnabledCapabilities", lldp_tx->enabled_capabilities), + JSON_BUILD_PAIR_STRING_NON_EMPTY("MUDURL", lldp_tx->mud_url), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("VlanID", lldp_tx->vlan_id)); +} diff --git a/src/systemd/sd-lldp-tx.h b/src/systemd/sd-lldp-tx.h index 4d92ca5ebe..3dfb481b55 100644 --- a/src/systemd/sd-lldp-tx.h +++ b/src/systemd/sd-lldp-tx.h @@ -18,6 +18,7 @@ ***/ #include "_sd-common.h" +#include "sd-json.h" #include "sd-lldp.h" /* IWYU pragma: export*/ _SD_BEGIN_DECLARATIONS; @@ -60,6 +61,8 @@ int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_ int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url); int sd_lldp_tx_set_vlan_id(sd_lldp_tx *lldp_tx, uint16_t vlan_id); +int sd_lldp_tx_describe(sd_lldp_tx *lldp_tx, sd_json_variant **ret); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_tx, sd_lldp_tx_unref); _SD_END_DECLARATIONS; From f0a05209f9227d310614f56670e337b6e18ccc78 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 10:13:25 +0900 Subject: [PATCH 7/8] network: also include information about LLDP sender in reply for Describe() method Addresses https://github.com/systemd/systemd/pull/37845#issuecomment-2973257825. --- src/network/networkd-json.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index d07227d288..b6b4b02c06 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -1404,6 +1404,23 @@ static int dhcp_client_append_json(Link *link, sd_json_variant **v) { return json_variant_set_field_non_null(v, "DHCPv4Client", w); } +static int lldp_tx_append_json(Link *link, sd_json_variant **v) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL; + int r; + + assert(link); + assert(v); + + if (!link->lldp_tx) + return 0; + + r = sd_lldp_tx_describe(link->lldp_tx, &w); + if (r < 0) + return r; + + return json_variant_set_field_non_null(v, "LLDP", w); +} + int link_build_json(Link *link, sd_json_variant **ret) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; _cleanup_free_ char *type = NULL, *flags = NULL; @@ -1540,6 +1557,10 @@ int link_build_json(Link *link, sd_json_variant **ret) { if (r < 0) return r; + r = lldp_tx_append_json(link, &v); + if (r < 0) + return r; + *ret = TAKE_PTR(v); return 0; } From e08fdfdd7d18c451055b7804646499b3f1baedf7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 15 Jun 2025 11:36:01 +0900 Subject: [PATCH 8/8] test-network: compare LLDP sender and receiver information --- test/test-network/systemd-networkd-tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7f20aa4aa5..710d94b463 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -6660,6 +6660,13 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, r'Connected To: .* on port veth-peer') + # Compare the json output from sender and receiver + sender_json = get_link_description('veth-peer')['LLDP'] + receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0] + print(sender_json) + print(receiver_json) + self.assertEqual(sender_json, receiver_json) + class NetworkdRATests(unittest.TestCase, Utilities): def setUp(self):