networkctl: add dhcp-lease verb to dump DHCP lease

This shows the DHCP message of an acquired DHCP lease.
Example:
```
$ networkctl dhcp-lease eth0
Header:
              KEY VALUE
   Hardware Type: ETHER
Hardware Address: 41:42:43:31:32:33
  Client Address: 192.0.2.123
  Server Address: 192.0.2.1

Options:
CODE NAME               DATA
   1 subnet mask        255.255.255.0
   3 router             192.0.2.1
   6 domain name server 192.0.2.1
  12 hostname           test-node
  15 domain name        lan
  28 broadcast address  192.0.2.255
  42 NTP server         192.0.2.11
                        192.0.2.12
  51 lease time         1d
  53 message type       5
  54 server identifier  192.0.2.1
  58 renewal time       11h 5min 38s
  59 rebinding time     20h 5min 38s
 119 domain search      hoge.example.com
                        foo.example.com
```
This commit is contained in:
Yu Watanabe
2026-05-17 21:02:20 +09:00
parent 149adb2fdc
commit 6cbf8222ca
8 changed files with 205 additions and 2 deletions
+116
View File
@@ -287,6 +287,122 @@
</varlistentry>
<varlistentry>
<term>
<command>dhcp-lease</command>
<replaceable>INTERFACE</replaceable>
<optional><replaceable>CODE<optional><replaceable>:FORMAT</replaceable></optional></replaceable></optional>
…
</term>
<listitem>
<para>Show the DHCP message of the acquired lease (i.e. the received DHCPACK message). Takes an
interface name and optional DHCP option codes. If no option code is specified, both the DHCP
message header and all options are shown. If one or more option codes are specified, only the
specified options are shown.</para>
<para>When called with the <option>--json=</option> option, the entire DHCP message is shown in
JSON format. In that case, any specified option codes are ignored.</para>
<para>By default, each option is displayed in a human-readable format, depending on the option.
For example, DNS server addresses are shown as a list of IP addresses (e.g.
<literal>192.0.2.1</literal>), and the lease time is shown as a human-readable duration (e.g.
<literal>8h</literal>).</para>
<para>Each option code may optionally be followed by a formatter, separated by a colon (e.g.
<literal>42:address</literal>). This can be useful if the default formatting is not appropriate.
</para>
<para>The following formatters are supported:</para>
<variablelist>
<varlistentry>
<term><option>auto</option></term>
<listitem>
<para>Use the default formatting.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>hex</option></term>
<listitem>
<para>Show the raw bytes in hexadecimal, separated by colons.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>flag</option></term>
<listitem>
<para>Treat the option as having zero-length data and display <literal>yes</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>bool</option></term>
<listitem>
<para>Treat the option as a 1-byte boolean and display <literal>yes</literal> or <literal>no</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>uint8</option></term>
<listitem>
<para>Treat the option as a 1-byte unsigned integer.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>uint16</option></term>
<listitem>
<para>Treat the option as a 2-byte unsigned integer.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>time</option></term>
<listitem>
<para>Treat the option as a 4-byte time value in seconds and display it as a human-readable duration.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>string</option></term>
<listitem>
<para>Treat the option as a string.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>address</option></term>
<listitem>
<para>Treat the option as one or more IPv4 addresses and display them in human-readable form.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Produces output similar to:
<programlisting>$ networkctl dhcp-lease eth0
Header:
KEY VALUE
Hardware Type: ETHER
Hardware Address: 41:42:43:31:32:33
Client Address: 192.0.2.123
Server Address: 192.0.2.1
Options:
CODE NAME DATA
1 subnet mask 255.255.255.0
3 router 192.0.2.1
6 domain name server 192.0.2.1
12 hostname test-node
15 domain name lan
28 broadcast address 192.0.2.255
42 NTP server 192.0.2.11
192.0.2.12
51 lease time 1d
53 message type 5
54 server identifier 192.0.2.1
58 renewal time 11h 5min 38s
59 rebinding time 20h 5min 38s
119 domain search hoge.example.com
foo.example.com</programlisting>
</para>
<xi:include href="version-info.xml" xpointer="v261"/>
</listitem>
</varlistentry>
<varlistentry>
<term>
<command>lldp</command>
+1 -1
View File
@@ -51,7 +51,7 @@ _networkctl() {
local -A VERBS=(
[STANDALONE]='label reload'
[LINKS]='status list lldp delete renew up down forcerenew reconfigure'
[LINKS]='status dhcp-lease list lldp delete renew up down forcerenew reconfigure'
[FILES_OR_LINKS]='edit cat'
[FILES]='mask unmask'
[BOOL]='persistent-storage'
+2 -1
View File
@@ -7,6 +7,7 @@
_networkctl_cmds=(
'list:List existing links'
'status:Show information about the specified links'
'dhcp-lease:Show DHCP lease'
'lldp:Show Link Layer Discovery Protocol status'
'label:Show address labels'
'delete:Delete virtual netdevs'
@@ -26,7 +27,7 @@
local -a _links
cmd="${${_networkctl_cmds[(r)$words[1]:*]%%:*}}"
case $cmd in
(list|status|up|down|cat|edit|lldp|delete|renew|forcerenew|reconfigure)
(list|status|dhcp-lease|up|down|cat|edit|lldp|delete|renew|forcerenew|reconfigure)
for link in ${(f)"$(_call_program links networkctl list --no-legend)"}; do _links+=($link[(w)2]:$link); done
if [[ -n "$_links" ]]; then
_describe -t links 'links' _links $( [[ $cmd == (edit|cat) ]] && print -- -P@ )
+1
View File
@@ -128,6 +128,7 @@ networkctl_sources = files(
'networkctl-address-label.c',
'networkctl-config-file.c',
'networkctl-description.c',
'networkctl-dhcp-lease.c',
'networkctl-dump-util.c',
'networkctl-journal.c',
'networkctl-link-info.c',
+67
View File
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-json.h"
#include "sd-netlink.h"
#include "sd-varlink.h"
#include "dhcp-message-dump.h"
#include "log.h"
#include "networkctl.h"
#include "networkctl-dhcp-lease.h"
#include "networkctl-link-info.h"
#include "networkctl-util.h"
#include "strv.h"
int verb_dhcp_lease(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
/* networkctl dhcp-lease INTERFACE [CODE[:FORMAT] ...] */
assert(argc >= 2);
pager_open(arg_pager_flags);
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
r = varlink_connect_networkd(&vl);
if (r < 0)
return r;
const char *ifname = argv[1];
_cleanup_(link_info_array_freep) LinkInfo *link = NULL;
r = acquire_link_info(vl, rtnl, STRV_MAKE(ifname), &link);
if (r < 0)
return r;
if (r == 0)
return -EINVAL; /* already logged in acquire_link_info(). */
if (r > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Interface name '%s' matches multiple interfaces.", ifname);
if (!link->dhcp_message)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
"Interface '%s' does not have DHCPv4 lease.", link->name);
if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = dhcp_message_build_json(link->dhcp_message, &v);
if (r < 0)
return log_error_errno(r, "Failed to build JSON variant from DHCP message: %m");
r = sd_json_variant_dump(v, arg_json_format_flags, /* f= */ NULL, /* prefix= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to dump JSON variant: %m");
return 0;
}
DumpDHCPMessageFlag flags = 0;
SET_FLAG(flags, DUMP_DHCP_MESSAGE_LEGEND, arg_legend);
SET_FLAG(flags, DUMP_DHCP_MESSAGE_FULL, arg_full);
return dump_dhcp_message(link->dhcp_message, strv_skip(argv, 2), flags);
}
+6
View File
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
int verb_dhcp_lease(int argc, char *argv[], uintptr_t _data, void *userdata);
+3
View File
@@ -12,6 +12,7 @@
#include "networkctl.h"
#include "networkctl-address-label.h"
#include "networkctl-config-file.h"
#include "networkctl-dhcp-lease.h"
#include "networkctl-list.h"
#include "networkctl-lldp.h"
#include "networkctl-misc.h"
@@ -42,6 +43,8 @@ VERB_SCOPE(, verb_list_links, "list", "[PATTERN...
"List links");
VERB_SCOPE(, verb_link_status, "status", "[PATTERN...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY,
"Show link status");
VERB_SCOPE(, verb_dhcp_lease, "dhcp-lease", "INTERFACE [CODE[:FORMAT]...]", 2, VERB_ANY, VERB_ONLINE_ONLY,
"Show DHCP lease");
VERB_SCOPE(, verb_link_lldp_status, "lldp", "[PATTERN...]", VERB_ANY, VERB_ANY, 0,
"Show LLDP neighbors");
VERB_SCOPE(, verb_list_address_labels, "label", NULL, 1, 1, 0,
+9
View File
@@ -51,6 +51,15 @@ logind.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="Kill
logind.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="InhibitDelayMaxSec="]
machine-info.xml ./refsect1[title="Options"]/refsect2[title="Machine Information"]/variablelist/varlistentry[term="PRETTY_HOSTNAME="]
machine-info.xml ./refsect1[title="Options"]/refsect2[title="Machine Information"]/variablelist/varlistentry[term="ICON_NAME="]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="auto"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="hex"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="flag"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="bool"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="uint8"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="uint16"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="time"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="string"]
networkctl.xml ./refsect1[title="Commands"]/variablelist/varlistentry[term="\n dhcp-lease\n INTERFACE\n CODE:FORMAT\n …\n "]/listitem/variablelist/varlistentry[term="address"]
os-release.xml ./refsect1[title="Options"]/refsect2[title="General information identifying the operating system"]/variablelist/varlistentry[term="NAME="]
os-release.xml ./refsect1[title="Options"]/refsect2[title="Information about the version of the operating system"]/variablelist/varlistentry[term="VERSION="]
os-release.xml ./refsect1[title="Options"]/refsect2[title="General information identifying the operating system"]/variablelist/varlistentry[term="ID="]