netifd: dhcp: add restart command for clean lease re-acquire

Add a proto_dhcp_restart() handler that re-acquires the DHCP lease via a
single ubus call, releasing the previous lease and triggering a fresh
DHCPDISCOVER without bouncing the interface.

The re-acquire is implemented by sending SIGHUP to udhcpc, which releases
the current lease (if any) and immediately transitions the state machine
to INIT_SELECTING so the next main-loop iteration sends a fresh
DHCPDISCOVER. A single signal thus expresses 'release this lease and get
a new one' without exiting the client, so upstream watchdogs (e.g. a
DNS-health monitor) can request a clean re-lease without tearing down the
interface.

Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
John Crispin
2026-06-17 10:44:57 +02:00
committed by Felix Fietkau
parent c92ded2f6e
commit b8175a021b
2 changed files with 58 additions and 0 deletions
@@ -10,6 +10,7 @@ init_proto "$@"
proto_dhcp_init_config() {
renew_handler=1
restart_handler=1
proto_config_add_string 'ipaddr:ipaddr'
proto_config_add_string 'hostname:hostname'
@@ -115,6 +116,15 @@ proto_dhcp_renew() {
[ -n "$sigusr1" ] && proto_kill_command "$interface" $sigusr1
}
proto_dhcp_restart() {
local interface="$1"
# SIGHUP asks a patched udhcpc to release the current lease and
# immediately re-enter INIT_SELECTING so a fresh DHCPDISCOVER goes
# out. Requires the matching busybox udhcpc patch.
local sighup="$(kill -l SIGHUP)"
[ -n "$sighup" ] && proto_kill_command "$interface" $sighup
}
proto_dhcp_teardown() {
local interface="$1"
proto_kill_command "$interface"
@@ -0,0 +1,48 @@
From: John Crispin <john@phrozen.org>
Date: Wed, 15 Apr 2026 16:15:38 +0200
Subject: [PATCH] udhcpc: treat SIGHUP as release + rediscover
Add a SIGHUP handler to udhcpc that releases the current lease (if any)
and immediately transitions the state machine to INIT_SELECTING so the
next main-loop iteration sends a fresh DHCPDISCOVER. This lets a single
signal express 'release this lease and get a new one' without exiting
the client, so upstream watchdogs (e.g. a DNS-health monitor) can
request a clean re-lease without tearing down the interface.
Signed-off-by: John Crispin <john@phrozen.org>
---
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1606,6 +1606,22 @@ int udhcpc_main(int argc UNUSED_PARAM, c
/* ^^^ switches to LISTEN_NONE */
timeout = INT_MAX;
continue;
+ case SIGHUP:
+ bb_info_msg("received SIGHUP, restarting DHCP session");
+ /* release the current lease if we have one */
+ if (client_data.state != INIT_SELECTING
+ && client_data.state != RELEASED)
+ perform_release(server_id, requested_ip);
+ /* drop any cached offer, kick the state machine into
+ * INIT_SELECTING so the next iteration sends a fresh
+ * DHCPDISCOVER */
+ change_listen_mode(LISTEN_NONE);
+ server_id = 0;
+ requested_ip = 0;
+ packet_num = 0;
+ client_data.state = INIT_SELECTING;
+ timeout = 0;
+ continue;
case SIGTERM:
bb_info_msg("received %s", "SIGTERM");
goto ret0;
--- a/networking/udhcp/signalpipe.c
+++ b/networking/udhcp/signalpipe.c
@@ -58,6 +58,7 @@ void FAST_FUNC udhcp_sp_setup(void)
bb_signals(0
+ (1 << SIGUSR1)
+ (1 << SIGUSR2)
+ + (1 << SIGHUP)
+ (1 << SIGTERM)
, signal_handler);
}