From c0e40b14ad14bfa2eeed9a8c88176a6381c9e9be Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Apr 2026 07:56:09 +0900 Subject: [PATCH] networkctl: load information about DHCP client from varlink By the previous commit, networkd now exposes the received DHCP message in the Descibe() DBus/Varlink method. Let's make networkctl deserialize the DHCP message and use it where necessary. --- src/network/meson.build | 1 + src/network/networkctl-dump-util.c | 19 +++-- src/network/networkctl-dump-util.h | 4 +- src/network/networkctl-link-info-json.c | 92 +++++++++++++++++++++++++ src/network/networkctl-link-info-json.h | 8 +++ src/network/networkctl-link-info.c | 35 +--------- src/network/networkctl-link-info.h | 6 ++ src/network/networkctl-status-link.c | 38 ++++++---- src/network/networkctl-status-system.c | 2 +- 9 files changed, 150 insertions(+), 55 deletions(-) create mode 100644 src/network/networkctl-link-info-json.c create mode 100644 src/network/networkctl-link-info-json.h diff --git a/src/network/meson.build b/src/network/meson.build index 134416bcc8..7370d011d0 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -131,6 +131,7 @@ networkctl_sources = files( 'networkctl-dump-util.c', 'networkctl-journal.c', 'networkctl-link-info.c', + 'networkctl-link-info-json.c', 'networkctl-list.c', 'networkctl-lldp.c', 'networkctl-misc.c', diff --git a/src/network/networkctl-dump-util.c b/src/network/networkctl-dump-util.c index 20d5e8f43c..15fdb103b6 100644 --- a/src/network/networkctl-dump-util.c +++ b/src/network/networkctl-dump-util.c @@ -5,6 +5,7 @@ #include "sd-netlink.h" #include "alloc-util.h" +#include "dhcp-protocol.h" #include "ether-addr-util.h" #include "format-ifname.h" #include "format-table.h" @@ -210,13 +211,14 @@ int dump_gateways(sd_netlink *rtnl, sd_hwdb *hwdb, Table *table, int ifindex) { int dump_addresses( sd_netlink *rtnl, + sd_dhcp_message *message, sd_dhcp_lease *lease, Table *table, int ifindex) { _cleanup_free_ struct local_address *local_addrs = NULL; _cleanup_strv_free_ char **buf = NULL; - struct in_addr dhcp4_address = {}; + struct in_addr dhcp4_address = {}, server_address = {}; int r, n; assert(rtnl); @@ -226,15 +228,18 @@ int dump_addresses( if (n <= 0) return n; - if (lease) + if (message) { + dhcp4_address.s_addr = message->header.yiaddr; + if (dhcp_message_get_option_address(message, SD_DHCP_OPTION_SERVER_IDENTIFIER, &server_address) < 0) + /* The message should be BOOTP, let's fallback to the siaddr field. */ + server_address.s_addr = message->header.siaddr; + } else if (lease) { (void) sd_dhcp_lease_get_address(lease, &dhcp4_address); + (void) sd_dhcp_lease_get_server_identifier(lease, &server_address); + } FOREACH_ARRAY(local, local_addrs, n) { - struct in_addr server_address; - bool dhcp4 = false; - - if (local->family == AF_INET && in4_addr_equal(&local->address.in, &dhcp4_address)) - dhcp4 = sd_dhcp_lease_get_server_identifier(lease, &server_address) >= 0; + bool dhcp4 = local->family == AF_INET && in4_addr_equal(&local->address.in, &dhcp4_address); r = strv_extendf(&buf, "%s%s%s%s%s%s", IN_ADDR_TO_STRING(local->family, &local->address), diff --git a/src/network/networkctl-dump-util.h b/src/network/networkctl-dump-util.h index cfd0dd3de0..2aafaae475 100644 --- a/src/network/networkctl-dump-util.h +++ b/src/network/networkctl-dump-util.h @@ -3,7 +3,9 @@ #include "shared-forward.h" +#include "dhcp-message.h" + int dump_list(Table *table, const char *key, char * const *l); int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret); int dump_gateways(sd_netlink *rtnl, sd_hwdb *hwdb, Table *table, int ifindex); -int dump_addresses(sd_netlink *rtnl, sd_dhcp_lease *lease, Table *table, int ifindex); +int dump_addresses(sd_netlink *rtnl, sd_dhcp_message *message, sd_dhcp_lease *lease, Table *table, int ifindex); diff --git a/src/network/networkctl-link-info-json.c b/src/network/networkctl-link-info-json.c new file mode 100644 index 0000000000..b187432b80 --- /dev/null +++ b/src/network/networkctl-link-info-json.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-dhcp-client-id.h" +#include "sd-json.h" + +#include "iovec-util.h" +#include "json-util.h" +#include "networkctl-link-info.h" +#include "networkctl-link-info-json.h" +#include "networkctl-util.h" + +static int acquire_link_bitrates(LinkInfo *link) { + int r; + + assert(link); + + 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 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 }, + {} + }; + + 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; +} + +static int acquire_link_dhcp_client(LinkInfo *link) { + int r; + + assert(link); + + sd_json_variant *v; + r = json_variant_find_object(link->description, STRV_MAKE("Interface", "DHCPv4Client", "ClientIdentifier"), &v); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + _cleanup_(iovec_done) struct iovec iov = {}; + r = json_dispatch_byte_array_iovec("ClientIdentifier", v, /* flags= */ 0, &iov); + if (r < 0) + return r; + + return sd_dhcp_client_id_set_raw(&link->dhcp_client_id, iov.iov_base, iov.iov_len); +} + +static int acquire_link_dhcp_message(LinkInfo *link) { + int r; + + assert(link); + + sd_json_variant *v; + r = json_variant_find_object(link->description, STRV_MAKE("Interface", "DHCPv4Client", "Lease", "Message"), &v); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + return dhcp_message_parse_json(v, &link->dhcp_message); +} + +int link_info_parse_description(LinkInfo *link, sd_varlink *vl) { + int r; + + assert(link); + + if (!vl) + return 0; + + r = acquire_link_description(vl, link->ifindex, &link->description); + if (r < 0) + return r; + + (void) acquire_link_bitrates(link); + (void) acquire_link_dhcp_client(link); + (void) acquire_link_dhcp_message(link); + + return 0; +} diff --git a/src/network/networkctl-link-info-json.h b/src/network/networkctl-link-info-json.h new file mode 100644 index 0000000000..2b15e5b01b --- /dev/null +++ b/src/network/networkctl-link-info-json.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "shared-forward.h" + +typedef struct LinkInfo LinkInfo; + +int link_info_parse_description(LinkInfo *link, sd_varlink *vl); diff --git a/src/network/networkctl-link-info.c b/src/network/networkctl-link-info.c index 0b40b44253..789f2a00ee 100644 --- a/src/network/networkctl-link-info.c +++ b/src/network/networkctl-link-info.c @@ -11,7 +11,7 @@ #include "glob-util.h" #include "netlink-util.h" #include "networkctl-link-info.h" -#include "networkctl-util.h" +#include "networkctl-link-info-json.h" #include "sort-util.h" #include "stdio-util.h" #include "string-util.h" @@ -26,6 +26,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); + sd_dhcp_message_unref(array[i].dhcp_message); free(array[i].netdev_kind); free(array[i].ssid); free(array[i].qdisc); @@ -278,34 +279,6 @@ static int decode_link( return 1; } -static int acquire_link_bitrates(LinkInfo *link) { - int r; - - assert(link); - - 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 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 }, - {} - }; - - 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; -} - static void acquire_ether_link_info(int *fd, LinkInfo *link) { assert(fd); assert(link); @@ -398,9 +371,7 @@ int acquire_link_info(sd_varlink *vl, sd_netlink *rtnl, char * const *patterns, 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]); + (void) link_info_parse_description(&links[c], vl); c++; } diff --git a/src/network/networkctl-link-info.h b/src/network/networkctl-link-info.h index ebea57348a..ae1bf71687 100644 --- a/src/network/networkctl-link-info.h +++ b/src/network/networkctl-link-info.h @@ -5,6 +5,8 @@ #include #include +#include "dhcp-client-id-internal.h" +#include "dhcp-message.h" #include "ether-addr-util.h" #include "ethtool-util.h" #include "shared-forward.h" @@ -58,6 +60,10 @@ typedef struct LinkInfo { uint64_t tx_bitrate; uint64_t rx_bitrate; + /* DHCPv4 */ + sd_dhcp_message *dhcp_message; + sd_dhcp_client_id dhcp_client_id; + /* bridge info */ uint32_t forward_delay; uint32_t hello_time; diff --git a/src/network/networkctl-status-link.c b/src/network/networkctl-status-link.c index 15c9c46336..9543953df4 100644 --- a/src/network/networkctl-status-link.c +++ b/src/network/networkctl-status-link.c @@ -3,6 +3,7 @@ #include "sd-device.h" #include "sd-dhcp-client-id.h" #include "sd-dhcp-lease.h" +#include "sd-dhcp-protocol.h" #include "sd-hwdb.h" #include "sd-netlink.h" #include "sd-network.h" @@ -779,7 +780,7 @@ static int link_status_one( return r; } - r = dump_addresses(rtnl, lease, table, info->ifindex); + r = dump_addresses(rtnl, info->dhcp_message, lease, table, info->ifindex); if (r < 0) return r; @@ -837,8 +838,18 @@ static int link_status_one( return table_log_add_error(r); } - if (lease) { - const sd_dhcp_client_id *client_id; + if (info->dhcp_message) { + _cleanup_free_ char *tz = NULL; + + if (dhcp_message_get_option_string(info->dhcp_message, SD_DHCP_OPTION_TZDB_TIMEZONE, &tz) >= 0 + && timezone_is_valid(tz, LOG_DEBUG)) { + r = table_add_many(table, + TABLE_FIELD, "Time Zone", + TABLE_STRING, tz); + if (r < 0) + return table_log_add_error(r); + } + } else if (lease) { const char *tz; r = sd_dhcp_lease_get_timezone(lease, &tz); @@ -849,19 +860,18 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); } + } - r = sd_dhcp_lease_get_client_id(lease, &client_id); + if (sd_dhcp_client_id_is_set(&info->dhcp_client_id)) { + _cleanup_free_ char *id = NULL; + + r = sd_dhcp_client_id_to_string(&info->dhcp_client_id, &id); if (r >= 0) { - _cleanup_free_ char *id = NULL; - - r = sd_dhcp_client_id_to_string(client_id, &id); - if (r >= 0) { - r = table_add_many(table, - TABLE_FIELD, "DHCPv4 Client ID", - TABLE_STRING, id); - if (r < 0) - return table_log_add_error(r); - } + r = table_add_many(table, + TABLE_FIELD, "DHCPv4 Client ID", + TABLE_STRING, id); + if (r < 0) + return table_log_add_error(r); } } diff --git a/src/network/networkctl-status-system.c b/src/network/networkctl-status-system.c index a03004b882..78dd77819d 100644 --- a/src/network/networkctl-status-system.c +++ b/src/network/networkctl-status-system.c @@ -94,7 +94,7 @@ int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { if (r < 0) return table_log_add_error(r); - r = dump_addresses(rtnl, NULL, table, 0); + r = dump_addresses(rtnl, /* message= */ NULL, /* lease= */ NULL, table, /* ifindex= */ 0); if (r < 0) return r;