Ryan Wilson a2fc29
From 11d47b91b0fd35aaf2db486783d80bdca7229d82 Mon Sep 17 00:00:00 2001
Ryan Wilson a2fc29
From: Ryan Wilson <ryantimwilson@meta4.com>
Ryan Wilson a2fc29
Date: Wed, 4 Dec 2024 16:15:30 -0800
Ryan Wilson a2fc29
Subject: [PATCH] networkctl: Make lldp/status backwards compatible with 255
Ryan Wilson a2fc29
 over dbus
Ryan Wilson a2fc29
Ryan Wilson a2fc29
---
Ryan Wilson a2fc29
 src/libsystemd-network/lldp-neighbor.c |  22 +++
Ryan Wilson a2fc29
 src/network/networkctl.c               | 208 ++++++++++++++++++++++++-
Ryan Wilson a2fc29
 src/systemd/sd-lldp-rx.h               |   1 +
Ryan Wilson a2fc29
 3 files changed, 224 insertions(+), 7 deletions(-)
Ryan Wilson a2fc29
Ryan Wilson a2fc29
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
Ryan Wilson a2fc29
index a4384ac2e1..02af2954ae 100644
Ryan Wilson a2fc29
--- a/src/libsystemd-network/lldp-neighbor.c
Ryan Wilson a2fc29
+++ b/src/libsystemd-network/lldp-neighbor.c
Ryan Wilson a2fc29
@@ -629,6 +629,28 @@ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret
Ryan Wilson a2fc29
         return 0;
Ryan Wilson a2fc29
 }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
Ryan Wilson a2fc29
+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
Ryan Wilson a2fc29
+        int r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        assert_return(ret, -EINVAL);
Ryan Wilson a2fc29
+        assert_return(raw || raw_size <= 0, -EINVAL);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        n = lldp_neighbor_new(raw_size);
Ryan Wilson a2fc29
+        if (!n)
Ryan Wilson a2fc29
+                return -ENOMEM;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        memcpy_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        r = lldp_neighbor_parse(n);
Ryan Wilson a2fc29
+        if (r < 0)
Ryan Wilson a2fc29
+                return r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        *ret = TAKE_PTR(n);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        return r;
Ryan Wilson a2fc29
+}
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
 int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
Ryan Wilson a2fc29
         assert_return(n, -EINVAL);
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
Ryan Wilson a2fc29
index a447c39a64..e31018e813 100644
Ryan Wilson a2fc29
--- a/src/network/networkctl.c
Ryan Wilson a2fc29
+++ b/src/network/networkctl.c
Ryan Wilson a2fc29
@@ -16,6 +16,7 @@
Ryan Wilson a2fc29
 #include "sd-device.h"
Ryan Wilson a2fc29
 #include "sd-dhcp-client.h"
Ryan Wilson a2fc29
 #include "sd-hwdb.h"
Ryan Wilson a2fc29
+#include "sd-lldp-rx.h"
Ryan Wilson a2fc29
 #include "sd-netlink.h"
Ryan Wilson a2fc29
 #include "sd-network.h"
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
@@ -173,7 +174,7 @@ int acquire_bus(sd_bus **ret) {
Ryan Wilson a2fc29
         if (networkd_is_running()) {
Ryan Wilson a2fc29
                 r = varlink_connect_networkd(/* ret_varlink = */ NULL);
Ryan Wilson a2fc29
                 if (r < 0)
Ryan Wilson a2fc29
-                        return r;
Ryan Wilson a2fc29
+                        log_warning("Varlink connection failed, fallback to D-Bus.");
Ryan Wilson a2fc29
         } else
Ryan Wilson a2fc29
                 log_warning("systemd-networkd is not running, output might be incomplete.");
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
@@ -1410,6 +1411,99 @@ static int dump_lldp_neighbors(Varlink *vl, Table *table, int ifindex) {
Ryan Wilson a2fc29
         return dump_list(table, "Connected To", buf);
Ryan Wilson a2fc29
 }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
+static int open_lldp_neighbors_legacy(int ifindex, FILE **ret) {
Ryan Wilson a2fc29
+        _cleanup_fclose_ FILE *f = NULL;
Ryan Wilson a2fc29
+        char p[STRLEN("/run/systemd/netif/lldp/") + DECIMAL_STR_MAX(int)];
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        assert(ifindex >= 0);
Ryan Wilson a2fc29
+        assert(ret);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        xsprintf(p, "/run/systemd/netif/lldp/%i", ifindex);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        f = fopen(p, "re");
Ryan Wilson a2fc29
+        if (!f)
Ryan Wilson a2fc29
+                return -errno;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        *ret = TAKE_PTR(f);
Ryan Wilson a2fc29
+        return 0;
Ryan Wilson a2fc29
+}
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+static int next_lldp_neighbor_legacy(FILE *f, sd_lldp_neighbor **ret) {
Ryan Wilson a2fc29
+        _cleanup_free_ void *raw = NULL;
Ryan Wilson a2fc29
+        size_t l;
Ryan Wilson a2fc29
+        le64_t u;
Ryan Wilson a2fc29
+        int r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        assert(f);
Ryan Wilson a2fc29
+        assert(ret);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        l = fread(&u, 1, sizeof(u), f);
Ryan Wilson a2fc29
+        if (l == 0 && feof(f))
Ryan Wilson a2fc29
+                return 0;
Ryan Wilson a2fc29
+        if (l != sizeof(u))
Ryan Wilson a2fc29
+                return -EBADMSG;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        /* each LLDP packet is at most MTU size, but let's allow up to 4KiB just in case */
Ryan Wilson a2fc29
+        if (le64toh(u) >= 4096)
Ryan Wilson a2fc29
+                return -EBADMSG;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        raw = new(uint8_t, le64toh(u));
Ryan Wilson a2fc29
+        if (!raw)
Ryan Wilson a2fc29
+                return -ENOMEM;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        if (fread(raw, 1, le64toh(u), f) != le64toh(u))
Ryan Wilson a2fc29
+                return -EBADMSG;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u));
Ryan Wilson a2fc29
+        if (r < 0)
Ryan Wilson a2fc29
+                return r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        return 1;
Ryan Wilson a2fc29
+}
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+static int dump_lldp_neighbors_legacy(Table *table, const char *prefix, int ifindex) {
Ryan Wilson a2fc29
+        _cleanup_strv_free_ char **buf = NULL;
Ryan Wilson a2fc29
+        _cleanup_fclose_ FILE *f = NULL;
Ryan Wilson a2fc29
+        int r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        assert(table);
Ryan Wilson a2fc29
+        assert(prefix);
Ryan Wilson a2fc29
+        assert(ifindex > 0);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        r = open_lldp_neighbors_legacy(ifindex, &f);
Ryan Wilson a2fc29
+        if (r == -ENOENT)
Ryan Wilson a2fc29
+                return 0;
Ryan Wilson a2fc29
+        if (r < 0)
Ryan Wilson a2fc29
+                return r;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        for (;;) {
Ryan Wilson a2fc29
+                const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
Ryan Wilson a2fc29
+                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                r = next_lldp_neighbor_legacy(f, &n);
Ryan Wilson a2fc29
+                if (r < 0)
Ryan Wilson a2fc29
+                        return r;
Ryan Wilson a2fc29
+                if (r == 0)
Ryan Wilson a2fc29
+                        break;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                (void) sd_lldp_neighbor_get_system_name(n, &system_name);
Ryan Wilson a2fc29
+                (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
Ryan Wilson a2fc29
+                (void) sd_lldp_neighbor_get_port_description(n, &port_description);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                r = strv_extendf(&buf, "%s on port %s%s%s%s",
Ryan Wilson a2fc29
+                                 strna(system_name),
Ryan Wilson a2fc29
+                                 strna(port_id),
Ryan Wilson a2fc29
+                                 isempty(port_description) ? "" : " (",
Ryan Wilson a2fc29
+                                 strempty(port_description),
Ryan Wilson a2fc29
+                                 isempty(port_description) ? "" : ")");
Ryan Wilson a2fc29
+                if (r < 0)
Ryan Wilson a2fc29
+                        return log_oom();
Ryan Wilson a2fc29
+        }
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        return dump_list(table, prefix, buf);
Ryan Wilson a2fc29
+}
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
 static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
Ryan Wilson a2fc29
         _cleanup_strv_free_ char **buf = NULL;
Ryan Wilson a2fc29
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Ryan Wilson a2fc29
@@ -1696,7 +1790,6 @@ static int link_status_one(
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         assert(bus);
Ryan Wilson a2fc29
         assert(rtnl);
Ryan Wilson a2fc29
-        assert(vl);
Ryan Wilson a2fc29
         assert(info);
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         (void) sd_network_link_get_operational_state(info->ifindex, &operational_state);
Ryan Wilson a2fc29
@@ -2315,7 +2408,7 @@ static int link_status_one(
Ryan Wilson a2fc29
                         return table_log_add_error(r);
Ryan Wilson a2fc29
         }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
-        r = dump_lldp_neighbors(vl, table, info->ifindex);
Ryan Wilson a2fc29
+        r = vl ? dump_lldp_neighbors(vl, table, info->ifindex) : dump_lldp_neighbors_legacy(table, "Connected To", info->ifindex);
Ryan Wilson a2fc29
         if (r < 0)
Ryan Wilson a2fc29
                 return r;
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
@@ -2449,8 +2542,10 @@ static int link_status(int argc, char *argv[], void *userdata) {
Ryan Wilson a2fc29
                 log_debug_errno(r, "Failed to open hardware database: %m");
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         r = varlink_connect_networkd(&vl);
Ryan Wilson a2fc29
-        if (r < 0)
Ryan Wilson a2fc29
-                return r;
Ryan Wilson a2fc29
+        if (r < 0) {
Ryan Wilson a2fc29
+                log_warning("Varlink connection failed, fallback to D-Bus.");
Ryan Wilson a2fc29
+                vl = NULL;
Ryan Wilson a2fc29
+        }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         if (arg_all)
Ryan Wilson a2fc29
                 c = acquire_link_info(bus, rtnl, NULL, &links);
Ryan Wilson a2fc29
@@ -2584,6 +2679,103 @@ static int dump_lldp_neighbors_json(JsonVariant *reply, char * const *patterns)
Ryan Wilson a2fc29
         return json_variant_dump(v, arg_json_format_flags, NULL, NULL);
Ryan Wilson a2fc29
 }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
+static int link_lldp_status_legacy(int argc, char *argv[], void *userdata) {
Ryan Wilson a2fc29
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
Ryan Wilson a2fc29
+        _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
Ryan Wilson a2fc29
+        _cleanup_(table_unrefp) Table *table = NULL;
Ryan Wilson a2fc29
+        int r, c, m = 0;
Ryan Wilson a2fc29
+        uint16_t all = 0;
Ryan Wilson a2fc29
+        TableCell *cell;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        r = sd_netlink_open(&rtnl);
Ryan Wilson a2fc29
+        if (r < 0)
Ryan Wilson a2fc29
+                return log_error_errno(r, "Failed to connect to netlink: %m");
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        c = acquire_link_info(NULL, rtnl, argc > 1 ? argv + 1 : NULL, &links);
Ryan Wilson a2fc29
+        if (c < 0)
Ryan Wilson a2fc29
+                return c;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        pager_open(arg_pager_flags);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        table = table_new("link",
Ryan Wilson a2fc29
+                          "chassis-id",
Ryan Wilson a2fc29
+                          "system-name",
Ryan Wilson a2fc29
+                          "caps",
Ryan Wilson a2fc29
+                          "port-id",
Ryan Wilson a2fc29
+                          "port-description");
Ryan Wilson a2fc29
+        if (!table)
Ryan Wilson a2fc29
+                return log_oom();
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        if (arg_full)
Ryan Wilson a2fc29
+                table_set_width(table, 0);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        table_set_header(table, arg_legend);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        assert_se(cell = table_get_cell(table, 0, 3));
Ryan Wilson a2fc29
+        table_set_minimum_width(table, cell, 11);
Ryan Wilson a2fc29
+        table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        FOREACH_ARRAY(link, links, c) {
Ryan Wilson a2fc29
+                _cleanup_fclose_ FILE *f = NULL;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                r = open_lldp_neighbors_legacy(link->ifindex, &f);
Ryan Wilson a2fc29
+                if (r == -ENOENT)
Ryan Wilson a2fc29
+                        continue;
Ryan Wilson a2fc29
+                if (r < 0) {
Ryan Wilson a2fc29
+                        log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", link->ifindex);
Ryan Wilson a2fc29
+                        continue;
Ryan Wilson a2fc29
+                }
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                for (;;) {
Ryan Wilson a2fc29
+                        const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL;
Ryan Wilson a2fc29
+                        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
Ryan Wilson a2fc29
+                        _cleanup_free_ char *capabilities = NULL;
Ryan Wilson a2fc29
+                        uint16_t cc;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                        r = next_lldp_neighbor_legacy(f, &n);
Ryan Wilson a2fc29
+                        if (r < 0) {
Ryan Wilson a2fc29
+                                log_warning_errno(r, "Failed to read neighbor data: %m");
Ryan Wilson a2fc29
+                                break;
Ryan Wilson a2fc29
+                        }
Ryan Wilson a2fc29
+                        if (r == 0)
Ryan Wilson a2fc29
+                                break;
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                        (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
Ryan Wilson a2fc29
+                        (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
Ryan Wilson a2fc29
+                        (void) sd_lldp_neighbor_get_system_name(n, &system_name);
Ryan Wilson a2fc29
+                        (void) sd_lldp_neighbor_get_port_description(n, &port_description);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                        if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) {
Ryan Wilson a2fc29
+                                capabilities = lldp_capabilities_to_string(cc);
Ryan Wilson a2fc29
+                                all |= cc;
Ryan Wilson a2fc29
+                        }
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                        r = table_add_many(table,
Ryan Wilson a2fc29
+                                           TABLE_STRING, link->name,
Ryan Wilson a2fc29
+                                           TABLE_STRING, chassis_id,
Ryan Wilson a2fc29
+                                           TABLE_STRING, system_name,
Ryan Wilson a2fc29
+                                           TABLE_STRING, capabilities,
Ryan Wilson a2fc29
+                                           TABLE_STRING, port_id,
Ryan Wilson a2fc29
+                                           TABLE_STRING, port_description);
Ryan Wilson a2fc29
+                        if (r < 0)
Ryan Wilson a2fc29
+                                return table_log_add_error(r);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+                        m++;
Ryan Wilson a2fc29
+                }
Ryan Wilson a2fc29
+        }
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        r = table_print(table, NULL);
Ryan Wilson a2fc29
+        if (r < 0)
Ryan Wilson a2fc29
+                return table_log_print_error(r);
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        if (arg_legend) {
Ryan Wilson a2fc29
+                lldp_capabilities_legend(all);
Ryan Wilson a2fc29
+                printf("\n%i neighbors listed.\n", m);
Ryan Wilson a2fc29
+        }
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
+        return 0;
Ryan Wilson a2fc29
+}
Ryan Wilson a2fc29
+
Ryan Wilson a2fc29
 static int link_lldp_status(int argc, char *argv[], void *userdata) {
Ryan Wilson a2fc29
         _cleanup_(varlink_flush_close_unrefp) Varlink *vl = NULL;
Ryan Wilson a2fc29
         _cleanup_(table_unrefp) Table *table = NULL;
Ryan Wilson a2fc29
@@ -2594,8 +2786,10 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
Ryan Wilson a2fc29
         int r;
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         r = varlink_connect_networkd(&vl);
Ryan Wilson a2fc29
-        if (r < 0)
Ryan Wilson a2fc29
-                return r;
Ryan Wilson a2fc29
+        if (r < 0) {
Ryan Wilson a2fc29
+                log_warning("Varlink connection failed, fallback to D-Bus.");
Ryan Wilson a2fc29
+                return link_lldp_status_legacy(argc, argv, userdata);
Ryan Wilson a2fc29
+        }
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
         r = varlink_call_and_log(vl, "io.systemd.Network.GetLLDPNeighbors", NULL, &reply);
Ryan Wilson a2fc29
         if (r < 0)
Ryan Wilson a2fc29
diff --git a/src/systemd/sd-lldp-rx.h b/src/systemd/sd-lldp-rx.h
Ryan Wilson a2fc29
index a7e1a9f376..154e37e2d8 100644
Ryan Wilson a2fc29
--- a/src/systemd/sd-lldp-rx.h
Ryan Wilson a2fc29
+++ b/src/systemd/sd-lldp-rx.h
Ryan Wilson a2fc29
@@ -88,6 +88,7 @@ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret)
Ryan Wilson a2fc29
 int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
Ryan Wilson a2fc29
 int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
Ryan Wilson a2fc29
 int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
Ryan Wilson a2fc29
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
Ryan Wilson a2fc29
 
Ryan Wilson a2fc29
 /* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
Ryan Wilson a2fc29
  * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
Ryan Wilson a2fc29
-- 
Ryan Wilson a2fc29
2.43.5
Ryan Wilson a2fc29