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