teknoraver / rpms / systemd

Forked from rpms/systemd 5 months ago
Clone
df7689
From 020c48ffda361eb4962a11b888aca1ba75284ba6 Mon Sep 17 00:00:00 2001
df7689
From: Ryan Wilson <ryantimwilson@meta4.com>
df7689
Date: Tue, 3 Dec 2024 14:46:54 -0800
df7689
Subject: [PATCH 1/3] networkctl: Make networkctl lldp output backwards
df7689
 compatible with 255
df7689
df7689
---
df7689
 src/network/networkctl-lldp.c | 16 +++++++---------
df7689
 1 file changed, 7 insertions(+), 9 deletions(-)
df7689
df7689
diff --git a/src/network/networkctl-lldp.c b/src/network/networkctl-lldp.c
df7689
index 43ffbabb9fcbb..b1dc927af9767 100644
df7689
--- a/src/network/networkctl-lldp.c
df7689
+++ b/src/network/networkctl-lldp.c
df7689
@@ -238,12 +238,11 @@ int link_lldp_status(int argc, char *argv[], void *userdata) {
df7689
 
df7689
         table = table_new("index",
df7689
                           "link",
df7689
-                          "system-name",
df7689
-                          "system-description",
df7689
                           "chassis-id",
df7689
+                          "system-name",
df7689
+                          "caps",
df7689
                           "port-id",
df7689
-                          "port-description",
df7689
-                          "caps");
df7689
+                          "port-description");
df7689
         if (!table)
df7689
                 return log_oom();
df7689
 
df7689
@@ -256,7 +255,7 @@ int link_lldp_status(int argc, char *argv[], void *userdata) {
df7689
         table_hide_column_from_display(table, (size_t) 0);
df7689
 
df7689
         /* Make the capabilities not truncated */
df7689
-        assert_se(cell = table_get_cell(table, 0, 7));
df7689
+        assert_se(cell = table_get_cell(table, 0, 4));
df7689
         table_set_minimum_width(table, cell, 11);
df7689
 
df7689
         sd_json_variant *i;
df7689
@@ -285,12 +284,11 @@ int link_lldp_status(int argc, char *argv[], void *userdata) {
df7689
                         r = table_add_many(table,
df7689
                                            TABLE_INT,    info.ifindex,
df7689
                                            TABLE_STRING, info.ifname,
df7689
-                                           TABLE_STRING, neighbor_info.system_name,
df7689
-                                           TABLE_STRING, neighbor_info.system_description,
df7689
                                            TABLE_STRING, neighbor_info.chassis_id,
df7689
+                                           TABLE_STRING, neighbor_info.system_name,
df7689
+                                           TABLE_STRING, cap_str,
df7689
                                            TABLE_STRING, neighbor_info.port_id,
df7689
-                                           TABLE_STRING, neighbor_info.port_description,
df7689
-                                           TABLE_STRING, cap_str);
df7689
+                                           TABLE_STRING, neighbor_info.port_description);
df7689
                         if (r < 0)
df7689
                                 return table_log_add_error(r);
df7689
 
df7689
df7689
From b99fb7eb9882f261fa250d9d5c76a83cbf1e41c6 Mon Sep 17 00:00:00 2001
df7689
From: Ryan Wilson <ryantimwilson@meta4.com>
df7689
Date: Wed, 4 Dec 2024 16:15:30 -0800
df7689
Subject: [PATCH 2/3] networkctl: Make lldp/status backwards compatible with
df7689
 255 over dbus
df7689
df7689
---
df7689
 src/libsystemd-network/lldp-neighbor.c |  22 +++
df7689
 src/network/networkctl-lldp.c          | 198 ++++++++++++++++++++++++-
df7689
 src/network/networkctl-lldp.h          |   1 +
df7689
 src/network/networkctl-status-link.c   |   9 +-
df7689
 src/network/networkctl-util.c          |   2 +-
df7689
 src/systemd/sd-lldp-rx.h               |   1 +
df7689
 6 files changed, 226 insertions(+), 7 deletions(-)
df7689
df7689
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
df7689
index 457b1e5926b0c..4e51a55bd1f64 100644
df7689
--- a/src/libsystemd-network/lldp-neighbor.c
df7689
+++ b/src/libsystemd-network/lldp-neighbor.c
df7689
@@ -630,6 +630,28 @@ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret
df7689
         return 0;
df7689
 }
df7689
 
df7689
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
df7689
+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
df7689
+        int r;
df7689
+
df7689
+        assert_return(ret, -EINVAL);
df7689
+        assert_return(raw || raw_size <= 0, -EINVAL);
df7689
+
df7689
+        n = lldp_neighbor_new(raw_size);
df7689
+        if (!n)
df7689
+                return -ENOMEM;
df7689
+
df7689
+        memcpy_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
df7689
+
df7689
+        r = lldp_neighbor_parse(n);
df7689
+        if (r < 0)
df7689
+                return r;
df7689
+
df7689
+        *ret = TAKE_PTR(n);
df7689
+
df7689
+        return r;
df7689
+}
df7689
+
df7689
 int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
df7689
         assert_return(n, -EINVAL);
df7689
 
df7689
diff --git a/src/network/networkctl-lldp.c b/src/network/networkctl-lldp.c
df7689
index b1dc927af9767..6a3a88210c8e2 100644
df7689
--- a/src/network/networkctl-lldp.c
df7689
+++ b/src/network/networkctl-lldp.c
df7689
@@ -1,11 +1,15 @@
df7689
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
df7689
 
df7689
 #include "alloc-util.h"
df7689
+#include "fd-util.h"
df7689
 #include "json-util.h"
df7689
 #include "networkctl.h"
df7689
 #include "networkctl-dump-util.h"
df7689
+#include "networkctl-link-info.h"
df7689
 #include "networkctl-lldp.h"
df7689
 #include "networkctl-util.h"
df7689
+#include "sd-lldp-rx.h"
df7689
+#include "sparse-endian.h"
df7689
 #include "stdio-util.h"
df7689
 #include "strv.h"
df7689
 #include "terminal-util.h"
df7689
@@ -214,6 +218,194 @@ static int dump_lldp_neighbors_json(sd_json_variant *reply, char * const *patter
df7689
         return sd_json_variant_dump(v, arg_json_format_flags, NULL, NULL);
df7689
 }
df7689
 
df7689
+static int open_lldp_neighbors_legacy(int ifindex, FILE **ret) {
df7689
+        _cleanup_fclose_ FILE *f = NULL;
df7689
+        char p[STRLEN("/run/systemd/netif/lldp/") + DECIMAL_STR_MAX(int)];
df7689
+
df7689
+        assert(ifindex >= 0);
df7689
+        assert(ret);
df7689
+
df7689
+        xsprintf(p, "/run/systemd/netif/lldp/%i", ifindex);
df7689
+
df7689
+        f = fopen(p, "re");
df7689
+        if (!f)
df7689
+                return -errno;
df7689
+
df7689
+        *ret = TAKE_PTR(f);
df7689
+        return 0;
df7689
+}
df7689
+
df7689
+static int next_lldp_neighbor_legacy(FILE *f, sd_lldp_neighbor **ret) {
df7689
+        _cleanup_free_ void *raw = NULL;
df7689
+        size_t l;
df7689
+        le64_t u;
df7689
+        int r;
df7689
+
df7689
+        assert(f);
df7689
+        assert(ret);
df7689
+
df7689
+        l = fread(&u, 1, sizeof(u), f);
df7689
+        if (l == 0 && feof(f))
df7689
+                return 0;
df7689
+        if (l != sizeof(u))
df7689
+                return -EBADMSG;
df7689
+
df7689
+        /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
df7689
+        if (le64toh(u) >= 4096)
df7689
+                return -EBADMSG;
df7689
+
df7689
+        raw = new(uint8_t, le64toh(u));
df7689
+        if (!raw)
df7689
+                return -ENOMEM;
df7689
+
df7689
+        if (fread(raw, 1, le64toh(u), f) != le64toh(u))
df7689
+                return -EBADMSG;
df7689
+
df7689
+        r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
df7689
+        if (r < 0)
df7689
+                return r;
df7689
+
df7689
+        return 1;
df7689
+}
df7689
+
df7689
+int dump_lldp_neighbors_legacy(Table *table, const char *prefix, int ifindex) {
df7689
+        _cleanup_strv_free_ char **buf = NULL;
df7689
+        _cleanup_fclose_ FILE *f = NULL;
df7689
+        int r;
df7689
+
df7689
+        assert(table);
df7689
+        assert(prefix);
df7689
+        assert(ifindex > 0);
df7689
+
df7689
+        r = open_lldp_neighbors_legacy(ifindex, &f);
df7689
+        if (r == -ENOENT)
df7689
+                return 0;
df7689
+        if (r < 0)
df7689
+                return r;
df7689
+
df7689
+        for (;;) {
df7689
+                const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
df7689
+                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
df7689
+
df7689
+                r = next_lldp_neighbor_legacy(f, &n);
df7689
+                if (r < 0)
df7689
+                        return r;
df7689
+                if (r == 0)
df7689
+                        break;
df7689
+
df7689
+                (void) sd_lldp_neighbor_get_system_name(n, &system_name);
df7689
+                (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
df7689
+                (void) sd_lldp_neighbor_get_port_description(n, &port_description);
df7689
+
df7689
+                r = strv_extendf(&buf, "%s on port %s%s%s%s",
df7689
+                                 strna(system_name),
df7689
+                                 strna(port_id),
df7689
+                                 isempty(port_description) ? "" : " (",
df7689
+                                 strempty(port_description),
df7689
+                                 isempty(port_description) ? "" : ")");
df7689
+                if (r < 0)
df7689
+                        return log_oom();
df7689
+        }
df7689
+
df7689
+        return dump_list(table, prefix, buf);
df7689
+}
df7689
+static int link_lldp_status_legacy(int argc, char *argv[], void *userdata) {
df7689
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
df7689
+        _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
df7689
+        _cleanup_(table_unrefp) Table *table = NULL;
df7689
+        int r, c, m = 0;
df7689
+        uint16_t all = 0;
df7689
+        TableCell *cell;
df7689
+
df7689
+        r = sd_netlink_open(&rtnl);
df7689
+        if (r < 0)
df7689
+                return log_error_errno(r, "Failed to connect to netlink: %m");
df7689
+
df7689
+        c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
df7689
+        if (c < 0)
df7689
+                return c;
df7689
+
df7689
+        pager_open(arg_pager_flags);
df7689
+
df7689
+        table = table_new("link",
df7689
+                          "chassis-id",
df7689
+                          "system-name",
df7689
+                          "caps",
df7689
+                          "port-id",
df7689
+                          "port-description");
df7689
+        if (!table)
df7689
+                return log_oom();
df7689
+
df7689
+        if (arg_full)
df7689
+                table_set_width(table, 0);
df7689
+
df7689
+        table_set_header(table, arg_legend);
df7689
+
df7689
+        assert_se(cell = table_get_cell(table, 0, 3));
df7689
+        table_set_minimum_width(table, cell, 11);
df7689
+        table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
df7689
+
df7689
+        FOREACH_ARRAY(link, links, c) {
df7689
+                _cleanup_fclose_ FILE *f = NULL;
df7689
+
df7689
+                r = open_lldp_neighbors_legacy(link->ifindex, &f);
df7689
+                if (r == -ENOENT)
df7689
+                        continue;
df7689
+                if (r < 0) {
df7689
+                        log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", link->ifindex);
df7689
+                        continue;
df7689
+                }
df7689
+
df7689
+                for (;;) {
df7689
+                        const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
df7689
+                        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
df7689
+                        _cleanup_free_ char *capabilities = NULL;
df7689
+                        uint16_t cc;
df7689
+
df7689
+                        r = next_lldp_neighbor_legacy(f, &n);
df7689
+                        if (r < 0) {
df7689
+                                log_warning_errno(r, "Failed to read neighbor data: %m");
df7689
+                                break;
df7689
+                        }
df7689
+                        if (r == 0)
df7689
+                                break;
df7689
+
df7689
+                        (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
df7689
+                        (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
df7689
+                        (void) sd_lldp_neighbor_get_system_name(n, &system_name);
df7689
+                        (void) sd_lldp_neighbor_get_port_description(n, &port_description);
df7689
+
df7689
+                        if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
df7689
+                                capabilities = lldp_capabilities_to_string(cc);
df7689
+                                all |= cc;
df7689
+                        }
df7689
+
df7689
+                        r = table_add_many(table,
df7689
+                                           TABLE_STRING, link->name,
df7689
+                                           TABLE_STRING, chassis_id,
df7689
+                                           TABLE_STRING, system_name,
df7689
+                                           TABLE_STRING, capabilities,
df7689
+                                           TABLE_STRING, port_id,
df7689
+                                           TABLE_STRING, port_description);
df7689
+                        if (r < 0)
df7689
+                                return table_log_add_error(r);
df7689
+
df7689
+                        m++;
df7689
+                }
df7689
+        }
df7689
+
df7689
+        r = table_print(table, NULL);
df7689
+        if (r < 0)
df7689
+                return table_log_print_error(r);
df7689
+
df7689
+        if (arg_legend) {
df7689
+                lldp_capabilities_legend(all);
df7689
+                printf("\n%i neighbors listed.\n", m);
df7689
+        }
df7689
+
df7689
+        return 0;
df7689
+}
df7689
+
df7689
 int link_lldp_status(int argc, char *argv[], void *userdata) {
df7689
         _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
df7689
         _cleanup_(table_unrefp) Table *table = NULL;
df7689
@@ -224,8 +416,10 @@ int link_lldp_status(int argc, char *argv[], void *userdata) {
df7689
         int r;
df7689
 
df7689
         r = varlink_connect_networkd(&vl);
df7689
-        if (r < 0)
df7689
-                return r;
df7689
+        if (r < 0) {
df7689
+                log_warning("Varlink connection failed, fallback to D-Bus.");
df7689
+                return link_lldp_status_legacy(argc, argv, userdata);
df7689
+        }
df7689
 
df7689
         r = varlink_call_and_log(vl, "io.systemd.Network.GetLLDPNeighbors", NULL, &reply);
df7689
         if (r < 0)
df7689
diff --git a/src/network/networkctl-lldp.h b/src/network/networkctl-lldp.h
df7689
index 3ec6fe76c1184..98288479241b3 100644
df7689
--- a/src/network/networkctl-lldp.h
df7689
+++ b/src/network/networkctl-lldp.h
df7689
@@ -7,3 +7,4 @@
df7689
 
df7689
 int dump_lldp_neighbors(sd_varlink *vl, Table *table, int ifindex);
df7689
 int link_lldp_status(int argc, char *argv[], void *userdata);
df7689
+int dump_lldp_neighbors_legacy(Table *table, const char *prefix, int ifindex);
df7689
diff --git a/src/network/networkctl-status-link.c b/src/network/networkctl-status-link.c
df7689
index ae13eba9ae076..fbda880e9c9b2 100644
df7689
--- a/src/network/networkctl-status-link.c
df7689
+++ b/src/network/networkctl-status-link.c
df7689
@@ -267,7 +267,6 @@ static int link_status_one(
df7689
 
df7689
         assert(bus);
df7689
         assert(rtnl);
df7689
-        assert(vl);
df7689
         assert(info);
df7689
 
df7689
         (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
df7689
@@ -904,7 +903,7 @@ static int link_status_one(
df7689
                         return table_log_add_error(r);
df7689
         }
df7689
 
df7689
-        r = dump_lldp_neighbors(vl, table, info->ifindex);
df7689
+        r = vl ? dump_lldp_neighbors(vl, table, info->ifindex) : dump_lldp_neighbors_legacy(table, "Connected To", info->ifindex);
df7689
         if (r < 0)
df7689
                 return r;
df7689
 
df7689
@@ -955,8 +954,10 @@ int link_status(int argc, char *argv[], void *userdata) {
df7689
                 log_debug_errno(r, "Failed to open hardware database: %m");
df7689
 
df7689
         r = varlink_connect_networkd(&vl);
df7689
-        if (r < 0)
df7689
-                return r;
df7689
+        if (r < 0) {
df7689
+                log_warning("Varlink connection failed, fallback to D-Bus.");
df7689
+                vl = NULL;
df7689
+        }
df7689
 
df7689
         if (arg_all)
df7689
                 c = acquire_link_info(bus, rtnl, NULL, &links);
df7689
diff --git a/src/network/networkctl-util.c b/src/network/networkctl-util.c
df7689
index 88620aad536a0..8bda6b1aec98f 100644
df7689
--- a/src/network/networkctl-util.c
df7689
+++ b/src/network/networkctl-util.c
df7689
@@ -90,7 +90,7 @@ int acquire_bus(sd_bus **ret) {
df7689
         if (networkd_is_running()) {
df7689
                 r = varlink_connect_networkd(/* ret_varlink = */ NULL);
df7689
                 if (r < 0)
df7689
-                        return r;
df7689
+                        log_warning("Varlink connection failed, fallback to D-Bus.");
df7689
         } else
df7689
                 log_warning("systemd-networkd is not running, output might be incomplete.");
df7689
 
df7689
diff --git a/src/systemd/sd-lldp-rx.h b/src/systemd/sd-lldp-rx.h
df7689
index 51b9f39482001..b697643a0721c 100644
df7689
--- a/src/systemd/sd-lldp-rx.h
df7689
+++ b/src/systemd/sd-lldp-rx.h
df7689
@@ -88,6 +88,7 @@ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret)
df7689
 int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
df7689
 int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
df7689
 int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
df7689
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
df7689
 
df7689
 /* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
df7689
  * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
df7689
df7689
From ce39b00868dee5aa80c700185649f41026b88d76 Mon Sep 17 00:00:00 2001
df7689
From: Ryan Wilson <ryantimwilson@meta4.com>
df7689
Date: Wed, 4 Dec 2024 16:53:40 -0800
df7689
Subject: [PATCH 3/3] Revert "network/lldp: do not save LLDP neighbors under
df7689
 /run/systemd"
df7689
df7689
This reverts commit 5a0f6adbb2e39914897f404ac97fecebcc2c385a.
df7689
---
df7689
 src/libsystemd-network/lldp-neighbor.c | 11 ++++
df7689
 src/network/networkd-link.c            |  7 ++-
df7689
 src/network/networkd-link.h            |  1 +
df7689
 src/network/networkd-lldp-rx.c         | 69 ++++++++++++++++++++++++++
df7689
 src/network/networkd-lldp-rx.h         |  1 +
df7689
 src/network/networkd-state-file.c      |  2 +
df7689
 src/network/networkd.c                 |  3 +-
df7689
 src/systemd/sd-lldp-rx.h               |  1 +
df7689
 tmpfiles.d/systemd-network.conf        |  1 +
df7689
 9 files changed, 94 insertions(+), 2 deletions(-)
df7689
df7689
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
df7689
index 4e51a55bd1f64..39864d18f7919 100644
df7689
--- a/src/libsystemd-network/lldp-neighbor.c
df7689
+++ b/src/libsystemd-network/lldp-neighbor.c
df7689
@@ -377,6 +377,17 @@ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_a
df7689
         return 0;
df7689
 }
df7689
 
df7689
+int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
df7689
+        assert_return(n, -EINVAL);
df7689
+        assert_return(ret, -EINVAL);
df7689
+        assert_return(size, -EINVAL);
df7689
+
df7689
+        *ret = LLDP_NEIGHBOR_RAW(n);
df7689
+        *size = n->raw_size;
df7689
+
df7689
+        return 0;
df7689
+}
df7689
+
df7689
 int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
df7689
         assert_return(n, -EINVAL);
df7689
         assert_return(type, -EINVAL);
df7689
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
df7689
index 3f816d7fa25c4..d0b37bd65d074 100644
df7689
--- a/src/network/networkd-link.c
df7689
+++ b/src/network/networkd-link.c
df7689
@@ -276,6 +276,7 @@ static Link *link_free(Link *link) {
df7689
         free(link->driver);
df7689
 
df7689
         unlink_and_free(link->lease_file);
df7689
+        unlink_and_free(link->lldp_file);
df7689
         unlink_and_free(link->state_file);
df7689
 
df7689
         sd_device_unref(link->dev);
df7689
@@ -2663,7 +2664,7 @@ static Link *link_drop_or_unref(Link *link) {
df7689
 DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_drop_or_unref);
df7689
 
df7689
 static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
df7689
-        _cleanup_free_ char *ifname = NULL, *kind = NULL, *state_file = NULL, *lease_file = NULL;
df7689
+        _cleanup_free_ char *ifname = NULL, *kind = NULL, *state_file = NULL, *lease_file = NULL, *lldp_file = NULL;
df7689
         _cleanup_(link_drop_or_unrefp) Link *link = NULL;
df7689
         unsigned short iftype;
df7689
         int r, ifindex;
df7689
@@ -2704,6 +2705,9 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
df7689
 
df7689
                 if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", ifindex) < 0)
df7689
                         return log_oom_debug();
df7689
+
df7689
+                if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0)
df7689
+                        return log_oom_debug();
df7689
         }
df7689
 
df7689
         link = new(Link, 1);
df7689
@@ -2726,6 +2730,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
df7689
 
df7689
                 .state_file = TAKE_PTR(state_file),
df7689
                 .lease_file = TAKE_PTR(lease_file),
df7689
+                .lldp_file = TAKE_PTR(lldp_file),
df7689
 
df7689
                 .n_dns = UINT_MAX,
df7689
                 .dns_default_route = -1,
df7689
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
df7689
index 113217cdb6ad8..229ce976bc2b8 100644
df7689
--- a/src/network/networkd-link.h
df7689
+++ b/src/network/networkd-link.h
df7689
@@ -194,6 +194,7 @@ typedef struct Link {
df7689
 
df7689
         /* This is about LLDP reception */
df7689
         sd_lldp_rx *lldp_rx;
df7689
+        char *lldp_file;
df7689
 
df7689
         /* This is about LLDP transmission */
df7689
         sd_lldp_tx *lldp_tx;
df7689
diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c
df7689
index 6ba198282e7a1..853e0a0aceb67 100644
df7689
--- a/src/network/networkd-lldp-rx.c
df7689
+++ b/src/network/networkd-lldp-rx.c
df7689
@@ -52,6 +52,8 @@ static void lldp_rx_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_ll
df7689
         Link *link = ASSERT_PTR(userdata);
df7689
         int r;
df7689
 
df7689
+        (void) link_lldp_save(link);
df7689
+
df7689
         if (link->lldp_tx && event == SD_LLDP_RX_EVENT_ADDED) {
df7689
                 /* If we received information about a new neighbor, restart the LLDP "fast" logic */
df7689
 
df7689
@@ -102,3 +104,70 @@ int link_lldp_rx_configure(Link *link) {
df7689
 
df7689
         return 0;
df7689
 }
df7689
+
df7689
+int link_lldp_save(Link *link) {
df7689
+        _cleanup_(unlink_and_freep) char *temp_path = NULL;
df7689
+        _cleanup_fclose_ FILE *f = NULL;
df7689
+        sd_lldp_neighbor **l = NULL;
df7689
+        int n = 0, r, i;
df7689
+
df7689
+        assert(link);
df7689
+
df7689
+        if (isempty(link->lldp_file))
df7689
+                return 0; /* Do not update state file when running in test mode. */
df7689
+
df7689
+        if (!link->lldp_rx) {
df7689
+                (void) unlink(link->lldp_file);
df7689
+                return 0;
df7689
+        }
df7689
+
df7689
+        r = sd_lldp_rx_get_neighbors(link->lldp_rx, &l);
df7689
+        if (r < 0)
df7689
+                return r;
df7689
+        if (r == 0) {
df7689
+                (void) unlink(link->lldp_file);
df7689
+                return 0;
df7689
+        }
df7689
+
df7689
+        n = r;
df7689
+
df7689
+        r = fopen_temporary(link->lldp_file, &f, &temp_path);
df7689
+        if (r < 0)
df7689
+                goto finish;
df7689
+
df7689
+        (void) fchmod(fileno(f), 0644);
df7689
+
df7689
+        for (i = 0; i < n; i++) {
df7689
+                const void *p;
df7689
+                le64_t u;
df7689
+                size_t sz;
df7689
+
df7689
+                r = sd_lldp_neighbor_get_raw(l[i], &p, &sz);
df7689
+                if (r < 0)
df7689
+                        goto finish;
df7689
+
df7689
+                u = htole64(sz);
df7689
+                fwrite(&u, 1, sizeof(u), f);
df7689
+                fwrite(p, 1, sz, f);
df7689
+        }
df7689
+
df7689
+        r = fflush_and_check(f);
df7689
+        if (r < 0)
df7689
+                goto finish;
df7689
+
df7689
+        r = conservative_rename(temp_path, link->lldp_file);
df7689
+        if (r < 0)
df7689
+                goto finish;
df7689
+
df7689
+finish:
df7689
+        if (r < 0)
df7689
+                log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file);
df7689
+
df7689
+        if (l) {
df7689
+                for (i = 0; i < n; i++)
df7689
+                        sd_lldp_neighbor_unref(l[i]);
df7689
+                free(l);
df7689
+        }
df7689
+
df7689
+        return r;
df7689
+}
df7689
diff --git a/src/network/networkd-lldp-rx.h b/src/network/networkd-lldp-rx.h
df7689
index 75c9f8ca8602f..22f6602bd0ffd 100644
df7689
--- a/src/network/networkd-lldp-rx.h
df7689
+++ b/src/network/networkd-lldp-rx.h
df7689
@@ -14,6 +14,7 @@ typedef enum LLDPMode {
df7689
 } LLDPMode;
df7689
 
df7689
 int link_lldp_rx_configure(Link *link);
df7689
+int link_lldp_save(Link *link);
df7689
 
df7689
 const char* lldp_mode_to_string(LLDPMode m) _const_;
df7689
 LLDPMode lldp_mode_from_string(const char *s) _pure_;
df7689
diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c
df7689
index da917dd897241..2e32fbc300c10 100644
df7689
--- a/src/network/networkd-state-file.c
df7689
+++ b/src/network/networkd-state-file.c
df7689
@@ -714,6 +714,8 @@ static int link_save(Link *link) {
df7689
         if (link->state == LINK_STATE_LINGER)
df7689
                 return 0;
df7689
 
df7689
+        link_lldp_save(link);
df7689
+
df7689
         admin_state = ASSERT_PTR(link_state_to_string(link->state));
df7689
         oper_state = ASSERT_PTR(link_operstate_to_string(link->operstate));
df7689
         carrier_state = ASSERT_PTR(link_carrier_state_to_string(link->carrier_state));
df7689
diff --git a/src/network/networkd.c b/src/network/networkd.c
df7689
index 883f16d81b257..12edb685839c9 100644
df7689
--- a/src/network/networkd.c
df7689
+++ b/src/network/networkd.c
df7689
@@ -75,7 +75,8 @@ static int run(int argc, char *argv[]) {
df7689
          * to support old kernels not supporting AmbientCapabilities=. */
df7689
         FOREACH_STRING(p,
df7689
                        "/run/systemd/netif/links/",
df7689
-                       "/run/systemd/netif/leases/") {
df7689
+                       "/run/systemd/netif/leases/",
df7689
+                       "/run/systemd/netif/lldp/") {
df7689
                 r = mkdir_safe_label(p, 0755, UID_INVALID, GID_INVALID, MKDIR_WARN_MODE);
df7689
                 if (r < 0)
df7689
                         log_warning_errno(r, "Could not create directory '%s': %m", p);
df7689
diff --git a/src/systemd/sd-lldp-rx.h b/src/systemd/sd-lldp-rx.h
df7689
index b697643a0721c..fedf9956cf8da 100644
df7689
--- a/src/systemd/sd-lldp-rx.h
df7689
+++ b/src/systemd/sd-lldp-rx.h
df7689
@@ -75,6 +75,7 @@ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
df7689
 int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
df7689
 int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
df7689
 int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
df7689
+int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
df7689
 
df7689
 /* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
df7689
 int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
df7689
diff --git a/tmpfiles.d/systemd-network.conf b/tmpfiles.d/systemd-network.conf
df7689
index 75b61b7d07279..881937d456004 100644
df7689
--- a/tmpfiles.d/systemd-network.conf
df7689
+++ b/tmpfiles.d/systemd-network.conf
df7689
@@ -10,4 +10,5 @@
df7689
 d$ /run/systemd/netif        0755 systemd-network systemd-network -
df7689
 d$ /run/systemd/netif/links  0755 systemd-network systemd-network -
df7689
 d$ /run/systemd/netif/leases 0755 systemd-network systemd-network -
df7689
+d$ /run/systemd/netif/lldp   0755 systemd-network systemd-network -
df7689
 d$ /var/lib/systemd/network  0755 systemd-network systemd-network -