]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl: use io.systemd.Network.Link.Describe() Varlink method
authornoxiouz <atiurin@proton.me>
Sun, 22 Feb 2026 15:17:37 +0000 (15:17 +0000)
committernoxiouz <atiurin@proton.me>
Wed, 18 Mar 2026 22:55:52 +0000 (22:55 +0000)
This makes networkctl fetch bit-rate statistics and offered DHCP
leases via Link.Describe() method instead of D-Bus.

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
src/network/networkctl-link-info.c
src/network/networkctl-link-info.h
src/network/networkctl-status-link.c
src/network/networkctl-util.c
src/network/networkctl-util.h

index 8f072f834ac707d25edb1b3f84ad3f21d920fcab..05990bffbc83e8f650852f5b6dc86e7c7f7a6d73 100644 (file)
@@ -2,13 +2,10 @@
 
 #include <linux/if_tunnel.h>
 
-#include "sd-bus.h"
 #include "sd-netlink.h"
+#include "sd-json.h"
 
 #include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "bus-error.h"
-#include "bus-util.h"
 #include "device-util.h"
 #include "fd-util.h"
 #include "glob-util.h"
@@ -28,6 +25,7 @@
 LinkInfo* link_info_array_free(LinkInfo *array) {
         for (unsigned i = 0; array && array[i].needs_freeing; i++) {
                 sd_device_unref(array[i].sd_device);
+                sd_json_variant_unref(array[i].description);
                 free(array[i].netdev_kind);
                 free(array[i].ssid);
                 free(array[i].qdisc);
@@ -280,33 +278,31 @@ static int decode_link(
         return 1;
 }
 
-static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int acquire_link_bitrates(LinkInfo *link) {
         int r;
 
-        assert(bus);
         assert(link);
 
-        r = link_get_property(bus, link->ifindex, &error, &reply, "org.freedesktop.network1.Link", "BitRates", "(tt)");
-        if (r < 0) {
-                bool quiet = sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
-                                                            BUS_ERROR_SPEED_METER_INACTIVE);
-
-                return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
-                                      r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
-        }
-
-        r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
+        sd_json_variant *v;
+        r = json_variant_find_object(link->description, STRV_MAKE("Interface", "BitRates"), &v);
+        if (r == -ENODATA)
+                return 0;
         if (r < 0)
-                return bus_log_parse_error(r);
+                return r;
 
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                return bus_log_parse_error(r);
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "TxBitRate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LinkInfo, tx_bitrate), SD_JSON_MANDATORY },
+                { "RxBitRate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LinkInfo, rx_bitrate), SD_JSON_MANDATORY },
+                {}
+        };
 
-        link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
+        r = sd_json_dispatch(v, dispatch_table,
+                             SD_JSON_LOG | SD_JSON_WARNING | SD_JSON_ALLOW_EXTENSIONS,
+                             link);
+        if (r < 0)
+                return r;
 
+        link->has_bitrates = true;
         return 0;
 }
 
@@ -356,7 +352,7 @@ static void acquire_wlan_link_info(LinkInfo *link) {
         link->has_wlan_link_info = r > 0 || k > 0;
 }
 
-int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret) {
+int acquire_link_info(sd_varlink *vl, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
         _cleanup_(link_info_array_freep) LinkInfo *links = NULL;
         _cleanup_free_ bool *matched_patterns = NULL;
@@ -402,6 +398,10 @@ int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, Lin
                 acquire_ether_link_info(&fd, &links[c]);
                 acquire_wlan_link_info(&links[c]);
 
+                if (vl)
+                        (void) acquire_link_description(vl, links[c].ifindex, &links[c].description);
+                (void) acquire_link_bitrates(&links[c]);
+
                 c++;
         }
 
@@ -421,10 +421,6 @@ int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, Lin
 
         typesafe_qsort(links, c, link_info_compare);
 
-        if (bus)
-                FOREACH_ARRAY(link, links, c)
-                        (void) acquire_link_bitrates(bus, link);
-
         *ret = TAKE_PTR(links);
 
         if (patterns && c == 0)
index 268798dc7fa1fd617fe1a36901fa1d09c38dab99..ebea57348a30da0ef26c01c23a81b74ce1b2e9f5 100644 (file)
@@ -35,6 +35,7 @@ typedef struct LinkInfo {
         char name[IFNAMSIZ+1];
         char *netdev_kind;
         sd_device *sd_device;
+        sd_json_variant *description;
         int ifindex;
         unsigned short iftype;
         struct hw_addr_data hw_address;
@@ -133,4 +134,4 @@ typedef struct LinkInfo {
 LinkInfo* link_info_array_free(LinkInfo *array);
 DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
 
-int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret);
+int acquire_link_info(sd_varlink *vl, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret);
index b73fceb91a2005a02dc451a2c278433567f4b46b..f63ee2d4175d7b62fd50bee0f3649910d6a9a906 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include "sd-bus.h"
 #include "sd-device.h"
 #include "sd-dhcp-client-id.h"
 #include "sd-dhcp-lease.h"
@@ -12,8 +11,6 @@
 #include "alloc-util.h"
 #include "bond-util.h"
 #include "bridge-util.h"
-#include "bus-error.h"
-#include "bus-util.h"
 #include "errno-util.h"
 #include "escape.h"
 #include "extract-word.h"
@@ -21,7 +18,9 @@
 #include "format-util.h"
 #include "geneve-util.h"
 #include "glyph-util.h"
+#include "iovec-util.h"
 #include "ipvlan-util.h"
+#include "json-util.h"
 #include "macvlan-util.h"
 #include "netif-util.h"
 #include "network-internal.h"
 #include "time-util.h"
 #include "udev-util.h"
 
-static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
+typedef struct LeaseInfo {
+        const char *address;
+        struct iovec client_id;
+} LeaseInfo;
+
+static void lease_info_done(LeaseInfo *p) {
+        assert(p);
+
+        iovec_done(&p->client_id);
+}
+
+static int dump_dhcp_leases(Table *table, const char *prefix, const LinkInfo *link) {
         _cleanup_strv_free_ char **buf = NULL;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
 
         assert(table);
         assert(prefix);
-        assert(bus);
         assert(link);
 
-        r = link_get_property(bus, link->ifindex, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases", "a(uayayayayt)");
-        if (r < 0) {
-                bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
-
-                log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
-                               r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
+        sd_json_variant *leases;
+        r = json_variant_find_object(link->description, STRV_MAKE("Interface", "DHCPServer", "Leases"), &leases);
+        if (r == -ENODATA)
                 return 0;
-        }
-
-        r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
         if (r < 0)
-                return bus_log_parse_error(r);
-
-        while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
-                _cleanup_free_ char *id = NULL, *ip = NULL;
-                const void *client_id, *addr, *gtw, *hwaddr;
-                size_t client_id_sz, sz;
-                uint64_t expiration;
-                uint32_t family;
-
-                r = sd_bus_message_read(reply, "u", &family);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
-                if (r < 0 || sz != 4)
-                        return bus_log_parse_error(r);
-
-                r = sd_bus_message_read_array(reply, 'y', &gtw, &sz);
-                if (r < 0 || sz != 4)
-                        return bus_log_parse_error(r);
+                return r;
 
-                r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "AddressString", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string,  offsetof(LeaseInfo, address),   SD_JSON_MANDATORY },
+                { "ClientId",      SD_JSON_VARIANT_ARRAY,  json_dispatch_byte_array_iovec, offsetof(LeaseInfo, client_id), 0                 },
+                {}
+        };
 
-                r = sd_bus_message_read_basic(reply, 't', &expiration);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+        sd_json_variant *lease;
+        JSON_VARIANT_ARRAY_FOREACH(lease, leases) {
+                _cleanup_(lease_info_done) LeaseInfo info = {};
+                _cleanup_free_ char *client_id = NULL;
 
-                r = sd_dhcp_client_id_to_string_from_raw(client_id, client_id_sz, &id);
+                r = sd_json_dispatch(lease, dispatch_table, SD_JSON_LOG | SD_JSON_WARNING | SD_JSON_ALLOW_EXTENSIONS, &info);
                 if (r < 0)
-                        return bus_log_parse_error(r);
+                        continue;
 
-                r = in_addr_to_string(family, addr, &ip);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+                if (info.client_id.iov_len > 0)
+                        (void) sd_dhcp_client_id_to_string_from_raw(info.client_id.iov_base, info.client_id.iov_len, &client_id);
 
-                r = strv_extendf(&buf, "%s (to %s)", ip, id);
+                r = strv_extendf(&buf, "%s%s%s%s",
+                                 info.address,
+                                 client_id ? " (to " : "",
+                                 strempty(client_id),
+                                 client_id ? ")" : "");
                 if (r < 0)
                         return log_oom();
-
-                r = sd_bus_message_exit_container(reply);
-                if (r < 0)
-                        return bus_log_parse_error(r);
         }
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                return bus_log_parse_error(r);
 
         if (strv_isempty(buf)) {
                 r = strv_extendf(&buf, "none");
@@ -259,7 +229,6 @@ static int format_config_files(char ***files, const char *main_config) {
 }
 
 static int link_status_one(
-                sd_bus *bus,
                 sd_netlink *rtnl,
                 sd_hwdb *hwdb,
                 sd_varlink *vl,
@@ -276,7 +245,6 @@ static int link_status_one(
         _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
-        assert(bus);
         assert(rtnl);
         assert(vl);
         assert(info);
@@ -919,7 +887,7 @@ static int link_status_one(
         if (r < 0)
                 return r;
 
-        r = dump_dhcp_leases(table, "Offered DHCP leases", bus, info);
+        r = dump_dhcp_leases(table, "Offered DHCP leases", info);
         if (r < 0)
                 return r;
 
@@ -940,7 +908,6 @@ static int link_status_one(
 }
 
 int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
         _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
@@ -951,10 +918,6 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
         if (r != 0)
                 return r;
 
-        r = acquire_bus(&bus);
-        if (r < 0)
-                return r;
-
         pager_open(arg_pager_flags);
 
         r = sd_netlink_open(&rtnl);
@@ -970,11 +933,11 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
                 return r;
 
         if (arg_all)
-                c = acquire_link_info(bus, rtnl, NULL, &links);
+                c = acquire_link_info(vl, rtnl, NULL, &links);
         else if (argc <= 1)
                 return system_status(rtnl, hwdb);
         else
-                c = acquire_link_info(bus, rtnl, argv + 1, &links);
+                c = acquire_link_info(vl, rtnl, argv + 1, &links);
         if (c < 0)
                 return c;
 
@@ -985,7 +948,7 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
                 if (!first)
                         putchar('\n');
 
-                RET_GATHER(r, link_status_one(bus, rtnl, hwdb, vl, i));
+                RET_GATHER(r, link_status_one(rtnl, hwdb, vl, i));
 
                 first = false;
         }
index b180dee0b6f77935e184b9a264a06d648b5d838c..6c1db351aef9ee86662fdac5b011f3a2f06dc398 100644 (file)
@@ -232,3 +232,40 @@ void online_state_to_color(const char *state, const char **on, const char **off)
                         *off = "";
         }
 }
+
+int acquire_link_description(sd_varlink *vl, int ifindex, sd_json_variant **ret) {
+        int r;
+
+        assert(vl);
+        assert(ifindex > 0);
+        assert(ret);
+
+        sd_json_variant *v; /* borrowed from vl, do not unref */
+        r = varlink_callbo_and_log(
+                        vl,
+                        "io.systemd.Network.Link.Describe",
+                        &v,
+                        SD_JSON_BUILD_PAIR_INTEGER("InterfaceIndex", ifindex));
+        if (r < 0)
+                return r;
+
+        *ret = sd_json_variant_ref(v);
+        return 0;
+}
+
+int json_variant_find_object(sd_json_variant *v, char * const *object_names, sd_json_variant **ret) {
+        assert(object_names);
+        assert(ret);
+
+        if (!v || sd_json_variant_is_null(v))
+                return -ENODATA;
+
+        STRV_FOREACH(name, object_names) {
+                v = sd_json_variant_by_key(v, *name);
+                if (!v || sd_json_variant_is_null(v))
+                        return -ENODATA;
+        }
+
+        *ret = v;
+        return 0;
+}
index ee05ef6b104766bef6afea0855cec1c8acff617d..df4ef403472fb28768fd21162527e2538c001b20 100644 (file)
@@ -20,3 +20,6 @@ int link_get_property(
 void operational_state_to_color(const char *name, const char *state, const char **on, const char **off);
 void setup_state_to_color(const char *state, const char **on, const char **off);
 void online_state_to_color(const char *state, const char **on, const char **off);
+
+int acquire_link_description(sd_varlink *vl, int ifindex, sd_json_variant **ret);
+int json_variant_find_object(sd_json_variant *v, char * const *object_names, sd_json_variant **ret);