From eab17f8a3f7fb67f1d0e9e6a985799c7cfdc03d2 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:10:29 +0200 Subject: [PATCH] dhcp: renew at half the lease when the server omits T1 Option 58 is optional, and dhcp_state.renewal is only ever written by parse_opts(), so a server that leaves it out keeps it at zero. The ACK handler then sets dhcp_timer to zero, and the callback treats a zero timer in LEASING as time to renew, so it sends a REQUEST on the next pass. That REQUEST does set a 30 second timeout, but the ACK that answers it comes back through the same path and zeroes the timer again, so the two sides trade REQUEST and ACK as fast as the round trip allows, with the console printing the whole lease each time round. RFC 2131 has the client renew at half the lease when the server does not say, so use that, and fall back to a fixed value if there is no lease either. Both only fill in a value that is missing: a server that asks for a short renewal still gets exactly what it asked for, which a blanket lower bound would have overridden. Not reproduced here, that needs a server configured without the option. --- dhcp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dhcp.c b/dhcp.c index 6cfa373a..bf615976 100644 --- a/dhcp.c +++ b/dhcp.c @@ -332,6 +332,18 @@ void parse_dhcp(void) uip_ipaddr(&uip_hostaddr, dhcp_state.current_ip[0], dhcp_state.current_ip[1], dhcp_state.current_ip[2], dhcp_state.current_ip[3]); uip_ipaddr(&uip_draddr, dhcp_state.router[0], dhcp_state.router[1], dhcp_state.router[2], dhcp_state.router[3]); uip_ipaddr(&uip_netmask, dhcp_state.subnet[0], dhcp_state.subnet[1], dhcp_state.subnet[2], dhcp_state.subnet[3]); + /* T1 is optional. Without it the renewal time stays zero, which + * makes dhcp_timer zero, and the callback then sends a REQUEST on + * every pass through LEASING: client and server trade REQUEST and + * ACK as fast as the round trip allows. RFC 2131 has the client + * renew at half the lease when the server does not say, and if + * there is no lease either, pick something rather than zero. Only + * filled in when absent, so a server that asks for a short renewal + * still gets it. */ + if (!dhcp_state.renewal) + dhcp_state.renewal = dhcp_state.lease / 2; + if (!dhcp_state.renewal) + dhcp_state.renewal = 60; dhcp_state.state = DHCP_LEASING; dhcp_state.ticks = SYS_TICK_HZ; dhcp_state.dhcp_timer = dhcp_state.renewal > 0xffff ? 0xffff : dhcp_state.renewal;