Buildroot update

* Updated buildroot
* Removed sd-lldp.patch for the old systemd, replaced with systemd's
  pull request #20333 [1]
* Added updated velia that works with the new systemd patch.
* Updated velia dependencies as we now require nlohmann/json
  (json-for-modern-cpp)
* Updated netconf-cli because we need new code that fixes build against
  boost 1.77 [2]

[1] https://github.com/systemd/systemd/pull/20333
[2] https://gerrit.cesnet.cz/c/CzechLight/netconf-cli/+/5071

Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/netconf-cli/+/5071
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/netconf-cli/+/5071
Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/velia/+/4995
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/velia/+/4995
Change-Id: Ifec45c9fbecb3012a769e4c10b37f90382ee4c7d
diff --git a/board/czechlight/common/patches/systemd/0001-network-Serialize-LLDP-neighbor-to-JSON-format.patch b/board/czechlight/common/patches/systemd/0001-network-Serialize-LLDP-neighbor-to-JSON-format.patch
new file mode 100644
index 0000000..8f85d59
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0001-network-Serialize-LLDP-neighbor-to-JSON-format.patch
@@ -0,0 +1,137 @@
+From 64d19ae11c2a3014fa58a0c05620b6ee95352125 Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Thu, 7 Oct 2021 11:16:57 +0200
+Subject: [PATCH 1/9] network: Serialize LLDP neighbor to JSON format
+
+Add functions serializing LLDP neighbors to JSON (JsonVariant).
+
+The entry contains a chassis id, system name and port id of the remote
+neighbor. Also it possibly contains an integer coding the enabled system
+capabilities and port description.
+---
+ src/libsystemd-network/lldp-neighbor.c | 22 +++++++++++++++
+ src/libsystemd-network/sd-lldp.c       | 37 ++++++++++++++++++++++++++
+ src/systemd/sd-lldp.h                  |  4 +++
+ 3 files changed, 63 insertions(+)
+
+diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
+index 3bd775158e..5c00205110 100644
+--- a/src/libsystemd-network/lldp-neighbor.c
++++ b/src/libsystemd-network/lldp-neighbor.c
+@@ -5,6 +5,7 @@
+ #include "ether-addr-util.h"
+ #include "hexdecoct.h"
+ #include "in-addr-util.h"
++#include "json.h"
+ #include "lldp-internal.h"
+ #include "lldp-neighbor.h"
+ #include "memory-util.h"
+@@ -777,3 +778,24 @@ _public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock
+         *ret = triple_timestamp_by_clock(&n->timestamp, clock);
+         return 0;
+ }
++
++_public_ int sd_lldp_neighbor_build_json(sd_lldp_neighbor *n, JsonVariant **ret) {
++        const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
++        uint16_t cc;
++        bool valid_cc;
++
++        (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
++        (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
++        (void) sd_lldp_neighbor_get_system_name(n, &system_name);
++        (void) sd_lldp_neighbor_get_port_description(n, &port_description);
++
++        valid_cc = sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0;
++
++        return json_build(ret, JSON_BUILD_OBJECT(
++                                JSON_BUILD_PAIR("neighbor", JSON_BUILD_OBJECT(
++                                                JSON_BUILD_PAIR("chassisId", JSON_BUILD_STRING(chassis_id)),
++                                                JSON_BUILD_PAIR("systemName", JSON_BUILD_STRING(system_name)),
++                                                JSON_BUILD_PAIR_CONDITION(valid_cc, "enabledCapabilities", JSON_BUILD_UNSIGNED(cc)),
++                                                JSON_BUILD_PAIR("portId", JSON_BUILD_STRING(port_id)),
++                                                JSON_BUILD_PAIR_CONDITION(port_description, "portDescription", JSON_BUILD_STRING(port_description))))));
++}
+diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
+index 49aa876a53..2d51992237 100644
+--- a/src/libsystemd-network/sd-lldp.c
++++ b/src/libsystemd-network/sd-lldp.c
+@@ -10,6 +10,7 @@
+ #include "ether-addr-util.h"
+ #include "event-util.h"
+ #include "fd-util.h"
++#include "json.h"
+ #include "lldp-internal.h"
+ #include "lldp-neighbor.h"
+ #include "lldp-network.h"
+@@ -488,6 +489,42 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
+         return k;
+ }
+ 
++_public_ int sd_lldp_get_neighbors_json(sd_lldp *lldp, JsonVariant ***ret) {
++        int i, j, r, n = 0;
++        sd_lldp_neighbor **l = NULL;
++        JsonVariant **v = NULL;
++
++        assert_return(lldp, -EINVAL);
++        assert_return(ret, -EINVAL);
++
++        r = sd_lldp_get_neighbors(lldp, &l);
++        if (r < 0)
++                return r;
++
++        n = r;
++
++        v = new0(JsonVariant*, n);
++        if (!v)
++                return -ENOMEM;
++
++        for (i = 0; i < n; i++) {
++                r = sd_lldp_neighbor_build_json(l[i], v + i);
++                if (r < 0)
++                        goto clear;
++        }
++
++        *ret = v;
++
++        return n;
++
++clear:
++        for (j = 0; j < i; j++)
++                json_variant_unrefp(v + j);
++
++        free(v);
++        return r;
++}
++
+ _public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
+         assert_return(lldp, -EINVAL);
+         assert_return(m > 0, -EINVAL);
+diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h
+index 64047ee817..7594502802 100644
+--- a/src/systemd/sd-lldp.h
++++ b/src/systemd/sd-lldp.h
+@@ -28,6 +28,8 @@
+ 
+ _SD_BEGIN_DECLARATIONS;
+ 
++typedef struct JsonVariant JsonVariant;
++
+ /* IEEE 802.1AB-2009 Clause 8: TLV Types */
+ enum {
+         SD_LLDP_TYPE_END                 = 0,
+@@ -156,10 +158,12 @@ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
+ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
+ 
+ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
++int sd_lldp_get_neighbors_json(sd_lldp *lldp, JsonVariant ***neighbors);
+ 
+ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
+ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
+ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
++int sd_lldp_neighbor_build_json(sd_lldp_neighbor *n, JsonVariant **ret);
+ 
+ /* Access to LLDP frame metadata */
+ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0002-network-Add-varlink-server.patch b/board/czechlight/common/patches/systemd/0002-network-Add-varlink-server.patch
new file mode 100644
index 0000000..2567d7f
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0002-network-Add-varlink-server.patch
@@ -0,0 +1,139 @@
+From 36f46b1f60453bdbb8841bd86724ff868149594b Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Tue, 24 Aug 2021 14:59:40 +0200
+Subject: [PATCH 2/9] network: Add varlink server
+
+Add a varlink server. In the next commits, we will implement a method
+that queries the LLDP neighbors.
+---
+ src/network/meson.build        |  2 ++
+ src/network/networkd-manager.c |  7 +++++++
+ src/network/networkd-manager.h |  3 +++
+ src/network/networkd-varlink.c | 34 ++++++++++++++++++++++++++++++++++
+ src/network/networkd-varlink.h |  7 +++++++
+ 5 files changed, 53 insertions(+)
+ create mode 100644 src/network/networkd-varlink.c
+ create mode 100644 src/network/networkd-varlink.h
+
+diff --git a/src/network/meson.build b/src/network/meson.build
+index 4e137d7b9e..2c97da60d3 100644
+--- a/src/network/meson.build
++++ b/src/network/meson.build
+@@ -129,6 +129,8 @@ sources = files('''
+         networkd-sysctl.h
+         networkd-util.c
+         networkd-util.h
++        networkd-varlink.c
++        networkd-varlink.h
+         networkd-wifi.c
+         networkd-wifi.h
+         tc/cake.c
+diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
+index 374d27bef3..5e53498711 100644
+--- a/src/network/networkd-manager.c
++++ b/src/network/networkd-manager.c
+@@ -38,6 +38,7 @@
+ #include "networkd-routing-policy-rule.h"
+ #include "networkd-speed-meter.h"
+ #include "networkd-state-file.h"
++#include "networkd-varlink.h"
+ #include "ordered-set.h"
+ #include "path-lookup.h"
+ #include "path-util.h"
+@@ -509,6 +510,8 @@ Manager* manager_free(Manager *m) {
+ 
+         m->fw_ctx = fw_ctx_free(m->fw_ctx);
+ 
++        manager_varlink_done(m);
++
+         return mfree(m);
+ }
+ 
+@@ -518,6 +521,10 @@ int manager_start(Manager *m) {
+ 
+         assert(m);
+ 
++        r = manager_varlink_init(m);
++        if (r < 0)
++                return r;
++
+         r = manager_start_speed_meter(m);
+         if (r < 0)
+                 return log_error_errno(r, "Failed to initialize speed meter: %m");
+diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
+index 4ee48f3468..f8d4d00db2 100644
+--- a/src/network/networkd-manager.h
++++ b/src/network/networkd-manager.h
+@@ -16,6 +16,7 @@
+ #include "ordered-set.h"
+ #include "set.h"
+ #include "time-util.h"
++#include "varlink.h"
+ 
+ struct Manager {
+         sd_netlink *rtnl;
+@@ -99,6 +100,8 @@ struct Manager {
+         FirewallContext *fw_ctx;
+ 
+         OrderedSet *request_queue;
++
++        VarlinkServer *varlink_server;
+ };
+ 
+ int manager_new(Manager **ret);
+diff --git a/src/network/networkd-varlink.c b/src/network/networkd-varlink.c
+new file mode 100644
+index 0000000000..57d8acb967
+--- /dev/null
++++ b/src/network/networkd-varlink.c
+@@ -0,0 +1,34 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++
++#include "networkd-varlink.h"
++
++int manager_varlink_init(Manager *m) {
++        _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
++        int r;
++
++        assert(m);
++
++        if (m->varlink_server)
++                return 0;
++
++        r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
++        if (r < 0)
++                return log_error_errno(r, "Failed to allocate varlink server object: %m");
++
++        r = varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", 0666);
++        if (r < 0)
++                return log_error_errno(r, "Failed to bind to varlink socket: %m");
++
++        r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
++        if (r < 0)
++                return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
++
++        m->varlink_server = TAKE_PTR(s);
++        return 0;
++}
++
++void manager_varlink_done(Manager *m) {
++        assert(m);
++
++        m->varlink_server = varlink_server_unref(m->varlink_server);
++}
+diff --git a/src/network/networkd-varlink.h b/src/network/networkd-varlink.h
+new file mode 100644
+index 0000000000..657e054a51
+--- /dev/null
++++ b/src/network/networkd-varlink.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++#pragma once
++
++#include "networkd-manager.h"
++
++int manager_varlink_init(Manager *m);
++void manager_varlink_done(Manager *m);
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0003-network-Add-LLDP-neighbors-method-to-varlink-server.patch b/board/czechlight/common/patches/systemd/0003-network-Add-LLDP-neighbors-method-to-varlink-server.patch
new file mode 100644
index 0000000..630b389
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0003-network-Add-LLDP-neighbors-method-to-varlink-server.patch
@@ -0,0 +1,118 @@
+From 3ee0693ac819543610120542f8e60fb95fe6f0d4 Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Wed, 1 Sep 2021 08:30:20 +0200
+Subject: [PATCH 3/9] network: Add LLDP neighbors method to varlink server
+
+Add a method to varlink server that will allow streaming the list of
+LLDP neighbors on a particular link. The method's argument is the ifindex
+of a link. The method then streams the JSONs representing individual
+neighbors as discovered by the LLDP protocol.
+---
+ src/network/networkd-varlink.c | 77 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 77 insertions(+)
+
+diff --git a/src/network/networkd-varlink.c b/src/network/networkd-varlink.c
+index 57d8acb967..f86be4e903 100644
+--- a/src/network/networkd-varlink.c
++++ b/src/network/networkd-varlink.c
+@@ -1,6 +1,76 @@
+ /* SPDX-License-Identifier: LGPL-2.1-or-later */
+ 
++#include "alloc-util.h"
++#include "fd-util.h"
++#include "json.h"
+ #include "networkd-varlink.h"
++#include "sd-lldp.h"
++#include "varlink.h"
++
++typedef struct LookupParameters {
++        int ifindex;
++} LookupParameters;
++
++static int vl_method_network_lldp_neighbors(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
++        static const JsonDispatch dispatch_table[] = {
++                { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(LookupParameters, ifindex), JSON_MANDATORY },
++                {}
++        };
++
++        int r, i, n;
++        Manager *m;
++        Link *l;
++        LookupParameters p;
++        JsonVariant **neighbors;
++
++        assert(link);
++
++        m = varlink_server_get_userdata(varlink_get_server(link));
++        assert(m);
++
++        if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY))
++                return -EINVAL;
++
++        if (!FLAGS_SET(flags, VARLINK_METHOD_MORE))
++                return -EINVAL;
++
++        r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
++        if (r < 0)
++                return r;
++
++        if (p.ifindex <= 0)
++                return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex"));
++
++        r = link_get_by_index(m, p.ifindex, &l);
++        if (r < 0)
++                return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex"));
++
++        r = sd_lldp_get_neighbors_json(l->lldp, &neighbors);
++        if (r < 0)
++                return r;
++        if (r == 0)
++                return varlink_error(link, "io.systemd.Network.NoNeighborFound", NULL);
++
++        n = r;
++        for (i = 0; i < n - 1; i++) {
++                r = varlink_notify(link, neighbors[i]);
++                if (r < 0)
++                        goto clear;
++        }
++
++        r = varlink_reply(link, neighbors[i]);
++        if (r < 0)
++                goto clear;
++
++        r = 0;
++
++clear:
++        for (i = 0; i < n; i++)
++                json_variant_unref(neighbors[i]);
++        free(neighbors);
++
++        return r;
++}
+ 
+ int manager_varlink_init(Manager *m) {
+         _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+@@ -15,6 +85,10 @@ int manager_varlink_init(Manager *m) {
+         if (r < 0)
+                 return log_error_errno(r, "Failed to allocate varlink server object: %m");
+ 
++        r = varlink_server_bind_method(s, "io.systemd.Network.LLDPNeighbors", vl_method_network_lldp_neighbors);
++        if (r < 0)
++                return log_error_errno(r, "Failed to register varlink method: %m");
++
+         r = varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", 0666);
+         if (r < 0)
+                 return log_error_errno(r, "Failed to bind to varlink socket: %m");
+@@ -24,6 +98,9 @@ int manager_varlink_init(Manager *m) {
+                 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+ 
+         m->varlink_server = TAKE_PTR(s);
++
++        varlink_server_set_userdata(m->varlink_server, m);
++
+         return 0;
+ }
+ 
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0004-varlink-Implement-varlink_observe_complete.patch b/board/czechlight/common/patches/systemd/0004-varlink-Implement-varlink_observe_complete.patch
new file mode 100644
index 0000000..579dd9f
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0004-varlink-Implement-varlink_observe_complete.patch
@@ -0,0 +1,74 @@
+From 657ce718099d6d82d2a6757909496d285221b22f Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Thu, 9 Sep 2021 09:19:05 +0200
+Subject: [PATCH 4/9] varlink: Implement varlink_observe_complete
+
+In some cases one doesn't want to run the sd_event loop just to obtain
+results from varlink replies in the "continues" mode.
+
+The new function (`varlink_observe_complete`) runs the varlink process
+mode on the client side synchronously and returns when no more replies
+are expected.
+This is similar to the behavior seen in the `varlink_call` function.
+---
+ src/shared/varlink.c | 31 +++++++++++++++++++++++++++++++
+ src/shared/varlink.h |  1 +
+ 2 files changed, 32 insertions(+)
+
+diff --git a/src/shared/varlink.c b/src/shared/varlink.c
+index 8da568e208..23a72516fb 100644
+--- a/src/shared/varlink.c
++++ b/src/shared/varlink.c
+@@ -1440,6 +1440,37 @@ int varlink_observeb(Varlink *v, const char *method, ...) {
+         return varlink_observe(v, method, parameters);
+ }
+ 
++int varlink_observe_complete(Varlink *v) {
++        int r;
++
++        while (v->state == VARLINK_AWAITING_REPLY_MORE) {
++                r = varlink_process(v);
++                if (r < 0)
++                        return r;
++                if (r > 0)
++                        continue;
++
++                r = varlink_wait(v, USEC_INFINITY);
++                if (r < 0)
++                        return r;
++        }
++
++        switch (v->state) {
++        case VARLINK_IDLE_CLIENT:
++                return 1;
++
++        case VARLINK_PENDING_DISCONNECT:
++        case VARLINK_DISCONNECTED:
++                return varlink_log_errno(v, SYNTHETIC_ERRNO(ECONNRESET), "Connection was closed.");
++
++        case VARLINK_PENDING_TIMEOUT:
++                return varlink_log_errno(v, SYNTHETIC_ERRNO(ETIME), "Connection timed out.");
++
++        default:
++                assert_not_reached("Unexpected state");
++        }
++}
++
+ int varlink_call(
+                 Varlink *v,
+                 const char *method,
+diff --git a/src/shared/varlink.h b/src/shared/varlink.h
+index 66a1ff630e..9bc836e1f9 100644
+--- a/src/shared/varlink.h
++++ b/src/shared/varlink.h
+@@ -92,6 +92,7 @@ int varlink_invokeb(Varlink *v, const char *method, ...);
+ /* Enqueue method call, expect a reply now, and possibly more later, which are all delivered to the reply callback */
+ int varlink_observe(Varlink *v, const char *method, JsonVariant *parameters);
+ int varlink_observeb(Varlink *v, const char *method, ...);
++int varlink_observe_complete(Varlink *v);
+ 
+ /* Enqueue a final reply */
+ int varlink_reply(Varlink *v, JsonVariant *parameters);
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0005-networkctl-lldp-uses-table_empty_string-instead-of-s.patch b/board/czechlight/common/patches/systemd/0005-networkctl-lldp-uses-table_empty_string-instead-of-s.patch
new file mode 100644
index 0000000..b1c97bc
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0005-networkctl-lldp-uses-table_empty_string-instead-of-s.patch
@@ -0,0 +1,43 @@
+From 876c47e0743031e352a126000c87b0b51209408d Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Thu, 7 Oct 2021 14:11:45 +0200
+Subject: [PATCH 5/9] networkctl: lldp uses table_empty_string instead of strna
+
+---
+ src/network/networkctl.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/src/network/networkctl.c b/src/network/networkctl.c
+index 9bbfe177b1..235731079d 100644
+--- a/src/network/networkctl.c
++++ b/src/network/networkctl.c
+@@ -2531,6 +2531,9 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+         assert_se(cell = table_get_cell(table, 0, 5));
+         table_set_minimum_width(table, cell, 16);
+ 
++        if (table_set_empty_string(table, "n/a") < 0)
++                return log_oom();
++
+         for (int i = 0; i < c; i++) {
+                 _cleanup_fclose_ FILE *f = NULL;
+ 
+@@ -2592,11 +2595,11 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+ 
+                         r = table_add_many(table,
+                                            TABLE_STRING, links[i].name,
+-                                           TABLE_STRING, strna(chassis_id),
+-                                           TABLE_STRING, strna(system_name),
+-                                           TABLE_STRING, strna(capabilities),
+-                                           TABLE_STRING, strna(port_id),
+-                                           TABLE_STRING, strna(port_description));
++                                           TABLE_STRING, chassis_id,
++                                           TABLE_STRING, system_name,
++                                           TABLE_STRING, capabilities,
++                                           TABLE_STRING, port_id,
++                                           TABLE_STRING, port_description);
+                         if (r < 0)
+                                 return table_log_add_error(r);
+ 
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0006-networkctl-lldp-table-now-ellpsizes-via-table-functi.patch b/board/czechlight/common/patches/systemd/0006-networkctl-lldp-table-now-ellpsizes-via-table-functi.patch
new file mode 100644
index 0000000..add9432
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0006-networkctl-lldp-table-now-ellpsizes-via-table-functi.patch
@@ -0,0 +1,90 @@
+From 03305bb0f181446ae4f1698c14e72470ae642bbb Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Thu, 7 Oct 2021 14:16:53 +0200
+Subject: [PATCH 6/9] networkctl: lldp table now ellpsizes via table functions
+
+Tables can now ellipize on their own so there is no need to manually
+call ellpsize on the table data.
+
+To achieve compatibility with previous versions the table columns are
+set to have the exact same size as before, therefore the minimum and
+maximum widths for the columns in the output.
+---
+ src/network/networkctl.c | 32 +++++++-------------------------
+ 1 file changed, 7 insertions(+), 25 deletions(-)
+
+diff --git a/src/network/networkctl.c b/src/network/networkctl.c
+index 235731079d..a1d42cbe92 100644
+--- a/src/network/networkctl.c
++++ b/src/network/networkctl.c
+@@ -2515,21 +2515,27 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+ 
+         assert_se(cell = table_get_cell(table, 0, 0));
+         table_set_minimum_width(table, cell, 16);
++        table_set_maximum_width(table, cell, 16);
+ 
+         assert_se(cell = table_get_cell(table, 0, 1));
+         table_set_minimum_width(table, cell, 17);
++        table_set_maximum_width(table, cell, 17);
+ 
+         assert_se(cell = table_get_cell(table, 0, 2));
+         table_set_minimum_width(table, cell, 16);
++        table_set_maximum_width(table, cell, 16);
+ 
+         assert_se(cell = table_get_cell(table, 0, 3));
+         table_set_minimum_width(table, cell, 11);
++        table_set_maximum_width(table, cell, 11);
+ 
+         assert_se(cell = table_get_cell(table, 0, 4));
+         table_set_minimum_width(table, cell, 17);
++        table_set_maximum_width(table, cell, 17);
+ 
+         assert_se(cell = table_get_cell(table, 0, 5));
+         table_set_minimum_width(table, cell, 16);
++        table_set_maximum_width(table, cell, 16);
+ 
+         if (table_set_empty_string(table, "n/a") < 0)
+                 return log_oom();
+@@ -2546,7 +2552,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+                 }
+ 
+                 for (;;) {
+-                        _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL, *capabilities = NULL;
++                        _cleanup_free_ char *capabilities = NULL;
+                         const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
+                         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+                         uint16_t cc;
+@@ -2564,30 +2570,6 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+                         (void) sd_lldp_neighbor_get_system_name(n, &system_name);
+                         (void) sd_lldp_neighbor_get_port_description(n, &port_description);
+ 
+-                        if (chassis_id) {
+-                                cid = ellipsize(chassis_id, 17, 100);
+-                                if (cid)
+-                                        chassis_id = cid;
+-                        }
+-
+-                        if (port_id) {
+-                                pid = ellipsize(port_id, 17, 100);
+-                                if (pid)
+-                                        port_id = pid;
+-                        }
+-
+-                        if (system_name) {
+-                                sname = ellipsize(system_name, 16, 100);
+-                                if (sname)
+-                                        system_name = sname;
+-                        }
+-
+-                        if (port_description) {
+-                                pdesc = ellipsize(port_description, 16, 100);
+-                                if (pdesc)
+-                                        port_description = pdesc;
+-                        }
+-
+                         if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
+                                 capabilities = lldp_capabilities_to_string(cc);
+                                 all |= cc;
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0007-networkctl-lldp-now-uses-varlink-call.patch b/board/czechlight/common/patches/systemd/0007-networkctl-lldp-now-uses-varlink-call.patch
new file mode 100644
index 0000000..fc8b007
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0007-networkctl-lldp-now-uses-varlink-call.patch
@@ -0,0 +1,223 @@
+From e5b7f28a216ee1986c417f83132e3daf39ef5fd2 Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Wed, 15 Sep 2021 14:42:34 +0200
+Subject: [PATCH 7/9] networkctl: lldp now uses varlink call
+
+`networkctl lldp` now uses varlink call to the networkd to query LLDP
+neighbor data.
+---
+ src/network/networkctl.c | 157 ++++++++++++++++++++++++++++-----------
+ 1 file changed, 113 insertions(+), 44 deletions(-)
+
+diff --git a/src/network/networkctl.c b/src/network/networkctl.c
+index a1d42cbe92..f98779da7e 100644
+--- a/src/network/networkctl.c
++++ b/src/network/networkctl.c
+@@ -36,6 +36,7 @@
+ #include "glob-util.h"
+ #include "hwdb-util.h"
+ #include "ipvlan-util.h"
++#include "json.h"
+ #include "local-addresses.h"
+ #include "locale-util.h"
+ #include "logs-show.h"
+@@ -61,6 +62,7 @@
+ #include "strxcpyx.h"
+ #include "terminal-util.h"
+ #include "unit-def.h"
++#include "varlink.h"
+ #include "verbs.h"
+ #include "wifi-util.h"
+ 
+@@ -2481,14 +2483,104 @@ static void lldp_capabilities_legend(uint16_t x) {
+         puts("");
+ }
+ 
++typedef struct LLDPNeighborEntry {
++        uint32_t capabilities;
++        char *chassis_id;
++        char *port_id;
++        char *system_name;
++        char *port_description;
++} LLDPNeighborEntry;
++
++static void lldp_neighbor_entry_free(LLDPNeighborEntry *e) {
++        if (!e)
++                return;
++
++        free(e->chassis_id);
++        free(e->port_id);
++        free(e->system_name);
++        free(e->port_description);
++}
++
++typedef struct LLDPUserdata {
++        int *neighbors_count;
++        uint16_t *capabilities_all;
++
++        char *link_name;
++        Table *table;
++} LLDPUserdata;
++
++static int lldp_neighbours_varlink_reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) {
++        int r;
++        _cleanup_free_ char *capabilities = NULL;
++        LLDPUserdata *udata;
++        _cleanup_(lldp_neighbor_entry_free) LLDPNeighborEntry entry = {};
++
++        static const JsonDispatch dispatch_table[] = {
++                { "chassisId",           JSON_VARIANT_STRING,   json_dispatch_string, offsetof(LLDPNeighborEntry, chassis_id),       0 },
++                { "portId",              JSON_VARIANT_STRING,   json_dispatch_string, offsetof(LLDPNeighborEntry, port_id),          0 },
++                { "systemName",          JSON_VARIANT_STRING,   json_dispatch_string, offsetof(LLDPNeighborEntry, system_name),      0 },
++                { "enabledCapabilities", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(LLDPNeighborEntry, capabilities),     0 },
++                { "portDescription",     JSON_VARIANT_STRING,   json_dispatch_string, offsetof(LLDPNeighborEntry, port_description), 0 },
++                {}
++        };
++
++        udata = userdata;
++
++        assert(udata);
++        assert(udata->link_name);
++
++        r = json_dispatch(json_variant_by_key(parameters, "neighbor"), dispatch_table, NULL, 0, &entry);
++        if (r < 0)
++                return r;
++
++        if (udata->table) {
++                capabilities = lldp_capabilities_to_string(entry.capabilities);
++
++                r = table_add_many(udata->table,
++                                TABLE_STRING, udata->link_name,
++                                TABLE_STRING, entry.chassis_id,
++                                TABLE_STRING, entry.system_name,
++                                TABLE_STRING, capabilities,
++                                TABLE_STRING, entry.port_id,
++                                TABLE_STRING, entry.port_description);
++                if (r < 0)
++                        return table_log_add_error(r);
++        }
++
++        if (udata->neighbors_count)
++                *(udata->neighbors_count) += 1;
++
++        if (udata->capabilities_all)
++                *(udata->capabilities_all) |= entry.capabilities;
++
++        return 0;
++}
++
+ static int link_lldp_status(int argc, char *argv[], void *userdata) {
++        static const char *address = "/run/systemd/netif/io.systemd.Network";
++        static const char *method = "io.systemd.Network.LLDPNeighbors";
++
++        int r, c;
++        _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
+         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+         _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
+         _cleanup_(table_unrefp) Table *table = NULL;
+-        int r, c, m = 0;
+-        uint16_t all = 0;
++        int neighbors_count = 0;
++        uint16_t capabilities_all = 0;
++        LLDPUserdata udata = {};
+         TableCell *cell;
+ 
++        r = varlink_connect_address(&link, address);
++        if (r < 0)
++                return log_error_errno(r, "Failed to connect to %s: %m", address);
++
++        (void) varlink_set_description(link, "network");
++        (void) varlink_set_relative_timeout(link, USEC_INFINITY);
++
++        r = varlink_bind_reply(link, lldp_neighbours_varlink_reply);
++        if (r < 0)
++                return log_error_errno(r, "Failed to bind reply callback: %m");
++
+         r = sd_netlink_open(&rtnl);
+         if (r < 0)
+                 return log_error_errno(r, "Failed to connect to netlink: %m");
+@@ -2540,53 +2632,30 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+         if (table_set_empty_string(table, "n/a") < 0)
+                 return log_oom();
+ 
+-        for (int i = 0; i < c; i++) {
+-                _cleanup_fclose_ FILE *f = NULL;
++        udata.table = table;
+ 
+-                r = open_lldp_neighbors(links[i].ifindex, &f);
+-                if (r == -ENOENT)
+-                        continue;
+-                if (r < 0) {
+-                        log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex);
+-                        continue;
+-                }
++        udata.neighbors_count = &neighbors_count;
++        udata.capabilities_all = &capabilities_all;
+ 
+-                for (;;) {
+-                        _cleanup_free_ char *capabilities = NULL;
+-                        const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
+-                        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+-                        uint16_t cc;
++        varlink_set_userdata(link, &udata);
+ 
+-                        r = next_lldp_neighbor(f, &n);
+-                        if (r < 0) {
+-                                log_warning_errno(r, "Failed to read neighbor data: %m");
+-                                break;
+-                        }
+-                        if (r == 0)
+-                                break;
++        for (int i = 0; i < c; i++) {
++                _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+ 
+-                        (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
+-                        (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
+-                        (void) sd_lldp_neighbor_get_system_name(n, &system_name);
+-                        (void) sd_lldp_neighbor_get_port_description(n, &port_description);
++                udata.link_name = links[i].name;
+ 
+-                        if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
+-                                capabilities = lldp_capabilities_to_string(cc);
+-                                all |= cc;
+-                        }
++                r = json_build(&cparams, JSON_BUILD_OBJECT(
++                                        JSON_BUILD_PAIR("ifindex", JSON_BUILD_UNSIGNED(links[i].ifindex))));
++                if (r < 0)
++                        return r;
+ 
+-                        r = table_add_many(table,
+-                                           TABLE_STRING, links[i].name,
+-                                           TABLE_STRING, chassis_id,
+-                                           TABLE_STRING, system_name,
+-                                           TABLE_STRING, capabilities,
+-                                           TABLE_STRING, port_id,
+-                                           TABLE_STRING, port_description);
+-                        if (r < 0)
+-                                return table_log_add_error(r);
++                r = varlink_observe(link, method, cparams);
++                if (r < 0)
++                        return log_error_errno(r, "Failed to execute varlink call: %m");
+ 
+-                        m++;
+-                }
++                r = varlink_observe_complete(link);
++                if (r < 0)
++                        return r;
+         }
+ 
+         r = table_print(table, NULL);
+@@ -2594,8 +2663,8 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+                 return table_log_print_error(r);
+ 
+         if (arg_legend) {
+-                lldp_capabilities_legend(all);
+-                printf("\n%i neighbors listed.\n", m);
++                lldp_capabilities_legend(capabilities_all);
++                printf("\n%i neighbors listed.\n", neighbors_count);
+         }
+ 
+         return 0;
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0008-networkctl-allow-format-LLDP-capabilities-string-wit.patch b/board/czechlight/common/patches/systemd/0008-networkctl-allow-format-LLDP-capabilities-string-wit.patch
new file mode 100644
index 0000000..72f855d
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0008-networkctl-allow-format-LLDP-capabilities-string-wit.patch
@@ -0,0 +1,58 @@
+From 658e0a69a353e2559ab04c3caa7ea1eaf0ef6dda Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Wed, 15 Sep 2021 14:30:19 +0200
+Subject: [PATCH 8/9] networkctl: allow format LLDP capabilities string without
+ dots
+
+Allows formatting the LLDP capabilities string without the dots
+representing "not enabled" (just omit them).
+---
+ src/network/networkctl.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/src/network/networkctl.c b/src/network/networkctl.c
+index f98779da7e..89abaf26da 100644
+--- a/src/network/networkctl.c
++++ b/src/network/networkctl.c
+@@ -2433,21 +2433,24 @@ static int link_status(int argc, char *argv[], void *userdata) {
+         return 0;
+ }
+ 
+-static char *lldp_capabilities_to_string(uint16_t x) {
++static char *lldp_capabilities_to_string(uint16_t x, bool dots) {
+         static const char characters[] = {
+                 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm',
+         };
+         char *ret;
+-        unsigned i;
++        unsigned i, j;
+ 
+         ret = new(char, ELEMENTSOF(characters) + 1);
+         if (!ret)
+                 return NULL;
+ 
+-        for (i = 0; i < ELEMENTSOF(characters); i++)
+-                ret[i] = (x & (1U << i)) ? characters[i] : '.';
++        for (i = 0, j = 0; i < ELEMENTSOF(characters); i++)
++                if (x & (1U << i))
++                        ret[j++] = characters[i];
++                else if (dots)
++                        ret[j++] = '.';
+ 
+-        ret[i] = 0;
++        ret[j] = 0;
+         return ret;
+ }
+ 
+@@ -2534,7 +2537,7 @@ static int lldp_neighbours_varlink_reply(Varlink *link, JsonVariant *parameters,
+                 return r;
+ 
+         if (udata->table) {
+-                capabilities = lldp_capabilities_to_string(entry.capabilities);
++                capabilities = lldp_capabilities_to_string(entry.capabilities, true);
+ 
+                 r = table_add_many(udata->table,
+                                 TABLE_STRING, udata->link_name,
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/0009-networkctl-JSON-output-in-networkctl-lldp.patch b/board/czechlight/common/patches/systemd/0009-networkctl-JSON-output-in-networkctl-lldp.patch
new file mode 100644
index 0000000..5d43f1d
--- /dev/null
+++ b/board/czechlight/common/patches/systemd/0009-networkctl-JSON-output-in-networkctl-lldp.patch
@@ -0,0 +1,203 @@
+From 7479e9e720909a2d2360f684e85821195301dce2 Mon Sep 17 00:00:00 2001
+From: Tomas Pecka <peckato1@users.noreply.github.com>
+Date: Wed, 6 Oct 2021 10:11:31 +0200
+Subject: [PATCH 9/9] networkctl: JSON output in networkctl lldp
+
+`networkctl lldp` now outputs also in JSON format when `--json=*`
+argument passed. The LLDP neighbors are listed in per interface lists.
+---
+ src/network/networkctl.c | 123 ++++++++++++++++++++++++++-------------
+ 1 file changed, 82 insertions(+), 41 deletions(-)
+
+diff --git a/src/network/networkctl.c b/src/network/networkctl.c
+index 89abaf26da..124ee8f0c7 100644
+--- a/src/network/networkctl.c
++++ b/src/network/networkctl.c
+@@ -2510,6 +2510,7 @@ typedef struct LLDPUserdata {
+ 
+         char *link_name;
+         Table *table;
++        JsonVariant **json_list;
+ } LLDPUserdata;
+ 
+ static int lldp_neighbours_varlink_reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) {
+@@ -2556,6 +2557,26 @@ static int lldp_neighbours_varlink_reply(Varlink *link, JsonVariant *parameters,
+         if (udata->capabilities_all)
+                 *(udata->capabilities_all) |= entry.capabilities;
+ 
++        if (udata->json_list) {
++                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
++
++                capabilities = lldp_capabilities_to_string(entry.capabilities, false);
++
++                r = json_build(&v, JSON_BUILD_OBJECT(
++                                        JSON_BUILD_PAIR("neighbor", JSON_BUILD_OBJECT(
++                                        JSON_BUILD_PAIR_CONDITION(entry.chassis_id, "chassisId", JSON_BUILD_STRING(entry.chassis_id)),
++                                        JSON_BUILD_PAIR_CONDITION(entry.port_id, "portId", JSON_BUILD_STRING(entry.port_id)),
++                                        JSON_BUILD_PAIR_CONDITION(entry.system_name, "systemName", JSON_BUILD_STRING(entry.system_name)),
++                                        JSON_BUILD_PAIR_CONDITION(entry.port_description, "portDescription", JSON_BUILD_STRING(entry.port_description)),
++                                        JSON_BUILD_PAIR_CONDITION(entry.capabilities, "enabledCapabilities", JSON_BUILD_STRING(capabilities))))));
++                if (r < 0)
++                        return r;
++
++                r = json_variant_append_array(udata->json_list, v);
++                if (r < 0)
++                        return r;
++        }
++
+         return 0;
+ }
+ 
+@@ -2568,10 +2589,10 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+         _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
+         _cleanup_(table_unrefp) Table *table = NULL;
++        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+         int neighbors_count = 0;
+         uint16_t capabilities_all = 0;
+         LLDPUserdata udata = {};
+-        TableCell *cell;
+ 
+         r = varlink_connect_address(&link, address);
+         if (r < 0)
+@@ -2592,50 +2613,58 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+         if (c < 0)
+                 return c;
+ 
+-        (void) pager_open(arg_pager_flags);
++        if (arg_json_format_flags == JSON_FORMAT_OFF) {
++                TableCell *cell;
+ 
+-        table = table_new("link",
+-                          "chassis id",
+-                          "system name",
+-                          "caps",
+-                          "port id",
+-                          "port description");
+-        if (!table)
+-                return log_oom();
++                (void) pager_open(arg_pager_flags);
+ 
+-        if (arg_full)
+-                table_set_width(table, 0);
++                table = table_new("link",
++                                  "chassis id",
++                                  "system name",
++                                  "caps",
++                                  "port id",
++                                  "port description");
++                if (!table)
++                        return log_oom();
+ 
+-        table_set_header(table, arg_legend);
++                if (arg_full)
++                        table_set_width(table, 0);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 0));
+-        table_set_minimum_width(table, cell, 16);
+-        table_set_maximum_width(table, cell, 16);
++                table_set_header(table, arg_legend);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 1));
+-        table_set_minimum_width(table, cell, 17);
+-        table_set_maximum_width(table, cell, 17);
++                assert_se(cell = table_get_cell(table, 0, 0));
++                table_set_minimum_width(table, cell, 16);
++                table_set_maximum_width(table, cell, 16);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 2));
+-        table_set_minimum_width(table, cell, 16);
+-        table_set_maximum_width(table, cell, 16);
++                assert_se(cell = table_get_cell(table, 0, 1));
++                table_set_minimum_width(table, cell, 17);
++                table_set_maximum_width(table, cell, 17);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 3));
+-        table_set_minimum_width(table, cell, 11);
+-        table_set_maximum_width(table, cell, 11);
++                assert_se(cell = table_get_cell(table, 0, 2));
++                table_set_minimum_width(table, cell, 16);
++                table_set_maximum_width(table, cell, 16);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 4));
+-        table_set_minimum_width(table, cell, 17);
+-        table_set_maximum_width(table, cell, 17);
++                assert_se(cell = table_get_cell(table, 0, 3));
++                table_set_minimum_width(table, cell, 11);
++                table_set_maximum_width(table, cell, 11);
+ 
+-        assert_se(cell = table_get_cell(table, 0, 5));
+-        table_set_minimum_width(table, cell, 16);
+-        table_set_maximum_width(table, cell, 16);
++                assert_se(cell = table_get_cell(table, 0, 4));
++                table_set_minimum_width(table, cell, 17);
++                table_set_maximum_width(table, cell, 17);
+ 
+-        if (table_set_empty_string(table, "n/a") < 0)
+-                return log_oom();
++                assert_se(cell = table_get_cell(table, 0, 5));
++                table_set_minimum_width(table, cell, 16);
++                table_set_maximum_width(table, cell, 16);
++
++                if (table_set_empty_string(table, "n/a") < 0)
++                        return log_oom();
+ 
+-        udata.table = table;
++                udata.table = table;
++        } else {
++                r = json_build(&v, JSON_BUILD_EMPTY_OBJECT);
++                if (r < 0)
++                        return r;
++        }
+ 
+         udata.neighbors_count = &neighbors_count;
+         udata.capabilities_all = &capabilities_all;
+@@ -2643,10 +2672,14 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+         varlink_set_userdata(link, &udata);
+ 
+         for (int i = 0; i < c; i++) {
+-                _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
++                _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL, *neighbors = NULL;
+ 
+                 udata.link_name = links[i].name;
+ 
++                if (arg_json_format_flags != JSON_FORMAT_OFF) {
++                        udata.json_list = &neighbors;
++                }
++
+                 r = json_build(&cparams, JSON_BUILD_OBJECT(
+                                         JSON_BUILD_PAIR("ifindex", JSON_BUILD_UNSIGNED(links[i].ifindex))));
+                 if (r < 0)
+@@ -2659,15 +2692,23 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
+                 r = varlink_observe_complete(link);
+                 if (r < 0)
+                         return r;
++
++                if (!json_variant_is_blank_array(neighbors)) {
++                        json_variant_set_field(&v, links[i].name, neighbors);
++                }
+         }
+ 
+-        r = table_print(table, NULL);
+-        if (r < 0)
+-                return table_log_print_error(r);
++        if (arg_json_format_flags == JSON_FORMAT_OFF) {
++                r = table_print(table, NULL);
++                if (r < 0)
++                        return table_log_print_error(r);
+ 
+-        if (arg_legend) {
+-                lldp_capabilities_legend(capabilities_all);
+-                printf("\n%i neighbors listed.\n", neighbors_count);
++                if (arg_legend) {
++                        lldp_capabilities_legend(capabilities_all);
++                        printf("\n%i neighbors listed.\n", neighbors_count);
++                }
++        } else {
++                json_variant_dump(v, arg_json_format_flags, NULL, NULL);
+         }
+ 
+         return 0;
+-- 
+2.33.0
+
diff --git a/board/czechlight/common/patches/systemd/sd_lldp.patch b/board/czechlight/common/patches/systemd/sd_lldp.patch
deleted file mode 100644
index d858e12..0000000
--- a/board/czechlight/common/patches/systemd/sd_lldp.patch
+++ /dev/null
@@ -1,4039 +0,0 @@
-From 1cdab502f703bdca754d7d55b9017d25c492c0c9 Mon Sep 17 00:00:00 2001
-From: Tomas Pecka <peckato1@fit.cvut.cz>
-Date: Mon, 7 Sep 2020 18:26:47 +0200
-Subject: [PATCH] Move sd_lldp to libsystemd
-
-This makes sd_lldp public in libsystemd. Clients can therefore operate
-with LLDP structures and files (e.g. parse LLDP neighbours).
----
- meson.build                            |   1 +
- src/libsystemd-network/lldp-internal.h |  39 --
- src/libsystemd-network/lldp-neighbor.c | 792 -------------------------
- src/libsystemd-network/lldp-neighbor.h |  92 ---
- src/libsystemd-network/lldp-network.c  |  78 ---
- src/libsystemd-network/lldp-network.h  |   6 -
- src/libsystemd-network/meson.build     |   6 -
- src/libsystemd-network/sd-lldp.c       | 498 ----------------
- src/libsystemd-network/test-lldp.c     | 378 ------------
- src/libsystemd/libsystemd.sym          |  39 ++
- src/libsystemd/meson.build             |   6 +
- src/libsystemd/sd-lldp/lldp-internal.h |  39 ++
- src/libsystemd/sd-lldp/lldp-neighbor.c | 792 +++++++++++++++++++++++++
- src/libsystemd/sd-lldp/lldp-neighbor.h |  92 +++
- src/libsystemd/sd-lldp/lldp-network.c  |  78 +++
- src/libsystemd/sd-lldp/lldp-network.h  |   6 +
- src/libsystemd/sd-lldp/sd-lldp.c       | 498 ++++++++++++++++
- src/libsystemd/sd-lldp/test-lldp.c     | 378 ++++++++++++
- src/systemd/meson.build                |   2 +-
- src/test/meson.build                   |  10 +-
- 20 files changed, 1935 insertions(+), 1895 deletions(-)
- delete mode 100644 src/libsystemd-network/lldp-internal.h
- delete mode 100644 src/libsystemd-network/lldp-neighbor.c
- delete mode 100644 src/libsystemd-network/lldp-neighbor.h
- delete mode 100644 src/libsystemd-network/lldp-network.c
- delete mode 100644 src/libsystemd-network/lldp-network.h
- delete mode 100644 src/libsystemd-network/sd-lldp.c
- delete mode 100644 src/libsystemd-network/test-lldp.c
- create mode 100644 src/libsystemd/sd-lldp/lldp-internal.h
- create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.c
- create mode 100644 src/libsystemd/sd-lldp/lldp-neighbor.h
- create mode 100644 src/libsystemd/sd-lldp/lldp-network.c
- create mode 100644 src/libsystemd/sd-lldp/lldp-network.h
- create mode 100644 src/libsystemd/sd-lldp/sd-lldp.c
- create mode 100644 src/libsystemd/sd-lldp/test-lldp.c
-
-diff --git a/meson.build b/meson.build
-index a5d1ed3d4d..45cc47f604 100644
---- a/meson.build
-+++ b/meson.build
-@@ -1552,6 +1552,7 @@ includes = include_directories('src/basic',
-                                'src/libsystemd/sd-event',
-                                'src/libsystemd/sd-hwdb',
-                                'src/libsystemd/sd-id128',
-+                               'src/libsystemd/sd-lldp',
-                                'src/libsystemd/sd-netlink',
-                                'src/libsystemd/sd-network',
-                                'src/libsystemd/sd-resolve',
-diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
-deleted file mode 100644
-index 9598438dba..0000000000
---- a/src/libsystemd-network/lldp-internal.h
-+++ /dev/null
-@@ -1,39 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--#pragma once
--
--#include "sd-event.h"
--#include "sd-lldp.h"
--
--#include "hashmap.h"
--#include "log.h"
--#include "prioq.h"
--
--struct sd_lldp {
--        unsigned n_ref;
--
--        int ifindex;
--        int fd;
--
--        sd_event *event;
--        int64_t event_priority;
--        sd_event_source *io_event_source;
--        sd_event_source *timer_event_source;
--
--        Prioq *neighbor_by_expiry;
--        Hashmap *neighbor_by_id;
--
--        uint64_t neighbors_max;
--
--        sd_lldp_callback_t callback;
--        void *userdata;
--
--        uint16_t capability_mask;
--
--        struct ether_addr filter_address;
--};
--
--#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
--#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
--
--const char* lldp_event_to_string(sd_lldp_event e) _const_;
--sd_lldp_event lldp_event_from_string(const char *s) _pure_;
-diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
-deleted file mode 100644
-index 02645b2bcd..0000000000
---- a/src/libsystemd-network/lldp-neighbor.c
-+++ /dev/null
-@@ -1,792 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--
--#include "alloc-util.h"
--#include "escape.h"
--#include "ether-addr-util.h"
--#include "hexdecoct.h"
--#include "in-addr-util.h"
--#include "lldp-internal.h"
--#include "lldp-neighbor.h"
--#include "memory-util.h"
--#include "missing_network.h"
--#include "unaligned.h"
--
--static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
--        siphash24_compress(id->chassis_id, id->chassis_id_size, state);
--        siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
--        siphash24_compress(id->port_id, id->port_id_size, state);
--        siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
--}
--
--int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
--        return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
--            ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
--}
--
--DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
--                                      sd_lldp_neighbor, lldp_neighbor_unlink);
--
--int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
--        const sd_lldp_neighbor *x = a, *y = b;
--
--        return CMP(x->until, y->until);
--}
--
--_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
--        if (!n)
--                return NULL;
--
--        assert(n->n_ref > 0 || n->lldp);
--        n->n_ref++;
--
--        return n;
--}
--
--static void lldp_neighbor_free(sd_lldp_neighbor *n) {
--        assert(n);
--
--        free(n->id.port_id);
--        free(n->id.chassis_id);
--        free(n->port_description);
--        free(n->system_name);
--        free(n->system_description);
--        free(n->mud_url);
--        free(n->chassis_id_as_string);
--        free(n->port_id_as_string);
--        free(n);
--}
--
--_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
--
--        /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
--         * the sd_lldp object. */
--
--        if (!n)
--                return NULL;
--
--        assert(n->n_ref > 0);
--        n->n_ref--;
--
--        if (n->n_ref <= 0 && !n->lldp)
--                lldp_neighbor_free(n);
--
--        return NULL;
--}
--
--sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
--
--        /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
--
--        if (!n)
--                return NULL;
--
--        if (!n->lldp)
--                return NULL;
--
--        /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
--         * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
--         * ourselves from the hashtable and sometimes are called after we already are de-registered. */
--
--        (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
--
--        assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
--
--        n->lldp = NULL;
--
--        if (n->n_ref <= 0)
--                lldp_neighbor_free(n);
--
--        return NULL;
--}
--
--sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
--        sd_lldp_neighbor *n;
--
--        n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
--        if (!n)
--                return NULL;
--
--        n->raw_size = raw_size;
--        n->n_ref = 1;
--
--        return n;
--}
--
--static int parse_string(char **s, const void *q, size_t n) {
--        const char *p = q;
--        char *k;
--
--        assert(s);
--        assert(p || n == 0);
--
--        if (*s) {
--                log_lldp("Found duplicate string, ignoring field.");
--                return 0;
--        }
--
--        /* Strip trailing NULs, just to be nice */
--        while (n > 0 && p[n-1] == 0)
--                n--;
--
--        if (n <= 0) /* Ignore empty strings */
--                return 0;
--
--        /* Look for inner NULs */
--        if (memchr(p, 0, n)) {
--                log_lldp("Found inner NUL in string, ignoring field.");
--                return 0;
--        }
--
--        /* Let's escape weird chars, for security reasons */
--        k = cescape_length(p, n);
--        if (!k)
--                return -ENOMEM;
--
--        free(*s);
--        *s = k;
--
--        return 1;
--}
--
--int lldp_neighbor_parse(sd_lldp_neighbor *n) {
--        struct ether_header h;
--        const uint8_t *p;
--        size_t left;
--        int r;
--
--        assert(n);
--
--        if (n->raw_size < sizeof(struct ether_header)) {
--                log_lldp("Received truncated packet, ignoring.");
--                return -EBADMSG;
--        }
--
--        memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
--
--        if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
--                log_lldp("Received packet with wrong type, ignoring.");
--                return -EBADMSG;
--        }
--
--        if (h.ether_dhost[0] != 0x01 ||
--            h.ether_dhost[1] != 0x80 ||
--            h.ether_dhost[2] != 0xc2 ||
--            h.ether_dhost[3] != 0x00 ||
--            h.ether_dhost[4] != 0x00 ||
--            !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
--                log_lldp("Received packet with wrong destination address, ignoring.");
--                return -EBADMSG;
--        }
--
--        memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
--        memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
--
--        p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
--        left = n->raw_size - sizeof(struct ether_header);
--
--        for (;;) {
--                uint8_t type;
--                uint16_t length;
--
--                if (left < 2) {
--                        log_lldp("TLV lacks header, ignoring.");
--                        return -EBADMSG;
--                }
--
--                type = p[0] >> 1;
--                length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
--                p += 2, left -= 2;
--
--                if (left < length) {
--                        log_lldp("TLV truncated, ignoring datagram.");
--                        return -EBADMSG;
--                }
--
--                switch (type) {
--
--                case SD_LLDP_TYPE_END:
--                        if (length != 0) {
--                                log_lldp("End marker TLV not zero-sized, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--
--                        /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
--                         * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
--
--                        goto end_marker;
--
--                case SD_LLDP_TYPE_CHASSIS_ID:
--                        if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
--                                log_lldp("Chassis ID field size out of range, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--                        if (n->id.chassis_id) {
--                                log_lldp("Duplicate chassis ID field, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--
--                        n->id.chassis_id = memdup(p, length);
--                        if (!n->id.chassis_id)
--                                return -ENOMEM;
--
--                        n->id.chassis_id_size = length;
--                        break;
--
--                case SD_LLDP_TYPE_PORT_ID:
--                        if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
--                                log_lldp("Port ID field size out of range, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--                        if (n->id.port_id) {
--                                log_lldp("Duplicate port ID field, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--
--                        n->id.port_id = memdup(p, length);
--                        if (!n->id.port_id)
--                                return -ENOMEM;
--
--                        n->id.port_id_size = length;
--                        break;
--
--                case SD_LLDP_TYPE_TTL:
--                        if (length != 2) {
--                                log_lldp("TTL field has wrong size, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--
--                        if (n->has_ttl) {
--                                log_lldp("Duplicate TTL field, ignoring datagram.");
--                                return -EBADMSG;
--                        }
--
--                        n->ttl = unaligned_read_be16(p);
--                        n->has_ttl = true;
--                        break;
--
--                case SD_LLDP_TYPE_PORT_DESCRIPTION:
--                        r = parse_string(&n->port_description, p, length);
--                        if (r < 0)
--                                return r;
--                        break;
--
--                case SD_LLDP_TYPE_SYSTEM_NAME:
--                        r = parse_string(&n->system_name, p, length);
--                        if (r < 0)
--                                return r;
--                        break;
--
--                case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
--                        r = parse_string(&n->system_description, p, length);
--                        if (r < 0)
--                                return r;
--                        break;
--
--                case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
--                        if (length != 4)
--                                log_lldp("System capabilities field has wrong size, ignoring.");
--                        else {
--                                n->system_capabilities = unaligned_read_be16(p);
--                                n->enabled_capabilities = unaligned_read_be16(p + 2);
--                                n->has_capabilities = true;
--                        }
--
--                        break;
--
--                case SD_LLDP_TYPE_PRIVATE: {
--                        if (length < 4)
--                                log_lldp("Found private TLV that is too short, ignoring.");
--                        else {
--                                /* RFC 8520: MUD URL */
--                                if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
--                                    p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
--                                        r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
--                                                         length - 1 - sizeof(SD_LLDP_OUI_MUD));
--                                        if (r < 0)
--                                                return r;
--                                }
--                        }
--                }
--
--                        break;
--                }
--
--                p += length, left -= length;
--        }
--
--end_marker:
--        if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
--                log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
--                return -EBADMSG;
--
--        }
--
--        n->rindex = sizeof(struct ether_header);
--
--        return 0;
--}
--
--void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
--        assert(n);
--
--        if (n->ttl > 0) {
--                usec_t base;
--
--                /* Use the packet's timestamp if there is one known */
--                base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
--                if (base <= 0 || base == USEC_INFINITY)
--                        base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
--
--                n->until = usec_add(base, n->ttl * USEC_PER_SEC);
--        } else
--                n->until = 0;
--
--        if (n->lldp)
--                prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
--}
--
--bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
--        if (a == b)
--                return true;
--
--        if (!a || !b)
--                return false;
--
--        if (a->raw_size != b->raw_size)
--                return false;
--
--        return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
--}
--
--_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
--        assert_return(n, -EINVAL);
--        assert_return(address, -EINVAL);
--
--        *address = n->source_address;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
--        assert_return(n, -EINVAL);
--        assert_return(address, -EINVAL);
--
--        *address = n->destination_address;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--        assert_return(size, -EINVAL);
--
--        *ret = LLDP_NEIGHBOR_RAW(n);
--        *size = n->raw_size;
--
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
--        assert_return(n, -EINVAL);
--        assert_return(type, -EINVAL);
--        assert_return(ret, -EINVAL);
--        assert_return(size, -EINVAL);
--
--        assert(n->id.chassis_id_size > 0);
--
--        *type = *(uint8_t*) n->id.chassis_id;
--        *ret = (uint8_t*) n->id.chassis_id + 1;
--        *size = n->id.chassis_id_size - 1;
--
--        return 0;
--}
--
--static int format_mac_address(const void *data, size_t sz, char **ret) {
--        struct ether_addr a;
--        char *k;
--
--        assert(data || sz <= 0);
--
--        if (sz != 7)
--                return 0;
--
--        memcpy(&a, (uint8_t*) data + 1, sizeof(a));
--
--        k = new(char, ETHER_ADDR_TO_STRING_MAX);
--        if (!k)
--                return -ENOMEM;
--
--        *ret = ether_addr_to_string(&a, k);
--        return 1;
--}
--
--static int format_network_address(const void *data, size_t sz, char **ret) {
--        union in_addr_union a;
--        int family, r;
--
--        if (sz == 6 && ((uint8_t*) data)[1] == 1) {
--                memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
--                family = AF_INET;
--        } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
--                memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
--                family = AF_INET6;
--        } else
--                return 0;
--
--        r = in_addr_to_string(family, &a, ret);
--        if (r < 0)
--                return r;
--        return 1;
--}
--
--_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
--        char *k;
--        int r;
--
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (n->chassis_id_as_string) {
--                *ret = n->chassis_id_as_string;
--                return 0;
--        }
--
--        assert(n->id.chassis_id_size > 0);
--
--        switch (*(uint8_t*) n->id.chassis_id) {
--
--        case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
--        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
--        case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
--        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
--        case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
--                k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
--                if (!k)
--                        return -ENOMEM;
--
--                goto done;
--
--        case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
--                r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
--                if (r < 0)
--                        return r;
--                if (r > 0)
--                        goto done;
--
--                break;
--
--        case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
--                r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
--                if (r < 0)
--                        return r;
--                if (r > 0)
--                        goto done;
--
--                break;
--        }
--
--        /* Generic fallback */
--        k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
--        if (!k)
--                return -ENOMEM;
--
--done:
--        *ret = n->chassis_id_as_string = k;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
--        assert_return(n, -EINVAL);
--        assert_return(type, -EINVAL);
--        assert_return(ret, -EINVAL);
--        assert_return(size, -EINVAL);
--
--        assert(n->id.port_id_size > 0);
--
--        *type = *(uint8_t*) n->id.port_id;
--        *ret = (uint8_t*) n->id.port_id + 1;
--        *size = n->id.port_id_size - 1;
--
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
--        char *k;
--        int r;
--
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (n->port_id_as_string) {
--                *ret = n->port_id_as_string;
--                return 0;
--        }
--
--        assert(n->id.port_id_size > 0);
--
--        switch (*(uint8_t*) n->id.port_id) {
--
--        case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
--        case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
--        case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
--        case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
--                k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
--                if (!k)
--                        return -ENOMEM;
--
--                goto done;
--
--        case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
--                r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
--                if (r < 0)
--                        return r;
--                if (r > 0)
--                        goto done;
--
--                break;
--
--        case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
--                r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
--                if (r < 0)
--                        return r;
--                if (r > 0)
--                        goto done;
--
--                break;
--        }
--
--        /* Generic fallback */
--        k = hexmem(n->id.port_id, n->id.port_id_size);
--        if (!k)
--                return -ENOMEM;
--
--done:
--        *ret = n->port_id_as_string = k;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
--        assert_return(n, -EINVAL);
--        assert_return(ret_sec, -EINVAL);
--
--        *ret_sec = n->ttl;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->system_name)
--                return -ENODATA;
--
--        *ret = n->system_name;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->system_description)
--                return -ENODATA;
--
--        *ret = n->system_description;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->port_description)
--                return -ENODATA;
--
--        *ret = n->port_description;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->mud_url)
--                return -ENODATA;
--
--        *ret = n->mud_url;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->has_capabilities)
--                return -ENODATA;
--
--        *ret = n->system_capabilities;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (!n->has_capabilities)
--                return -ENODATA;
--
--        *ret = n->enabled_capabilities;
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
--        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
--        int r;
--
--        assert_return(ret, -EINVAL);
--        assert_return(raw || raw_size <= 0, -EINVAL);
--
--        n = lldp_neighbor_new(raw_size);
--        if (!n)
--                return -ENOMEM;
--
--        memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
--        r = lldp_neighbor_parse(n);
--        if (r < 0)
--                return r;
--
--        *ret = TAKE_PTR(n);
--
--        return r;
--}
--
--_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
--        assert_return(n, -EINVAL);
--
--        assert(n->raw_size >= sizeof(struct ether_header));
--        n->rindex = sizeof(struct ether_header);
--
--        return n->rindex < n->raw_size;
--}
--
--_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
--        size_t length;
--
--        assert_return(n, -EINVAL);
--
--        if (n->rindex == n->raw_size) /* EOF */
--                return -ESPIPE;
--
--        if (n->rindex + 2 > n->raw_size) /* Truncated message */
--                return -EBADMSG;
--
--        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
--        if (n->rindex + 2 + length > n->raw_size)
--                return -EBADMSG;
--
--        n->rindex += 2 + length;
--        return n->rindex < n->raw_size;
--}
--
--_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
--        assert_return(n, -EINVAL);
--        assert_return(type, -EINVAL);
--
--        if (n->rindex == n->raw_size) /* EOF */
--                return -ESPIPE;
--
--        if (n->rindex + 2 > n->raw_size)
--                return -EBADMSG;
--
--        *type = LLDP_NEIGHBOR_TLV_TYPE(n);
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
--        uint8_t k;
--        int r;
--
--        assert_return(n, -EINVAL);
--
--        r = sd_lldp_neighbor_tlv_get_type(n, &k);
--        if (r < 0)
--                return r;
--
--        return type == k;
--}
--
--_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
--        const uint8_t *d;
--        size_t length;
--        int r;
--
--        assert_return(n, -EINVAL);
--        assert_return(oui, -EINVAL);
--        assert_return(subtype, -EINVAL);
--
--        r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
--        if (r < 0)
--                return r;
--        if (r == 0)
--                return -ENXIO;
--
--        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
--        if (length < 4)
--                return -EBADMSG;
--
--        if (n->rindex + 2 + length > n->raw_size)
--                return -EBADMSG;
--
--        d = LLDP_NEIGHBOR_TLV_DATA(n);
--        memcpy(oui, d, 3);
--        *subtype = d[3];
--
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
--        uint8_t k[3], st;
--        int r;
--
--        r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
--        if (r == -ENXIO)
--                return 0;
--        if (r < 0)
--                return r;
--
--        return memcmp(k, oui, 3) == 0 && st == subtype;
--}
--
--_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
--        size_t length;
--
--        assert_return(n, -EINVAL);
--        assert_return(ret, -EINVAL);
--        assert_return(size, -EINVAL);
--
--        /* Note that this returns the full TLV, including the TLV header */
--
--        if (n->rindex + 2 > n->raw_size)
--                return -EBADMSG;
--
--        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
--        if (n->rindex + 2 + length > n->raw_size)
--                return -EBADMSG;
--
--        *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
--        *size = length + 2;
--
--        return 0;
--}
--
--_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
--        assert_return(n, -EINVAL);
--        assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
--        assert_return(clock_supported(clock), -EOPNOTSUPP);
--        assert_return(ret, -EINVAL);
--
--        if (!triple_timestamp_is_set(&n->timestamp))
--                return -ENODATA;
--
--        *ret = triple_timestamp_by_clock(&n->timestamp, clock);
--        return 0;
--}
-diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h
-deleted file mode 100644
-index 74175edf54..0000000000
---- a/src/libsystemd-network/lldp-neighbor.h
-+++ /dev/null
-@@ -1,92 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--#pragma once
--
--#include <inttypes.h>
--#include <stdbool.h>
--#include <sys/types.h>
--
--#include "sd-lldp.h"
--
--#include "hash-funcs.h"
--#include "lldp-internal.h"
--#include "time-util.h"
--
--typedef struct LLDPNeighborID {
--        /* The spec calls this an "MSAP identifier" */
--        void *chassis_id;
--        size_t chassis_id_size;
--
--        void *port_id;
--        size_t port_id_size;
--} LLDPNeighborID;
--
--struct sd_lldp_neighbor {
--        /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
--        sd_lldp *lldp;
--        unsigned n_ref;
--
--        triple_timestamp timestamp;
--
--        usec_t until;
--        unsigned prioq_idx;
--
--        struct ether_addr source_address;
--        struct ether_addr destination_address;
--
--        LLDPNeighborID id;
--
--        /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
--        size_t raw_size;
--
--        /* The current read index for the iterative TLV interface */
--        size_t rindex;
--
--        /* And a couple of fields parsed out. */
--        bool has_ttl:1;
--        bool has_capabilities:1;
--        bool has_port_vlan_id:1;
--
--        uint16_t ttl;
--
--        uint16_t system_capabilities;
--        uint16_t enabled_capabilities;
--
--        char *port_description;
--        char *system_name;
--        char *system_description;
--        char *mud_url;
--
--        uint16_t port_vlan_id;
--
--        char *chassis_id_as_string;
--        char *port_id_as_string;
--};
--
--static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
--        return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
--}
--
--static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
--        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
--}
--
--static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
--        uint8_t *p;
--
--        p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
--        return p[1] + (((size_t) (p[0] & 1)) << 8);
--}
--
--static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
--        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
--}
--
--extern const struct hash_ops lldp_neighbor_hash_ops;
--int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
--int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
--
--sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
--sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
--int lldp_neighbor_parse(sd_lldp_neighbor *n);
--void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
--bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
-diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
-deleted file mode 100644
-index 53e329734b..0000000000
---- a/src/libsystemd-network/lldp-network.c
-+++ /dev/null
-@@ -1,78 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--
--#include <linux/filter.h>
--#include <netinet/if_ether.h>
--
--#include "fd-util.h"
--#include "lldp-network.h"
--#include "missing_network.h"
--#include "socket-util.h"
--
--int lldp_network_bind_raw_socket(int ifindex) {
--
--        static const struct sock_filter filter[] = {
--                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)),      /* A <- 4 bytes of destination MAC */
--                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
--                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
--                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
--                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0),                        /* A != 00:00 */
--                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0),                        /* A != 00:03 */
--                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0),                        /* A != 00:0e */
--                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
--                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)),     /* A <- protocol */
--                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0),                /* A != ETHERTYPE_LLDP */
--                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
--                BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1),                                 /* accept packet */
--        };
--
--        static const struct sock_fprog fprog = {
--                .len = ELEMENTSOF(filter),
--                .filter = (struct sock_filter*) filter,
--        };
--
--        struct packet_mreq mreq = {
--                .mr_ifindex = ifindex,
--                .mr_type = PACKET_MR_MULTICAST,
--                .mr_alen = ETH_ALEN,
--                .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
--        };
--
--        union sockaddr_union saddrll = {
--                .ll.sll_family = AF_PACKET,
--                .ll.sll_ifindex = ifindex,
--        };
--
--        _cleanup_close_ int fd = -1;
--        int r;
--
--        assert(ifindex > 0);
--
--        fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
--                    htobe16(ETHERTYPE_LLDP));
--        if (fd < 0)
--                return -errno;
--
--        r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
--        if (r < 0)
--                return -errno;
--
--        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
--        if (r < 0)
--                return -errno;
--
--        mreq.mr_address[ETH_ALEN - 1] = 0x03;
--        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
--        if (r < 0)
--                return -errno;
--
--        mreq.mr_address[ETH_ALEN - 1] = 0x0E;
--        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
--        if (r < 0)
--                return -errno;
--
--        r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
--        if (r < 0)
--                return -errno;
--
--        return TAKE_FD(fd);
--}
-diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
-deleted file mode 100644
-index e4ed2898a5..0000000000
---- a/src/libsystemd-network/lldp-network.h
-+++ /dev/null
-@@ -1,6 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--#pragma once
--
--#include "sd-event.h"
--
--int lldp_network_bind_raw_socket(int ifindex);
-diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
-index 7fa0c67956..517ffd1471 100644
---- a/src/libsystemd-network/meson.build
-+++ b/src/libsystemd-network/meson.build
-@@ -34,12 +34,6 @@ sources = files('''
-         sd-dhcp6-lease.c
-         dhcp-identifier.h
-         dhcp-identifier.c
--        lldp-internal.h
--        lldp-network.h
--        lldp-network.c
--        lldp-neighbor.h
--        lldp-neighbor.c
--        sd-lldp.c
- '''.split())
- 
- network_internal_h = files('network-internal.h')
-diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
-deleted file mode 100644
-index d3606cf501..0000000000
---- a/src/libsystemd-network/sd-lldp.c
-+++ /dev/null
-@@ -1,498 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--
--#include <arpa/inet.h>
--#include <linux/sockios.h>
--#include <sys/ioctl.h>
--
--#include "sd-lldp.h"
--
--#include "alloc-util.h"
--#include "ether-addr-util.h"
--#include "event-util.h"
--#include "fd-util.h"
--#include "lldp-internal.h"
--#include "lldp-neighbor.h"
--#include "lldp-network.h"
--#include "memory-util.h"
--#include "socket-util.h"
--#include "sort-util.h"
--#include "string-table.h"
--
--#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
--
--static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
--        [SD_LLDP_EVENT_ADDED]   = "added",
--        [SD_LLDP_EVENT_REMOVED] = "removed",
--        [SD_LLDP_EVENT_UPDATED]   = "updated",
--        [SD_LLDP_EVENT_REFRESHED] = "refreshed",
--};
--
--DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
--
--static void lldp_flush_neighbors(sd_lldp *lldp) {
--        assert(lldp);
--
--        hashmap_clear(lldp->neighbor_by_id);
--}
--
--static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
--        assert(lldp);
--        assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
--
--        if (!lldp->callback) {
--                log_lldp("Received '%s' event.", lldp_event_to_string(event));
--                return;
--        }
--
--        log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
--        lldp->callback(lldp, event, n, lldp->userdata);
--}
--
--static int lldp_make_space(sd_lldp *lldp, size_t extra) {
--        usec_t t = USEC_INFINITY;
--        bool changed = false;
--
--        assert(lldp);
--
--        /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
--         * are free. */
--
--        for (;;) {
--                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
--
--                n = prioq_peek(lldp->neighbor_by_expiry);
--                if (!n)
--                        break;
--
--                sd_lldp_neighbor_ref(n);
--
--                if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
--                        goto remove_one;
--
--                if (t == USEC_INFINITY)
--                        t = now(clock_boottime_or_monotonic());
--
--                if (n->until > t)
--                        break;
--
--        remove_one:
--                lldp_neighbor_unlink(n);
--                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
--                changed = true;
--        }
--
--        return changed;
--}
--
--static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
--        assert(lldp);
--        assert(n);
--
--        /* Don't keep data with a zero TTL */
--        if (n->ttl <= 0)
--                return false;
--
--        /* Filter out data from the filter address */
--        if (!ether_addr_is_null(&lldp->filter_address) &&
--            ether_addr_equal(&lldp->filter_address, &n->source_address))
--                return false;
--
--        /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
--         * no caps field set. */
--        if (n->has_capabilities &&
--            (n->enabled_capabilities & lldp->capability_mask) == 0)
--                return false;
--
--        /* Keep everything else */
--        return true;
--}
--
--static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
--
--static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
--        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
--        bool keep;
--        int r;
--
--        assert(lldp);
--        assert(n);
--        assert(!n->lldp);
--
--        keep = lldp_keep_neighbor(lldp, n);
--
--        /* First retrieve the old entry for this MSAP */
--        old = hashmap_get(lldp->neighbor_by_id, &n->id);
--        if (old) {
--                sd_lldp_neighbor_ref(old);
--
--                if (!keep) {
--                        lldp_neighbor_unlink(old);
--                        lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
--                        return 0;
--                }
--
--                if (lldp_neighbor_equal(n, old)) {
--                        /* Is this equal, then restart the TTL counter, but don't do anything else. */
--                        old->timestamp = n->timestamp;
--                        lldp_start_timer(lldp, old);
--                        lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
--                        return 0;
--                }
--
--                /* Data changed, remove the old entry, and add a new one */
--                lldp_neighbor_unlink(old);
--
--        } else if (!keep)
--                return 0;
--
--        /* Then, make room for at least one new neighbor */
--        lldp_make_space(lldp, 1);
--
--        r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
--        if (r < 0)
--                goto finish;
--
--        r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
--        if (r < 0) {
--                assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
--                goto finish;
--        }
--
--        n->lldp = lldp;
--
--        lldp_start_timer(lldp, n);
--        lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
--
--        return 1;
--
--finish:
--        if (old)
--                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
--
--        return r;
--}
--
--static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
--        int r;
--
--        assert(lldp);
--        assert(n);
--
--        r = lldp_neighbor_parse(n);
--        if (r == -EBADMSG) /* Ignore bad messages */
--                return 0;
--        if (r < 0)
--                return r;
--
--        r = lldp_add_neighbor(lldp, n);
--        if (r < 0) {
--                log_lldp_errno(r, "Failed to add datagram. Ignoring.");
--                return 0;
--        }
--
--        log_lldp("Successfully processed LLDP datagram.");
--        return 0;
--}
--
--static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
--        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
--        ssize_t space, length;
--        sd_lldp *lldp = userdata;
--        struct timespec ts;
--
--        assert(fd >= 0);
--        assert(lldp);
--
--        space = next_datagram_size_fd(fd);
--        if (space < 0)
--                return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
--
--        n = lldp_neighbor_new(space);
--        if (!n)
--                return -ENOMEM;
--
--        length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
--        if (length < 0) {
--                if (IN_SET(errno, EAGAIN, EINTR))
--                        return 0;
--
--                return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
--        }
--
--        if ((size_t) length != n->raw_size) {
--                log_lldp("Packet size mismatch.");
--                return -EINVAL;
--        }
--
--        /* Try to get the timestamp of this packet if it is known */
--        if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
--                triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
--        else
--                triple_timestamp_get(&n->timestamp);
--
--        return lldp_handle_datagram(lldp, n);
--}
--
--static void lldp_reset(sd_lldp *lldp) {
--        assert(lldp);
--
--        (void) event_source_disable(lldp->timer_event_source);
--        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
--        lldp->fd = safe_close(lldp->fd);
--}
--
--_public_ int sd_lldp_start(sd_lldp *lldp) {
--        int r;
--
--        assert_return(lldp, -EINVAL);
--        assert_return(lldp->event, -EINVAL);
--        assert_return(lldp->ifindex > 0, -EINVAL);
--
--        if (lldp->fd >= 0)
--                return 0;
--
--        assert(!lldp->io_event_source);
--
--        lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
--        if (lldp->fd < 0)
--                return lldp->fd;
--
--        r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
--        if (r < 0)
--                goto fail;
--
--        r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
--        if (r < 0)
--                goto fail;
--
--        (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
--
--        log_lldp("Started LLDP client");
--        return 1;
--
--fail:
--        lldp_reset(lldp);
--        return r;
--}
--
--_public_ int sd_lldp_stop(sd_lldp *lldp) {
--        assert_return(lldp, -EINVAL);
--
--        if (lldp->fd < 0)
--                return 0;
--
--        log_lldp("Stopping LLDP client");
--
--        lldp_reset(lldp);
--        lldp_flush_neighbors(lldp);
--
--        return 1;
--}
--
--_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
--        int r;
--
--        assert_return(lldp, -EINVAL);
--        assert_return(lldp->fd < 0, -EBUSY);
--        assert_return(!lldp->event, -EBUSY);
--
--        if (event)
--                lldp->event = sd_event_ref(event);
--        else {
--                r = sd_event_default(&lldp->event);
--                if (r < 0)
--                        return r;
--        }
--
--        lldp->event_priority = priority;
--
--        return 0;
--}
--
--_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
--
--        assert_return(lldp, -EINVAL);
--        assert_return(lldp->fd < 0, -EBUSY);
--
--        lldp->event = sd_event_unref(lldp->event);
--        return 0;
--}
--
--_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
--        assert_return(lldp, NULL);
--
--        return lldp->event;
--}
--
--_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
--        assert_return(lldp, -EINVAL);
--
--        lldp->callback = cb;
--        lldp->userdata = userdata;
--
--        return 0;
--}
--
--_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
--        assert_return(lldp, -EINVAL);
--        assert_return(ifindex > 0, -EINVAL);
--        assert_return(lldp->fd < 0, -EBUSY);
--
--        lldp->ifindex = ifindex;
--        return 0;
--}
--
--static sd_lldp* lldp_free(sd_lldp *lldp) {
--        assert(lldp);
--
--        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
--
--        lldp_reset(lldp);
--        sd_lldp_detach_event(lldp);
--        lldp_flush_neighbors(lldp);
--
--        hashmap_free(lldp->neighbor_by_id);
--        prioq_free(lldp->neighbor_by_expiry);
--        return mfree(lldp);
--}
--
--DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
--
--_public_ int sd_lldp_new(sd_lldp **ret) {
--        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
--        int r;
--
--        assert_return(ret, -EINVAL);
--
--        lldp = new(sd_lldp, 1);
--        if (!lldp)
--                return -ENOMEM;
--
--        *lldp = (sd_lldp) {
--                .n_ref = 1,
--                .fd = -1,
--                .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
--                .capability_mask = (uint16_t) -1,
--        };
--
--        lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
--        if (!lldp->neighbor_by_id)
--                return -ENOMEM;
--
--        r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
--        if (r < 0)
--                return r;
--
--        *ret = TAKE_PTR(lldp);
--
--        return 0;
--}
--
--static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
--        return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
--}
--
--static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
--        sd_lldp *lldp = userdata;
--        int r;
--
--        r = lldp_make_space(lldp, 0);
--        if (r < 0)
--                return log_lldp_errno(r, "Failed to make space: %m");
--
--        r = lldp_start_timer(lldp, NULL);
--        if (r < 0)
--                return log_lldp_errno(r, "Failed to restart timer: %m");
--
--        return 0;
--}
--
--static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
--        sd_lldp_neighbor *n;
--
--        assert(lldp);
--
--        if (neighbor)
--                lldp_neighbor_start_ttl(neighbor);
--
--        n = prioq_peek(lldp->neighbor_by_expiry);
--        if (!n)
--                return event_source_disable(lldp->timer_event_source);
--
--        if (!lldp->event)
--                return 0;
--
--        return event_reset_time(lldp->event, &lldp->timer_event_source,
--                                clock_boottime_or_monotonic(),
--                                n->until, 0,
--                                on_timer_event, lldp,
--                                lldp->event_priority, "lldp-timer", true);
--}
--
--_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
--        sd_lldp_neighbor **l = NULL, *n;
--        Iterator i;
--        int k = 0, r;
--
--        assert_return(lldp, -EINVAL);
--        assert_return(ret, -EINVAL);
--
--        if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
--                *ret = NULL;
--                return 0;
--        }
--
--        l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
--        if (!l)
--                return -ENOMEM;
--
--        r = lldp_start_timer(lldp, NULL);
--        if (r < 0) {
--                free(l);
--                return r;
--        }
--
--        HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
--                l[k++] = sd_lldp_neighbor_ref(n);
--
--        assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
--
--        /* Return things in a stable order */
--        typesafe_qsort(l, k, neighbor_compare_func);
--        *ret = l;
--
--        return k;
--}
--
--_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
--        assert_return(lldp, -EINVAL);
--        assert_return(m > 0, -EINVAL);
--
--        lldp->neighbors_max = m;
--        lldp_make_space(lldp, 0);
--
--        return 0;
--}
--
--_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
--        assert_return(lldp, -EINVAL);
--        assert_return(mask != 0, -EINVAL);
--
--        lldp->capability_mask = mask;
--
--        return 0;
--}
--
--_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
--        assert_return(lldp, -EINVAL);
--
--        /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
--         * that our own can be filtered out here. */
--
--        if (addr)
--                lldp->filter_address = *addr;
--        else
--                zero(lldp->filter_address);
--
--        return 0;
--}
-diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
-deleted file mode 100644
-index a2ac65095f..0000000000
---- a/src/libsystemd-network/test-lldp.c
-+++ /dev/null
-@@ -1,378 +0,0 @@
--/* SPDX-License-Identifier: LGPL-2.1+ */
--
--#include <arpa/inet.h>
--#include <errno.h>
--#include <net/ethernet.h>
--#include <stdio.h>
--#include <unistd.h>
--
--#include "sd-event.h"
--#include "sd-lldp.h"
--
--#include "alloc-util.h"
--#include "fd-util.h"
--#include "lldp-network.h"
--#include "macro.h"
--#include "string-util.h"
--#include "tests.h"
--
--#define TEST_LLDP_PORT "em1"
--#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
--#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
--
--static int test_fd[2] = { -1, -1 };
--static int lldp_handler_calls;
--
--int lldp_network_bind_raw_socket(int ifindex) {
--        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
--                return -errno;
--
--        return test_fd[0];
--}
--
--static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
--        lldp_handler_calls++;
--}
--
--static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
--        int r;
--
--        r = sd_lldp_new(lldp);
--        if (r < 0)
--                return r;
--
--        r = sd_lldp_set_ifindex(*lldp, 42);
--        if (r < 0)
--                return r;
--
--        r = sd_lldp_set_callback(*lldp, cb, cb_data);
--        if (r < 0)
--                return r;
--
--        r = sd_lldp_attach_event(*lldp, e, 0);
--        if (r < 0)
--                return r;
--
--        r = sd_lldp_start(*lldp);
--        if (r < 0)
--                return r;
--
--        return 0;
--}
--
--static int stop_lldp(sd_lldp *lldp) {
--        int r;
--
--        r = sd_lldp_stop(lldp);
--        if (r < 0)
--                return r;
--
--        r = sd_lldp_detach_event(lldp);
--        if (r < 0)
--                return r;
--
--        sd_lldp_unref(lldp);
--        safe_close(test_fd[1]);
--
--        return 0;
--}
--
--static void test_receive_basic_packet(sd_event *e) {
--
--        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: interface name, "1/3" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                /* LLDP optional TLVs */
--                0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
--                0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
--                0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--
--        sd_lldp *lldp;
--        sd_lldp_neighbor **neighbors;
--        uint8_t type;
--        const void *data;
--        uint16_t ttl;
--        size_t length;
--        const char *str;
--
--        lldp_handler_calls = 0;
--        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
--
--        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
--        sd_event_run(e, 0);
--        assert_se(lldp_handler_calls == 1);
--        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
--
--        assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0);
--        assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
--        assert_se(length == ETH_ALEN);
--        assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
--
--        assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0);
--        assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME);
--        assert_se(length == 3);
--        assert_se(!memcmp(data, "1/3", 3));
--
--        assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0);
--        assert_se(streq(str, "Port"));
--
--        assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0);
--        assert_se(streq(str, "SYS"));
--
--        assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0);
--        assert_se(streq(str, "foo"));
--
--        assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0);
--        assert_se(ttl == 120);
--
--        sd_lldp_neighbor_unref(neighbors[0]);
--        free(neighbors);
--
--        assert_se(stop_lldp(lldp) == 0);
--}
--
--static void test_receive_incomplete_packet(sd_event *e) {
--        sd_lldp *lldp;
--        sd_lldp_neighbor **neighbors;
--        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: interface name, "1/3" */
--                                                        /* Missing TTL */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--
--        lldp_handler_calls = 0;
--        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
--
--        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
--        sd_event_run(e, 0);
--        assert_se(lldp_handler_calls == 0);
--        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0);
--
--        assert_se(stop_lldp(lldp) == 0);
--}
--
--static void test_receive_oui_packet(sd_event *e) {
--        sd_lldp *lldp;
--        sd_lldp_neighbor **neighbors;
--        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 */
--        };
--
--        lldp_handler_calls = 0;
--        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
--
--        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
--        sd_event_run(e, 0);
--        assert_se(lldp_handler_calls == 1);
--        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
--
--        assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0);
--        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_3, SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
--        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0);
--        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0);
--
--        sd_lldp_neighbor_unref(neighbors[0]);
--        free(neighbors);
--
--        assert_se(stop_lldp(lldp) == 0);
--}
--
--static void test_multiple_neighbors_sorted(sd_event *e) {
--
--        static const uint8_t frame1[] = {
--                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
--                0x04, 0x04, 0x02, '2', '/', '3',        /* Port component: "2/3" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const uint8_t frame2[] = {
--                /* 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, 0x04, 0x01, '2', '/', '1',        /* Chassis component: "2/1" */
--                0x04, 0x04, 0x02, '1', '/', '3',        /* Port component: "1/3" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const uint8_t frame3[] = {
--                /* 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, 0x05, 0x01, '2', '/', '1', '0',   /* Chassis component: "2/10" */
--                0x04, 0x04, 0x02, '1', '/', '0',        /* Port component: "1/0" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const uint8_t frame4[] = {
--                /* 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, 0x05, 0x01, '2', '/', '1', '9',   /* Chassis component: "2/19" */
--                0x04, 0x04, 0x02, '1', '/', '0',        /* Port component: "1/0" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const uint8_t frame5[] = {
--                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
--                0x04, 0x05, 0x02, '2', '/', '1', '0',   /* Port component: "2/10" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const uint8_t frame6[] = {
--                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
--                0x04, 0x05, 0x02, '2', '/', '3', '9',   /* Port component: "2/10" */
--                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
--                0x00, 0x00                              /* End Of LLDPDU */
--        };
--        static const char* expected[] = {
--                /* ordered pairs of Chassis+Port */
--                "1/2", "2/10",
--                "1/2", "2/3",
--                "1/2", "2/39",
--                "2/1", "1/3",
--                "2/10", "1/0",
--                "2/19", "1/0",
--        };
--
--        sd_lldp *lldp;
--        sd_lldp_neighbor **neighbors;
--        int i;
--        uint8_t type;
--        const void *data;
--        size_t length, expected_length;
--        uint16_t ttl;
--
--        lldp_handler_calls = 0;
--        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
--
--        assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1));
--        sd_event_run(e, 0);
--        assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2));
--        sd_event_run(e, 0);
--        assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3));
--        sd_event_run(e, 0);
--        assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4));
--        sd_event_run(e, 0);
--        assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5));
--        sd_event_run(e, 0);
--        assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6));
--        sd_event_run(e, 0);
--        assert_se(lldp_handler_calls == 6);
--
--        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6);
--
--        for (i = 0; i < 6; i++) {
--                assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0);
--                assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT);
--                expected_length = strlen(expected[2 * i]);
--                assert_se(length == expected_length);
--                assert_se(memcmp(data, expected[2 * i], expected_length) == 0);
--
--                assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0);
--                assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT);
--                expected_length = strlen(expected[2 * i + 1]);
--                assert_se(length == expected_length);
--                assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0);
--
--                assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0);
--                assert_se(ttl == 120);
--        }
--
--        for (i = 0; i < 6; i++)
--                sd_lldp_neighbor_unref(neighbors[i]);
--        free(neighbors);
--
--        assert_se(stop_lldp(lldp) == 0);
--}
--
--int main(int argc, char *argv[]) {
--        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
--
--        test_setup_logging(LOG_DEBUG);
--
--        /* LLDP reception tests */
--        assert_se(sd_event_new(&e) == 0);
--        test_receive_basic_packet(e);
--        test_receive_incomplete_packet(e);
--        test_receive_oui_packet(e);
--        test_multiple_neighbors_sorted(e);
--
--        return 0;
--}
-diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
-index 1e654b49ea..d584ac2faf 100644
---- a/src/libsystemd/libsystemd.sym
-+++ b/src/libsystemd/libsystemd.sym
-@@ -720,4 +720,43 @@ global:
- 
-         sd_journal_enumerate_available_data;
-         sd_journal_enumerate_available_unique;
-+
-+        sd_lldp_start;
-+		sd_lldp_neighbor_ref;
-+        sd_lldp_neighbor_unref;
-+        sd_lldp_neighbor_get_source_address;
-+        sd_lldp_neighbor_get_destination_address;
-+        sd_lldp_neighbor_get_raw;
-+        sd_lldp_neighbor_get_chassis_id;
-+        sd_lldp_neighbor_get_chassis_id_as_string;
-+        sd_lldp_neighbor_get_port_id;
-+        sd_lldp_neighbor_get_port_id_as_string;
-+        sd_lldp_neighbor_get_ttl;
-+        sd_lldp_neighbor_get_system_name;
-+        sd_lldp_neighbor_get_system_description;
-+        sd_lldp_neighbor_get_port_description;
-+        sd_lldp_neighbor_get_mud_url;
-+        sd_lldp_neighbor_get_system_capabilities;
-+        sd_lldp_neighbor_get_enabled_capabilities;
-+        sd_lldp_neighbor_from_raw;
-+        sd_lldp_neighbor_tlv_rewind;
-+        sd_lldp_neighbor_tlv_next;
-+        sd_lldp_neighbor_tlv_get_type;
-+        sd_lldp_neighbor_tlv_is_type;
-+        sd_lldp_neighbor_tlv_get_oui;
-+        sd_lldp_neighbor_tlv_is_oui;
-+        sd_lldp_neighbor_tlv_get_raw;
-+        sd_lldp_neighbor_get_timestamp;
-+        sd_lldp_start;
-+        sd_lldp_stop;
-+        sd_lldp_attach_event;
-+        sd_lldp_detach_event;
-+        sd_lldp_get_event;
-+        sd_lldp_set_callback;
-+        sd_lldp_set_ifindex;
-+        sd_lldp_new;
-+        sd_lldp_get_neighbors;
-+        sd_lldp_set_neighbors_max;
-+        sd_lldp_match_capabilities;
-+        sd_lldp_set_filter_address;
- } LIBSYSTEMD_245;
-diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
-index aa1ed9b7dd..db28fbcf37 100644
---- a/src/libsystemd/meson.build
-+++ b/src/libsystemd/meson.build
-@@ -70,6 +70,12 @@ libsystemd_sources = files('''
-         sd-hwdb/hwdb-util.c
-         sd-hwdb/hwdb-util.h
-         sd-hwdb/sd-hwdb.c
-+        sd-lldp/sd-lldp.c
-+        sd-lldp/lldp-internal.h
-+        sd-lldp/lldp-neighbor.c
-+        sd-lldp/lldp-neighbor.h
-+        sd-lldp/lldp-network.c
-+        sd-lldp/lldp-network.h
-         sd-netlink/generic-netlink.c
-         sd-netlink/generic-netlink.h
-         sd-netlink/netlink-internal.h
-diff --git a/src/libsystemd/sd-lldp/lldp-internal.h b/src/libsystemd/sd-lldp/lldp-internal.h
-new file mode 100644
-index 0000000000..9598438dba
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/lldp-internal.h
-@@ -0,0 +1,39 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+#pragma once
-+
-+#include "sd-event.h"
-+#include "sd-lldp.h"
-+
-+#include "hashmap.h"
-+#include "log.h"
-+#include "prioq.h"
-+
-+struct sd_lldp {
-+        unsigned n_ref;
-+
-+        int ifindex;
-+        int fd;
-+
-+        sd_event *event;
-+        int64_t event_priority;
-+        sd_event_source *io_event_source;
-+        sd_event_source *timer_event_source;
-+
-+        Prioq *neighbor_by_expiry;
-+        Hashmap *neighbor_by_id;
-+
-+        uint64_t neighbors_max;
-+
-+        sd_lldp_callback_t callback;
-+        void *userdata;
-+
-+        uint16_t capability_mask;
-+
-+        struct ether_addr filter_address;
-+};
-+
-+#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
-+#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
-+
-+const char* lldp_event_to_string(sd_lldp_event e) _const_;
-+sd_lldp_event lldp_event_from_string(const char *s) _pure_;
-diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.c b/src/libsystemd/sd-lldp/lldp-neighbor.c
-new file mode 100644
-index 0000000000..02645b2bcd
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/lldp-neighbor.c
-@@ -0,0 +1,792 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+
-+#include "alloc-util.h"
-+#include "escape.h"
-+#include "ether-addr-util.h"
-+#include "hexdecoct.h"
-+#include "in-addr-util.h"
-+#include "lldp-internal.h"
-+#include "lldp-neighbor.h"
-+#include "memory-util.h"
-+#include "missing_network.h"
-+#include "unaligned.h"
-+
-+static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
-+        siphash24_compress(id->chassis_id, id->chassis_id_size, state);
-+        siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
-+        siphash24_compress(id->port_id, id->port_id_size, state);
-+        siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
-+}
-+
-+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
-+        return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
-+            ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
-+}
-+
-+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
-+                                      sd_lldp_neighbor, lldp_neighbor_unlink);
-+
-+int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
-+        const sd_lldp_neighbor *x = a, *y = b;
-+
-+        return CMP(x->until, y->until);
-+}
-+
-+_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
-+        if (!n)
-+                return NULL;
-+
-+        assert(n->n_ref > 0 || n->lldp);
-+        n->n_ref++;
-+
-+        return n;
-+}
-+
-+static void lldp_neighbor_free(sd_lldp_neighbor *n) {
-+        assert(n);
-+
-+        free(n->id.port_id);
-+        free(n->id.chassis_id);
-+        free(n->port_description);
-+        free(n->system_name);
-+        free(n->system_description);
-+        free(n->mud_url);
-+        free(n->chassis_id_as_string);
-+        free(n->port_id_as_string);
-+        free(n);
-+}
-+
-+_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
-+
-+        /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
-+         * the sd_lldp object. */
-+
-+        if (!n)
-+                return NULL;
-+
-+        assert(n->n_ref > 0);
-+        n->n_ref--;
-+
-+        if (n->n_ref <= 0 && !n->lldp)
-+                lldp_neighbor_free(n);
-+
-+        return NULL;
-+}
-+
-+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
-+
-+        /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
-+
-+        if (!n)
-+                return NULL;
-+
-+        if (!n->lldp)
-+                return NULL;
-+
-+        /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
-+         * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
-+         * ourselves from the hashtable and sometimes are called after we already are de-registered. */
-+
-+        (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
-+
-+        assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
-+
-+        n->lldp = NULL;
-+
-+        if (n->n_ref <= 0)
-+                lldp_neighbor_free(n);
-+
-+        return NULL;
-+}
-+
-+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
-+        sd_lldp_neighbor *n;
-+
-+        n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
-+        if (!n)
-+                return NULL;
-+
-+        n->raw_size = raw_size;
-+        n->n_ref = 1;
-+
-+        return n;
-+}
-+
-+static int parse_string(char **s, const void *q, size_t n) {
-+        const char *p = q;
-+        char *k;
-+
-+        assert(s);
-+        assert(p || n == 0);
-+
-+        if (*s) {
-+                log_lldp("Found duplicate string, ignoring field.");
-+                return 0;
-+        }
-+
-+        /* Strip trailing NULs, just to be nice */
-+        while (n > 0 && p[n-1] == 0)
-+                n--;
-+
-+        if (n <= 0) /* Ignore empty strings */
-+                return 0;
-+
-+        /* Look for inner NULs */
-+        if (memchr(p, 0, n)) {
-+                log_lldp("Found inner NUL in string, ignoring field.");
-+                return 0;
-+        }
-+
-+        /* Let's escape weird chars, for security reasons */
-+        k = cescape_length(p, n);
-+        if (!k)
-+                return -ENOMEM;
-+
-+        free(*s);
-+        *s = k;
-+
-+        return 1;
-+}
-+
-+int lldp_neighbor_parse(sd_lldp_neighbor *n) {
-+        struct ether_header h;
-+        const uint8_t *p;
-+        size_t left;
-+        int r;
-+
-+        assert(n);
-+
-+        if (n->raw_size < sizeof(struct ether_header)) {
-+                log_lldp("Received truncated packet, ignoring.");
-+                return -EBADMSG;
-+        }
-+
-+        memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
-+
-+        if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
-+                log_lldp("Received packet with wrong type, ignoring.");
-+                return -EBADMSG;
-+        }
-+
-+        if (h.ether_dhost[0] != 0x01 ||
-+            h.ether_dhost[1] != 0x80 ||
-+            h.ether_dhost[2] != 0xc2 ||
-+            h.ether_dhost[3] != 0x00 ||
-+            h.ether_dhost[4] != 0x00 ||
-+            !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
-+                log_lldp("Received packet with wrong destination address, ignoring.");
-+                return -EBADMSG;
-+        }
-+
-+        memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
-+        memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
-+
-+        p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
-+        left = n->raw_size - sizeof(struct ether_header);
-+
-+        for (;;) {
-+                uint8_t type;
-+                uint16_t length;
-+
-+                if (left < 2) {
-+                        log_lldp("TLV lacks header, ignoring.");
-+                        return -EBADMSG;
-+                }
-+
-+                type = p[0] >> 1;
-+                length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
-+                p += 2, left -= 2;
-+
-+                if (left < length) {
-+                        log_lldp("TLV truncated, ignoring datagram.");
-+                        return -EBADMSG;
-+                }
-+
-+                switch (type) {
-+
-+                case SD_LLDP_TYPE_END:
-+                        if (length != 0) {
-+                                log_lldp("End marker TLV not zero-sized, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+
-+                        /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
-+                         * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
-+
-+                        goto end_marker;
-+
-+                case SD_LLDP_TYPE_CHASSIS_ID:
-+                        if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
-+                                log_lldp("Chassis ID field size out of range, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+                        if (n->id.chassis_id) {
-+                                log_lldp("Duplicate chassis ID field, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+
-+                        n->id.chassis_id = memdup(p, length);
-+                        if (!n->id.chassis_id)
-+                                return -ENOMEM;
-+
-+                        n->id.chassis_id_size = length;
-+                        break;
-+
-+                case SD_LLDP_TYPE_PORT_ID:
-+                        if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
-+                                log_lldp("Port ID field size out of range, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+                        if (n->id.port_id) {
-+                                log_lldp("Duplicate port ID field, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+
-+                        n->id.port_id = memdup(p, length);
-+                        if (!n->id.port_id)
-+                                return -ENOMEM;
-+
-+                        n->id.port_id_size = length;
-+                        break;
-+
-+                case SD_LLDP_TYPE_TTL:
-+                        if (length != 2) {
-+                                log_lldp("TTL field has wrong size, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+
-+                        if (n->has_ttl) {
-+                                log_lldp("Duplicate TTL field, ignoring datagram.");
-+                                return -EBADMSG;
-+                        }
-+
-+                        n->ttl = unaligned_read_be16(p);
-+                        n->has_ttl = true;
-+                        break;
-+
-+                case SD_LLDP_TYPE_PORT_DESCRIPTION:
-+                        r = parse_string(&n->port_description, p, length);
-+                        if (r < 0)
-+                                return r;
-+                        break;
-+
-+                case SD_LLDP_TYPE_SYSTEM_NAME:
-+                        r = parse_string(&n->system_name, p, length);
-+                        if (r < 0)
-+                                return r;
-+                        break;
-+
-+                case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
-+                        r = parse_string(&n->system_description, p, length);
-+                        if (r < 0)
-+                                return r;
-+                        break;
-+
-+                case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
-+                        if (length != 4)
-+                                log_lldp("System capabilities field has wrong size, ignoring.");
-+                        else {
-+                                n->system_capabilities = unaligned_read_be16(p);
-+                                n->enabled_capabilities = unaligned_read_be16(p + 2);
-+                                n->has_capabilities = true;
-+                        }
-+
-+                        break;
-+
-+                case SD_LLDP_TYPE_PRIVATE: {
-+                        if (length < 4)
-+                                log_lldp("Found private TLV that is too short, ignoring.");
-+                        else {
-+                                /* RFC 8520: MUD URL */
-+                                if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
-+                                    p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
-+                                        r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
-+                                                         length - 1 - sizeof(SD_LLDP_OUI_MUD));
-+                                        if (r < 0)
-+                                                return r;
-+                                }
-+                        }
-+                }
-+
-+                        break;
-+                }
-+
-+                p += length, left -= length;
-+        }
-+
-+end_marker:
-+        if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
-+                log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
-+                return -EBADMSG;
-+
-+        }
-+
-+        n->rindex = sizeof(struct ether_header);
-+
-+        return 0;
-+}
-+
-+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
-+        assert(n);
-+
-+        if (n->ttl > 0) {
-+                usec_t base;
-+
-+                /* Use the packet's timestamp if there is one known */
-+                base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
-+                if (base <= 0 || base == USEC_INFINITY)
-+                        base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
-+
-+                n->until = usec_add(base, n->ttl * USEC_PER_SEC);
-+        } else
-+                n->until = 0;
-+
-+        if (n->lldp)
-+                prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
-+}
-+
-+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
-+        if (a == b)
-+                return true;
-+
-+        if (!a || !b)
-+                return false;
-+
-+        if (a->raw_size != b->raw_size)
-+                return false;
-+
-+        return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
-+        assert_return(n, -EINVAL);
-+        assert_return(address, -EINVAL);
-+
-+        *address = n->source_address;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
-+        assert_return(n, -EINVAL);
-+        assert_return(address, -EINVAL);
-+
-+        *address = n->destination_address;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+        assert_return(size, -EINVAL);
-+
-+        *ret = LLDP_NEIGHBOR_RAW(n);
-+        *size = n->raw_size;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
-+        assert_return(n, -EINVAL);
-+        assert_return(type, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+        assert_return(size, -EINVAL);
-+
-+        assert(n->id.chassis_id_size > 0);
-+
-+        *type = *(uint8_t*) n->id.chassis_id;
-+        *ret = (uint8_t*) n->id.chassis_id + 1;
-+        *size = n->id.chassis_id_size - 1;
-+
-+        return 0;
-+}
-+
-+static int format_mac_address(const void *data, size_t sz, char **ret) {
-+        struct ether_addr a;
-+        char *k;
-+
-+        assert(data || sz <= 0);
-+
-+        if (sz != 7)
-+                return 0;
-+
-+        memcpy(&a, (uint8_t*) data + 1, sizeof(a));
-+
-+        k = new(char, ETHER_ADDR_TO_STRING_MAX);
-+        if (!k)
-+                return -ENOMEM;
-+
-+        *ret = ether_addr_to_string(&a, k);
-+        return 1;
-+}
-+
-+static int format_network_address(const void *data, size_t sz, char **ret) {
-+        union in_addr_union a;
-+        int family, r;
-+
-+        if (sz == 6 && ((uint8_t*) data)[1] == 1) {
-+                memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
-+                family = AF_INET;
-+        } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
-+                memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
-+                family = AF_INET6;
-+        } else
-+                return 0;
-+
-+        r = in_addr_to_string(family, &a, ret);
-+        if (r < 0)
-+                return r;
-+        return 1;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
-+        char *k;
-+        int r;
-+
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (n->chassis_id_as_string) {
-+                *ret = n->chassis_id_as_string;
-+                return 0;
-+        }
-+
-+        assert(n->id.chassis_id_size > 0);
-+
-+        switch (*(uint8_t*) n->id.chassis_id) {
-+
-+        case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
-+        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
-+        case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
-+        case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
-+        case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
-+                k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
-+                if (!k)
-+                        return -ENOMEM;
-+
-+                goto done;
-+
-+        case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-+                r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
-+                if (r < 0)
-+                        return r;
-+                if (r > 0)
-+                        goto done;
-+
-+                break;
-+
-+        case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
-+                r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
-+                if (r < 0)
-+                        return r;
-+                if (r > 0)
-+                        goto done;
-+
-+                break;
-+        }
-+
-+        /* Generic fallback */
-+        k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
-+        if (!k)
-+                return -ENOMEM;
-+
-+done:
-+        *ret = n->chassis_id_as_string = k;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
-+        assert_return(n, -EINVAL);
-+        assert_return(type, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+        assert_return(size, -EINVAL);
-+
-+        assert(n->id.port_id_size > 0);
-+
-+        *type = *(uint8_t*) n->id.port_id;
-+        *ret = (uint8_t*) n->id.port_id + 1;
-+        *size = n->id.port_id_size - 1;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
-+        char *k;
-+        int r;
-+
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (n->port_id_as_string) {
-+                *ret = n->port_id_as_string;
-+                return 0;
-+        }
-+
-+        assert(n->id.port_id_size > 0);
-+
-+        switch (*(uint8_t*) n->id.port_id) {
-+
-+        case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
-+        case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
-+        case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
-+        case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-+                k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
-+                if (!k)
-+                        return -ENOMEM;
-+
-+                goto done;
-+
-+        case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-+                r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
-+                if (r < 0)
-+                        return r;
-+                if (r > 0)
-+                        goto done;
-+
-+                break;
-+
-+        case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
-+                r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
-+                if (r < 0)
-+                        return r;
-+                if (r > 0)
-+                        goto done;
-+
-+                break;
-+        }
-+
-+        /* Generic fallback */
-+        k = hexmem(n->id.port_id, n->id.port_id_size);
-+        if (!k)
-+                return -ENOMEM;
-+
-+done:
-+        *ret = n->port_id_as_string = k;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret_sec, -EINVAL);
-+
-+        *ret_sec = n->ttl;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->system_name)
-+                return -ENODATA;
-+
-+        *ret = n->system_name;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->system_description)
-+                return -ENODATA;
-+
-+        *ret = n->system_description;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->port_description)
-+                return -ENODATA;
-+
-+        *ret = n->port_description;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->mud_url)
-+                return -ENODATA;
-+
-+        *ret = n->mud_url;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->has_capabilities)
-+                return -ENODATA;
-+
-+        *ret = n->system_capabilities;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!n->has_capabilities)
-+                return -ENODATA;
-+
-+        *ret = n->enabled_capabilities;
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
-+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-+        int r;
-+
-+        assert_return(ret, -EINVAL);
-+        assert_return(raw || raw_size <= 0, -EINVAL);
-+
-+        n = lldp_neighbor_new(raw_size);
-+        if (!n)
-+                return -ENOMEM;
-+
-+        memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
-+        r = lldp_neighbor_parse(n);
-+        if (r < 0)
-+                return r;
-+
-+        *ret = TAKE_PTR(n);
-+
-+        return r;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
-+        assert_return(n, -EINVAL);
-+
-+        assert(n->raw_size >= sizeof(struct ether_header));
-+        n->rindex = sizeof(struct ether_header);
-+
-+        return n->rindex < n->raw_size;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
-+        size_t length;
-+
-+        assert_return(n, -EINVAL);
-+
-+        if (n->rindex == n->raw_size) /* EOF */
-+                return -ESPIPE;
-+
-+        if (n->rindex + 2 > n->raw_size) /* Truncated message */
-+                return -EBADMSG;
-+
-+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-+        if (n->rindex + 2 + length > n->raw_size)
-+                return -EBADMSG;
-+
-+        n->rindex += 2 + length;
-+        return n->rindex < n->raw_size;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
-+        assert_return(n, -EINVAL);
-+        assert_return(type, -EINVAL);
-+
-+        if (n->rindex == n->raw_size) /* EOF */
-+                return -ESPIPE;
-+
-+        if (n->rindex + 2 > n->raw_size)
-+                return -EBADMSG;
-+
-+        *type = LLDP_NEIGHBOR_TLV_TYPE(n);
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
-+        uint8_t k;
-+        int r;
-+
-+        assert_return(n, -EINVAL);
-+
-+        r = sd_lldp_neighbor_tlv_get_type(n, &k);
-+        if (r < 0)
-+                return r;
-+
-+        return type == k;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
-+        const uint8_t *d;
-+        size_t length;
-+        int r;
-+
-+        assert_return(n, -EINVAL);
-+        assert_return(oui, -EINVAL);
-+        assert_return(subtype, -EINVAL);
-+
-+        r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
-+        if (r < 0)
-+                return r;
-+        if (r == 0)
-+                return -ENXIO;
-+
-+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-+        if (length < 4)
-+                return -EBADMSG;
-+
-+        if (n->rindex + 2 + length > n->raw_size)
-+                return -EBADMSG;
-+
-+        d = LLDP_NEIGHBOR_TLV_DATA(n);
-+        memcpy(oui, d, 3);
-+        *subtype = d[3];
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
-+        uint8_t k[3], st;
-+        int r;
-+
-+        r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
-+        if (r == -ENXIO)
-+                return 0;
-+        if (r < 0)
-+                return r;
-+
-+        return memcmp(k, oui, 3) == 0 && st == subtype;
-+}
-+
-+_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
-+        size_t length;
-+
-+        assert_return(n, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+        assert_return(size, -EINVAL);
-+
-+        /* Note that this returns the full TLV, including the TLV header */
-+
-+        if (n->rindex + 2 > n->raw_size)
-+                return -EBADMSG;
-+
-+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
-+        if (n->rindex + 2 + length > n->raw_size)
-+                return -EBADMSG;
-+
-+        *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
-+        *size = length + 2;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
-+        assert_return(n, -EINVAL);
-+        assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
-+        assert_return(clock_supported(clock), -EOPNOTSUPP);
-+        assert_return(ret, -EINVAL);
-+
-+        if (!triple_timestamp_is_set(&n->timestamp))
-+                return -ENODATA;
-+
-+        *ret = triple_timestamp_by_clock(&n->timestamp, clock);
-+        return 0;
-+}
-diff --git a/src/libsystemd/sd-lldp/lldp-neighbor.h b/src/libsystemd/sd-lldp/lldp-neighbor.h
-new file mode 100644
-index 0000000000..74175edf54
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/lldp-neighbor.h
-@@ -0,0 +1,92 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+#pragma once
-+
-+#include <inttypes.h>
-+#include <stdbool.h>
-+#include <sys/types.h>
-+
-+#include "sd-lldp.h"
-+
-+#include "hash-funcs.h"
-+#include "lldp-internal.h"
-+#include "time-util.h"
-+
-+typedef struct LLDPNeighborID {
-+        /* The spec calls this an "MSAP identifier" */
-+        void *chassis_id;
-+        size_t chassis_id_size;
-+
-+        void *port_id;
-+        size_t port_id_size;
-+} LLDPNeighborID;
-+
-+struct sd_lldp_neighbor {
-+        /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
-+        sd_lldp *lldp;
-+        unsigned n_ref;
-+
-+        triple_timestamp timestamp;
-+
-+        usec_t until;
-+        unsigned prioq_idx;
-+
-+        struct ether_addr source_address;
-+        struct ether_addr destination_address;
-+
-+        LLDPNeighborID id;
-+
-+        /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
-+        size_t raw_size;
-+
-+        /* The current read index for the iterative TLV interface */
-+        size_t rindex;
-+
-+        /* And a couple of fields parsed out. */
-+        bool has_ttl:1;
-+        bool has_capabilities:1;
-+        bool has_port_vlan_id:1;
-+
-+        uint16_t ttl;
-+
-+        uint16_t system_capabilities;
-+        uint16_t enabled_capabilities;
-+
-+        char *port_description;
-+        char *system_name;
-+        char *system_description;
-+        char *mud_url;
-+
-+        uint16_t port_vlan_id;
-+
-+        char *chassis_id_as_string;
-+        char *port_id_as_string;
-+};
-+
-+static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
-+        return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
-+}
-+
-+static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
-+        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
-+}
-+
-+static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
-+        uint8_t *p;
-+
-+        p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
-+        return p[1] + (((size_t) (p[0] & 1)) << 8);
-+}
-+
-+static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
-+        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
-+}
-+
-+extern const struct hash_ops lldp_neighbor_hash_ops;
-+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
-+int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
-+
-+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
-+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
-+int lldp_neighbor_parse(sd_lldp_neighbor *n);
-+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
-+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
-diff --git a/src/libsystemd/sd-lldp/lldp-network.c b/src/libsystemd/sd-lldp/lldp-network.c
-new file mode 100644
-index 0000000000..53e329734b
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/lldp-network.c
-@@ -0,0 +1,78 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+
-+#include <linux/filter.h>
-+#include <netinet/if_ether.h>
-+
-+#include "fd-util.h"
-+#include "lldp-network.h"
-+#include "missing_network.h"
-+#include "socket-util.h"
-+
-+int lldp_network_bind_raw_socket(int ifindex) {
-+
-+        static const struct sock_filter filter[] = {
-+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)),      /* A <- 4 bytes of destination MAC */
-+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
-+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
-+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0),                        /* A != 00:00 */
-+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0),                        /* A != 00:03 */
-+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0),                        /* A != 00:0e */
-+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)),     /* A <- protocol */
-+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0),                /* A != ETHERTYPE_LLDP */
-+                BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-+                BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1),                                 /* accept packet */
-+        };
-+
-+        static const struct sock_fprog fprog = {
-+                .len = ELEMENTSOF(filter),
-+                .filter = (struct sock_filter*) filter,
-+        };
-+
-+        struct packet_mreq mreq = {
-+                .mr_ifindex = ifindex,
-+                .mr_type = PACKET_MR_MULTICAST,
-+                .mr_alen = ETH_ALEN,
-+                .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
-+        };
-+
-+        union sockaddr_union saddrll = {
-+                .ll.sll_family = AF_PACKET,
-+                .ll.sll_ifindex = ifindex,
-+        };
-+
-+        _cleanup_close_ int fd = -1;
-+        int r;
-+
-+        assert(ifindex > 0);
-+
-+        fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
-+                    htobe16(ETHERTYPE_LLDP));
-+        if (fd < 0)
-+                return -errno;
-+
-+        r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
-+        if (r < 0)
-+                return -errno;
-+
-+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-+        if (r < 0)
-+                return -errno;
-+
-+        mreq.mr_address[ETH_ALEN - 1] = 0x03;
-+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-+        if (r < 0)
-+                return -errno;
-+
-+        mreq.mr_address[ETH_ALEN - 1] = 0x0E;
-+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-+        if (r < 0)
-+                return -errno;
-+
-+        r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
-+        if (r < 0)
-+                return -errno;
-+
-+        return TAKE_FD(fd);
-+}
-diff --git a/src/libsystemd/sd-lldp/lldp-network.h b/src/libsystemd/sd-lldp/lldp-network.h
-new file mode 100644
-index 0000000000..e4ed2898a5
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/lldp-network.h
-@@ -0,0 +1,6 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+#pragma once
-+
-+#include "sd-event.h"
-+
-+int lldp_network_bind_raw_socket(int ifindex);
-diff --git a/src/libsystemd/sd-lldp/sd-lldp.c b/src/libsystemd/sd-lldp/sd-lldp.c
-new file mode 100644
-index 0000000000..d3606cf501
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/sd-lldp.c
-@@ -0,0 +1,498 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+
-+#include <arpa/inet.h>
-+#include <linux/sockios.h>
-+#include <sys/ioctl.h>
-+
-+#include "sd-lldp.h"
-+
-+#include "alloc-util.h"
-+#include "ether-addr-util.h"
-+#include "event-util.h"
-+#include "fd-util.h"
-+#include "lldp-internal.h"
-+#include "lldp-neighbor.h"
-+#include "lldp-network.h"
-+#include "memory-util.h"
-+#include "socket-util.h"
-+#include "sort-util.h"
-+#include "string-table.h"
-+
-+#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
-+
-+static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
-+        [SD_LLDP_EVENT_ADDED]   = "added",
-+        [SD_LLDP_EVENT_REMOVED] = "removed",
-+        [SD_LLDP_EVENT_UPDATED]   = "updated",
-+        [SD_LLDP_EVENT_REFRESHED] = "refreshed",
-+};
-+
-+DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
-+
-+static void lldp_flush_neighbors(sd_lldp *lldp) {
-+        assert(lldp);
-+
-+        hashmap_clear(lldp->neighbor_by_id);
-+}
-+
-+static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
-+        assert(lldp);
-+        assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
-+
-+        if (!lldp->callback) {
-+                log_lldp("Received '%s' event.", lldp_event_to_string(event));
-+                return;
-+        }
-+
-+        log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
-+        lldp->callback(lldp, event, n, lldp->userdata);
-+}
-+
-+static int lldp_make_space(sd_lldp *lldp, size_t extra) {
-+        usec_t t = USEC_INFINITY;
-+        bool changed = false;
-+
-+        assert(lldp);
-+
-+        /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
-+         * are free. */
-+
-+        for (;;) {
-+                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-+
-+                n = prioq_peek(lldp->neighbor_by_expiry);
-+                if (!n)
-+                        break;
-+
-+                sd_lldp_neighbor_ref(n);
-+
-+                if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
-+                        goto remove_one;
-+
-+                if (t == USEC_INFINITY)
-+                        t = now(clock_boottime_or_monotonic());
-+
-+                if (n->until > t)
-+                        break;
-+
-+        remove_one:
-+                lldp_neighbor_unlink(n);
-+                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
-+                changed = true;
-+        }
-+
-+        return changed;
-+}
-+
-+static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
-+        assert(lldp);
-+        assert(n);
-+
-+        /* Don't keep data with a zero TTL */
-+        if (n->ttl <= 0)
-+                return false;
-+
-+        /* Filter out data from the filter address */
-+        if (!ether_addr_is_null(&lldp->filter_address) &&
-+            ether_addr_equal(&lldp->filter_address, &n->source_address))
-+                return false;
-+
-+        /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
-+         * no caps field set. */
-+        if (n->has_capabilities &&
-+            (n->enabled_capabilities & lldp->capability_mask) == 0)
-+                return false;
-+
-+        /* Keep everything else */
-+        return true;
-+}
-+
-+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
-+
-+static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
-+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
-+        bool keep;
-+        int r;
-+
-+        assert(lldp);
-+        assert(n);
-+        assert(!n->lldp);
-+
-+        keep = lldp_keep_neighbor(lldp, n);
-+
-+        /* First retrieve the old entry for this MSAP */
-+        old = hashmap_get(lldp->neighbor_by_id, &n->id);
-+        if (old) {
-+                sd_lldp_neighbor_ref(old);
-+
-+                if (!keep) {
-+                        lldp_neighbor_unlink(old);
-+                        lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
-+                        return 0;
-+                }
-+
-+                if (lldp_neighbor_equal(n, old)) {
-+                        /* Is this equal, then restart the TTL counter, but don't do anything else. */
-+                        old->timestamp = n->timestamp;
-+                        lldp_start_timer(lldp, old);
-+                        lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
-+                        return 0;
-+                }
-+
-+                /* Data changed, remove the old entry, and add a new one */
-+                lldp_neighbor_unlink(old);
-+
-+        } else if (!keep)
-+                return 0;
-+
-+        /* Then, make room for at least one new neighbor */
-+        lldp_make_space(lldp, 1);
-+
-+        r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
-+        if (r < 0)
-+                goto finish;
-+
-+        r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
-+        if (r < 0) {
-+                assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
-+                goto finish;
-+        }
-+
-+        n->lldp = lldp;
-+
-+        lldp_start_timer(lldp, n);
-+        lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
-+
-+        return 1;
-+
-+finish:
-+        if (old)
-+                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
-+
-+        return r;
-+}
-+
-+static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
-+        int r;
-+
-+        assert(lldp);
-+        assert(n);
-+
-+        r = lldp_neighbor_parse(n);
-+        if (r == -EBADMSG) /* Ignore bad messages */
-+                return 0;
-+        if (r < 0)
-+                return r;
-+
-+        r = lldp_add_neighbor(lldp, n);
-+        if (r < 0) {
-+                log_lldp_errno(r, "Failed to add datagram. Ignoring.");
-+                return 0;
-+        }
-+
-+        log_lldp("Successfully processed LLDP datagram.");
-+        return 0;
-+}
-+
-+static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-+        ssize_t space, length;
-+        sd_lldp *lldp = userdata;
-+        struct timespec ts;
-+
-+        assert(fd >= 0);
-+        assert(lldp);
-+
-+        space = next_datagram_size_fd(fd);
-+        if (space < 0)
-+                return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
-+
-+        n = lldp_neighbor_new(space);
-+        if (!n)
-+                return -ENOMEM;
-+
-+        length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
-+        if (length < 0) {
-+                if (IN_SET(errno, EAGAIN, EINTR))
-+                        return 0;
-+
-+                return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
-+        }
-+
-+        if ((size_t) length != n->raw_size) {
-+                log_lldp("Packet size mismatch.");
-+                return -EINVAL;
-+        }
-+
-+        /* Try to get the timestamp of this packet if it is known */
-+        if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
-+                triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
-+        else
-+                triple_timestamp_get(&n->timestamp);
-+
-+        return lldp_handle_datagram(lldp, n);
-+}
-+
-+static void lldp_reset(sd_lldp *lldp) {
-+        assert(lldp);
-+
-+        (void) event_source_disable(lldp->timer_event_source);
-+        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
-+        lldp->fd = safe_close(lldp->fd);
-+}
-+
-+_public_ int sd_lldp_start(sd_lldp *lldp) {
-+        int r;
-+
-+        assert_return(lldp, -EINVAL);
-+        assert_return(lldp->event, -EINVAL);
-+        assert_return(lldp->ifindex > 0, -EINVAL);
-+
-+        if (lldp->fd >= 0)
-+                return 0;
-+
-+        assert(!lldp->io_event_source);
-+
-+        lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
-+        if (lldp->fd < 0)
-+                return lldp->fd;
-+
-+        r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
-+        if (r < 0)
-+                goto fail;
-+
-+        r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
-+        if (r < 0)
-+                goto fail;
-+
-+        (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
-+
-+        log_lldp("Started LLDP client");
-+        return 1;
-+
-+fail:
-+        lldp_reset(lldp);
-+        return r;
-+}
-+
-+_public_ int sd_lldp_stop(sd_lldp *lldp) {
-+        assert_return(lldp, -EINVAL);
-+
-+        if (lldp->fd < 0)
-+                return 0;
-+
-+        log_lldp("Stopping LLDP client");
-+
-+        lldp_reset(lldp);
-+        lldp_flush_neighbors(lldp);
-+
-+        return 1;
-+}
-+
-+_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
-+        int r;
-+
-+        assert_return(lldp, -EINVAL);
-+        assert_return(lldp->fd < 0, -EBUSY);
-+        assert_return(!lldp->event, -EBUSY);
-+
-+        if (event)
-+                lldp->event = sd_event_ref(event);
-+        else {
-+                r = sd_event_default(&lldp->event);
-+                if (r < 0)
-+                        return r;
-+        }
-+
-+        lldp->event_priority = priority;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
-+
-+        assert_return(lldp, -EINVAL);
-+        assert_return(lldp->fd < 0, -EBUSY);
-+
-+        lldp->event = sd_event_unref(lldp->event);
-+        return 0;
-+}
-+
-+_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
-+        assert_return(lldp, NULL);
-+
-+        return lldp->event;
-+}
-+
-+_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
-+        assert_return(lldp, -EINVAL);
-+
-+        lldp->callback = cb;
-+        lldp->userdata = userdata;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
-+        assert_return(lldp, -EINVAL);
-+        assert_return(ifindex > 0, -EINVAL);
-+        assert_return(lldp->fd < 0, -EBUSY);
-+
-+        lldp->ifindex = ifindex;
-+        return 0;
-+}
-+
-+static sd_lldp* lldp_free(sd_lldp *lldp) {
-+        assert(lldp);
-+
-+        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
-+
-+        lldp_reset(lldp);
-+        sd_lldp_detach_event(lldp);
-+        lldp_flush_neighbors(lldp);
-+
-+        hashmap_free(lldp->neighbor_by_id);
-+        prioq_free(lldp->neighbor_by_expiry);
-+        return mfree(lldp);
-+}
-+
-+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
-+
-+_public_ int sd_lldp_new(sd_lldp **ret) {
-+        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
-+        int r;
-+
-+        assert_return(ret, -EINVAL);
-+
-+        lldp = new(sd_lldp, 1);
-+        if (!lldp)
-+                return -ENOMEM;
-+
-+        *lldp = (sd_lldp) {
-+                .n_ref = 1,
-+                .fd = -1,
-+                .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
-+                .capability_mask = (uint16_t) -1,
-+        };
-+
-+        lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
-+        if (!lldp->neighbor_by_id)
-+                return -ENOMEM;
-+
-+        r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
-+        if (r < 0)
-+                return r;
-+
-+        *ret = TAKE_PTR(lldp);
-+
-+        return 0;
-+}
-+
-+static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
-+        return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
-+}
-+
-+static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
-+        sd_lldp *lldp = userdata;
-+        int r;
-+
-+        r = lldp_make_space(lldp, 0);
-+        if (r < 0)
-+                return log_lldp_errno(r, "Failed to make space: %m");
-+
-+        r = lldp_start_timer(lldp, NULL);
-+        if (r < 0)
-+                return log_lldp_errno(r, "Failed to restart timer: %m");
-+
-+        return 0;
-+}
-+
-+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
-+        sd_lldp_neighbor *n;
-+
-+        assert(lldp);
-+
-+        if (neighbor)
-+                lldp_neighbor_start_ttl(neighbor);
-+
-+        n = prioq_peek(lldp->neighbor_by_expiry);
-+        if (!n)
-+                return event_source_disable(lldp->timer_event_source);
-+
-+        if (!lldp->event)
-+                return 0;
-+
-+        return event_reset_time(lldp->event, &lldp->timer_event_source,
-+                                clock_boottime_or_monotonic(),
-+                                n->until, 0,
-+                                on_timer_event, lldp,
-+                                lldp->event_priority, "lldp-timer", true);
-+}
-+
-+_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
-+        sd_lldp_neighbor **l = NULL, *n;
-+        Iterator i;
-+        int k = 0, r;
-+
-+        assert_return(lldp, -EINVAL);
-+        assert_return(ret, -EINVAL);
-+
-+        if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
-+                *ret = NULL;
-+                return 0;
-+        }
-+
-+        l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
-+        if (!l)
-+                return -ENOMEM;
-+
-+        r = lldp_start_timer(lldp, NULL);
-+        if (r < 0) {
-+                free(l);
-+                return r;
-+        }
-+
-+        HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
-+                l[k++] = sd_lldp_neighbor_ref(n);
-+
-+        assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
-+
-+        /* Return things in a stable order */
-+        typesafe_qsort(l, k, neighbor_compare_func);
-+        *ret = l;
-+
-+        return k;
-+}
-+
-+_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
-+        assert_return(lldp, -EINVAL);
-+        assert_return(m > 0, -EINVAL);
-+
-+        lldp->neighbors_max = m;
-+        lldp_make_space(lldp, 0);
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
-+        assert_return(lldp, -EINVAL);
-+        assert_return(mask != 0, -EINVAL);
-+
-+        lldp->capability_mask = mask;
-+
-+        return 0;
-+}
-+
-+_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
-+        assert_return(lldp, -EINVAL);
-+
-+        /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
-+         * that our own can be filtered out here. */
-+
-+        if (addr)
-+                lldp->filter_address = *addr;
-+        else
-+                zero(lldp->filter_address);
-+
-+        return 0;
-+}
-diff --git a/src/libsystemd/sd-lldp/test-lldp.c b/src/libsystemd/sd-lldp/test-lldp.c
-new file mode 100644
-index 0000000000..a2ac65095f
---- /dev/null
-+++ b/src/libsystemd/sd-lldp/test-lldp.c
-@@ -0,0 +1,378 @@
-+/* SPDX-License-Identifier: LGPL-2.1+ */
-+
-+#include <arpa/inet.h>
-+#include <errno.h>
-+#include <net/ethernet.h>
-+#include <stdio.h>
-+#include <unistd.h>
-+
-+#include "sd-event.h"
-+#include "sd-lldp.h"
-+
-+#include "alloc-util.h"
-+#include "fd-util.h"
-+#include "lldp-network.h"
-+#include "macro.h"
-+#include "string-util.h"
-+#include "tests.h"
-+
-+#define TEST_LLDP_PORT "em1"
-+#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
-+#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
-+
-+static int test_fd[2] = { -1, -1 };
-+static int lldp_handler_calls;
-+
-+int lldp_network_bind_raw_socket(int ifindex) {
-+        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
-+                return -errno;
-+
-+        return test_fd[0];
-+}
-+
-+static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
-+        lldp_handler_calls++;
-+}
-+
-+static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) {
-+        int r;
-+
-+        r = sd_lldp_new(lldp);
-+        if (r < 0)
-+                return r;
-+
-+        r = sd_lldp_set_ifindex(*lldp, 42);
-+        if (r < 0)
-+                return r;
-+
-+        r = sd_lldp_set_callback(*lldp, cb, cb_data);
-+        if (r < 0)
-+                return r;
-+
-+        r = sd_lldp_attach_event(*lldp, e, 0);
-+        if (r < 0)
-+                return r;
-+
-+        r = sd_lldp_start(*lldp);
-+        if (r < 0)
-+                return r;
-+
-+        return 0;
-+}
-+
-+static int stop_lldp(sd_lldp *lldp) {
-+        int r;
-+
-+        r = sd_lldp_stop(lldp);
-+        if (r < 0)
-+                return r;
-+
-+        r = sd_lldp_detach_event(lldp);
-+        if (r < 0)
-+                return r;
-+
-+        sd_lldp_unref(lldp);
-+        safe_close(test_fd[1]);
-+
-+        return 0;
-+}
-+
-+static void test_receive_basic_packet(sd_event *e) {
-+
-+        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: interface name, "1/3" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                /* LLDP optional TLVs */
-+                0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
-+                0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
-+                0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+
-+        sd_lldp *lldp;
-+        sd_lldp_neighbor **neighbors;
-+        uint8_t type;
-+        const void *data;
-+        uint16_t ttl;
-+        size_t length;
-+        const char *str;
-+
-+        lldp_handler_calls = 0;
-+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
-+
-+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
-+        sd_event_run(e, 0);
-+        assert_se(lldp_handler_calls == 1);
-+        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
-+
-+        assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0);
-+        assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
-+        assert_se(length == ETH_ALEN);
-+        assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
-+
-+        assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0);
-+        assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME);
-+        assert_se(length == 3);
-+        assert_se(!memcmp(data, "1/3", 3));
-+
-+        assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0);
-+        assert_se(streq(str, "Port"));
-+
-+        assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0);
-+        assert_se(streq(str, "SYS"));
-+
-+        assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0);
-+        assert_se(streq(str, "foo"));
-+
-+        assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0);
-+        assert_se(ttl == 120);
-+
-+        sd_lldp_neighbor_unref(neighbors[0]);
-+        free(neighbors);
-+
-+        assert_se(stop_lldp(lldp) == 0);
-+}
-+
-+static void test_receive_incomplete_packet(sd_event *e) {
-+        sd_lldp *lldp;
-+        sd_lldp_neighbor **neighbors;
-+        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: interface name, "1/3" */
-+                                                        /* Missing TTL */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+
-+        lldp_handler_calls = 0;
-+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
-+
-+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
-+        sd_event_run(e, 0);
-+        assert_se(lldp_handler_calls == 0);
-+        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0);
-+
-+        assert_se(stop_lldp(lldp) == 0);
-+}
-+
-+static void test_receive_oui_packet(sd_event *e) {
-+        sd_lldp *lldp;
-+        sd_lldp_neighbor **neighbors;
-+        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 */
-+        };
-+
-+        lldp_handler_calls = 0;
-+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
-+
-+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
-+        sd_event_run(e, 0);
-+        assert_se(lldp_handler_calls == 1);
-+        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1);
-+
-+        assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_3, SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0);
-+        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0);
-+
-+        sd_lldp_neighbor_unref(neighbors[0]);
-+        free(neighbors);
-+
-+        assert_se(stop_lldp(lldp) == 0);
-+}
-+
-+static void test_multiple_neighbors_sorted(sd_event *e) {
-+
-+        static const uint8_t frame1[] = {
-+                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
-+                0x04, 0x04, 0x02, '2', '/', '3',        /* Port component: "2/3" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const uint8_t frame2[] = {
-+                /* 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, 0x04, 0x01, '2', '/', '1',        /* Chassis component: "2/1" */
-+                0x04, 0x04, 0x02, '1', '/', '3',        /* Port component: "1/3" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const uint8_t frame3[] = {
-+                /* 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, 0x05, 0x01, '2', '/', '1', '0',   /* Chassis component: "2/10" */
-+                0x04, 0x04, 0x02, '1', '/', '0',        /* Port component: "1/0" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const uint8_t frame4[] = {
-+                /* 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, 0x05, 0x01, '2', '/', '1', '9',   /* Chassis component: "2/19" */
-+                0x04, 0x04, 0x02, '1', '/', '0',        /* Port component: "1/0" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const uint8_t frame5[] = {
-+                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
-+                0x04, 0x05, 0x02, '2', '/', '1', '0',   /* Port component: "2/10" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const uint8_t frame6[] = {
-+                /* 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, 0x04, 0x01, '1', '/', '2',        /* Chassis component: "1/2" */
-+                0x04, 0x05, 0x02, '2', '/', '3', '9',   /* Port component: "2/10" */
-+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-+                0x00, 0x00                              /* End Of LLDPDU */
-+        };
-+        static const char* expected[] = {
-+                /* ordered pairs of Chassis+Port */
-+                "1/2", "2/10",
-+                "1/2", "2/3",
-+                "1/2", "2/39",
-+                "2/1", "1/3",
-+                "2/10", "1/0",
-+                "2/19", "1/0",
-+        };
-+
-+        sd_lldp *lldp;
-+        sd_lldp_neighbor **neighbors;
-+        int i;
-+        uint8_t type;
-+        const void *data;
-+        size_t length, expected_length;
-+        uint16_t ttl;
-+
-+        lldp_handler_calls = 0;
-+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
-+
-+        assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1));
-+        sd_event_run(e, 0);
-+        assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2));
-+        sd_event_run(e, 0);
-+        assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3));
-+        sd_event_run(e, 0);
-+        assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4));
-+        sd_event_run(e, 0);
-+        assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5));
-+        sd_event_run(e, 0);
-+        assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6));
-+        sd_event_run(e, 0);
-+        assert_se(lldp_handler_calls == 6);
-+
-+        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6);
-+
-+        for (i = 0; i < 6; i++) {
-+                assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0);
-+                assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT);
-+                expected_length = strlen(expected[2 * i]);
-+                assert_se(length == expected_length);
-+                assert_se(memcmp(data, expected[2 * i], expected_length) == 0);
-+
-+                assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0);
-+                assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT);
-+                expected_length = strlen(expected[2 * i + 1]);
-+                assert_se(length == expected_length);
-+                assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0);
-+
-+                assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0);
-+                assert_se(ttl == 120);
-+        }
-+
-+        for (i = 0; i < 6; i++)
-+                sd_lldp_neighbor_unref(neighbors[i]);
-+        free(neighbors);
-+
-+        assert_se(stop_lldp(lldp) == 0);
-+}
-+
-+int main(int argc, char *argv[]) {
-+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
-+
-+        test_setup_logging(LOG_DEBUG);
-+
-+        /* LLDP reception tests */
-+        assert_se(sd_event_new(&e) == 0);
-+        test_receive_basic_packet(e);
-+        test_receive_incomplete_packet(e);
-+        test_receive_oui_packet(e);
-+        test_multiple_neighbors_sorted(e);
-+
-+        return 0;
-+}
-diff --git a/src/systemd/meson.build b/src/systemd/meson.build
-index 62baf7784e..090f06087b 100644
---- a/src/systemd/meson.build
-+++ b/src/systemd/meson.build
-@@ -13,6 +13,7 @@ _systemd_headers = '''
-         sd-login.h
-         sd-messages.h
-         sd-path.h
-+        sd-lldp.h
- '''.split()
- 
- # https://github.com/mesonbuild/meson/issues/1633
-@@ -28,7 +29,6 @@ _not_installed_headers = '''
-         sd-dhcp-server.h
-         sd-ipv4acd.h
-         sd-ipv4ll.h
--        sd-lldp.h
-         sd-ndisc.h
-         sd-netlink.h
-         sd-network.h
-diff --git a/src/test/meson.build b/src/test/meson.build
-index 132989f197..1b7d2d6e6d 100644
---- a/src/test/meson.build
-+++ b/src/test/meson.build
-@@ -1037,6 +1037,11 @@ tests += [
-          [],
-          []],
- 
-+        [['src/libsystemd/sd-lldp/test-lldp.c'],
-+         [libsystemd_static,
-+          libshared],
-+         []],
-+
- ]
- 
- if cxx_cmd != ''
-@@ -1122,11 +1127,6 @@ tests += [
-          [libshared,
-           libsystemd_network],
-          []],
--
--        [['src/libsystemd-network/test-lldp.c'],
--         [libshared,
--          libsystemd_network],
--         []],
- ]
- 
- ############################################################
--- 
-2.28.0
-
diff --git a/package/velia/Config.in b/package/velia/Config.in
index f444cc8..4499ba0 100644
--- a/package/velia/Config.in
+++ b/package/velia/Config.in
@@ -8,6 +8,7 @@
 	select BR2_PACKAGE_SYSTEMD
 	select BR2_PACKAGE_SYSREPO
 	select BR2_PACKAGE_SYSREPO_CPP
+	select BR2_PACKAGE_JSON_FOR_MODERN_CPP
 	help
 	  Health tracking for embedded devices running Linux
 
diff --git a/package/velia/velia.mk b/package/velia/velia.mk
index 8a6be0e..ec09817 100644
--- a/package/velia/velia.mk
+++ b/package/velia/velia.mk
@@ -2,7 +2,7 @@
 VELIA_SITE = https://gerrit.cesnet.cz/CzechLight/velia
 VELIA_SITE_METHOD = git
 VELIA_INSTALL_STAGING = NO
-VELIA_DEPENDENCIES = docopt-cpp spdlog boost sdbus-cpp systemd sysrepo libnl
+VELIA_DEPENDENCIES = docopt-cpp spdlog boost sdbus-cpp systemd sysrepo libnl json-for-modern-cpp
 VELIA_LICENSE = Apache-2.0
 VELIA_LICENSE_FILES = LICENSE.md
 
diff --git a/submodules/buildroot b/submodules/buildroot
index e64b486..afc84e6 160000
--- a/submodules/buildroot
+++ b/submodules/buildroot
@@ -1 +1 @@
-Subproject commit e64b486dba345929a08eb2075fdc14864aa4a0ad
+Subproject commit afc84e6a49b47e4ddb848aeaf1bd613d3cec0a40
diff --git a/submodules/netconf-cli b/submodules/netconf-cli
index db937a9..68c8942 160000
--- a/submodules/netconf-cli
+++ b/submodules/netconf-cli
@@ -1 +1 @@
-Subproject commit db937a93ac4fe4e4f9e888f27e248f357bd66213
+Subproject commit 68c8942fba71b60243759dde5aadfbda32e74dd0
diff --git a/submodules/velia b/submodules/velia
index 2041a67..e2b23bb 160000
--- a/submodules/velia
+++ b/submodules/velia
@@ -1 +1 @@
-Subproject commit 2041a67f4bd0e71bebcaf1cad88373709f0f2a4a
+Subproject commit e2b23bbaa3a8228758ec58d8e7a64ee2fe412684