]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl: add dhcp-lease verb to dump DHCP lease
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 16 Apr 2026 17:58:52 +0000 (02:58 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 17 May 2026 12:02:20 +0000 (21:02 +0900)
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
```

man/networkctl.xml
shell-completion/bash/networkctl
shell-completion/zsh/_networkctl
src/network/meson.build
src/network/networkctl-dhcp-lease.c [new file with mode: 0644]
src/network/networkctl-dhcp-lease.h [new file with mode: 0644]
src/network/networkctl.c
tools/command_ignorelist

index 561eb932386bfa3e8dfa38bf10b5ecc83db1ffa2..b436797999e045a4ceeb12e711516f4677ae682d 100644 (file)
 
       </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>
index c16768079e30e71e4fae828fe4dbe6de12bdcc1d..fe186a49a2e7f27d92b0aa9e665bfc11d8cf2b7f 100644 (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'
index cf072c0fcbba68c4b3d309849f6e46295e500b9e..c44b3469498345a3ae830773d7b53f8b673d7754 100644 (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@ )
index 7370d011d0a22c60af62061bc61e340dd8ef7980..031910249259020ad15a26ba9e64f6c2ede1a426 100644 (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',
diff --git a/src/network/networkctl-dhcp-lease.c b/src/network/networkctl-dhcp-lease.c
new file mode 100644 (file)
index 0000000..465bff0
--- /dev/null
@@ -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);
+}
diff --git a/src/network/networkctl-dhcp-lease.h b/src/network/networkctl-dhcp-lease.h
new file mode 100644 (file)
index 0000000..f068df3
--- /dev/null
@@ -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);
index bdf487bf2e80305c5fae9406da8cdcc2b806ea2d..bf74f10a4a48e0ca046476983e67a44e810e7fbd 100644 (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,
index 548acff8bd03416727084429a66822a612b35b06..cb256d664726a447e2e358f6aff24e8ee441dd89 100644 (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="]