netutils/dhcpc/dhcpc.c:Implement a dedicated interface to send

DHCPRELEASE, According to RFC 2131 section 3.1, DHCPRELEASE is
used by a client to relinquish a network address and cancel any
remaining lease time

Signed-off-by: nuttxs <zhaoqing.zhang@sony.com>
This commit is contained in:
nuttxs 2025-07-14 13:50:02 +08:00 committed by Alan C. Assis
parent 227d3ec35b
commit dfeeef41ed
3 changed files with 150 additions and 0 deletions

View file

@ -78,6 +78,7 @@ FAR void *dhcpc_open(FAR const char *interface,
FAR const void *mac_addr, int mac_len);
int dhcpc_request(FAR void *handle, FAR struct dhcpc_state *presult);
int dhcpc_request_async(FAR void *handle, dhcpc_callback_t callback);
int dhcpc_release(FAR void *handle, FAR struct dhcpc_state *presult);
void dhcpc_cancel(FAR void *handle);
void dhcpc_close(FAR void *handle);

View file

@ -43,4 +43,42 @@ config NETUTILS_DHCPC_BOOTP_FLAGS
being fully configured, e.g. with forward enabled. Then we need
to enable the broadcast flag under these situations.
config NETUTILS_DHCPC_RELEASE_RETRIES
int "DHCP Release send retries"
default 3
---help---
Number of times to retry sending DHCPRELEASE message if sendto() fails.
Default is 3 attempts.
config NETUTILS_DHCPC_RELEASE_TRANSMISSION_DELAY_MS
int "DHCP Release transmission delay (milliseconds)"
default 10
---help---
Delay in milliseconds used for:
1. Between DHCPRELEASE retry attempts when sendto() fails
2. To ensure packet transmission before socket closure (if IP clearing enabled)
Since UDP is connectionless, this delay gives the network stack
time to actually send the packet. Set to 0 to disable delays
(not recommended). Default is 10ms.
config NETUTILS_DHCPC_RELEASE_ENSURE_TRANSMISSION
bool "Ensure DHCP Release message transmission"
default y
---help---
Add a delay after successfully sending DHCPRELEASE message to ensure
the packet is actually transmitted before the function returns.
Since DHCP RELEASE has no ACK response from server, this delay helps
ensure the release message reaches the server before closing the socket.
Uses the same delay value as CONFIG_NETUTILS_DHCPC_RELEASE_TRANSMISSION_DELAY_MS.
config NETUTILS_DHCPC_RELEASE_CLEAR_IP
bool "Clear IP address after DHCP release"
default n
---help---
Clear all network configuration from the interface after sending
DHCPRELEASE message. This includes IP address, subnet mask, default
gateway. RFC 2131 doesn't mandate immediate clearing, so this is
disabled by default to maintain connectivity.
Enable this for scenarios where complete network reset is required.
endif

View file

@ -303,6 +303,16 @@ static int dhcpc_sendmsg(FAR struct dhcpc_state_s *pdhcpc,
serverid = presult->serverid.s_addr;
break;
/* Send RELEASE message to the server to relinquish the lease */
case DHCPRELEASE:
memcpy(pdhcpc->packet.ciaddr, &presult->ipaddr.s_addr, 4);
pend = dhcpc_addserverid(&presult->serverid, pend);
pend = dhcpc_addclientid(pdhcpc->macaddr, pdhcpc->maclen, pend);
serverid = presult->serverid.s_addr;
break;
default:
errno = EINVAL;
return ERROR;
@ -950,3 +960,104 @@ int dhcpc_request_async(FAR void *handle, dhcpc_callback_t callback)
return OK;
}
/****************************************************************************
* Name: dhcpc_release
****************************************************************************/
int dhcpc_release(FAR void *handle, FAR struct dhcpc_state *presult)
{
FAR struct dhcpc_state_s *pdhcpc = (FAR struct dhcpc_state_s *)handle;
int ret;
int retries = 0;
#ifdef CONFIG_NETUTILS_DHCPC_RELEASE_CLEAR_IP
struct in_addr zero_addr;
#endif
if (!handle || !presult)
{
errno = EINVAL;
return ERROR;
}
/* Check that we have valid IP address and server ID to release */
if (presult->ipaddr.s_addr == 0 || presult->serverid.s_addr == 0)
{
errno = EINVAL;
return ERROR;
}
/* Increment transaction ID for the release message */
pdhcpc->xid[3]++;
/* Send DHCPRELEASE message to the server with retry mechanism.
* According to RFC 2131, no response is expected from the server.
*/
for (; ; )
{
ret = dhcpc_sendmsg(pdhcpc, presult, DHCPRELEASE);
if (ret > 0)
{
ninfo("DHCPRELEASE message sent successfully (%d bytes)\n", ret);
break;
}
else
{
retries++;
nerr("Failed send DHCPRELEASE (attempt %d/%d), ret=%d, errno=%d\n",
retries, CONFIG_NETUTILS_DHCPC_RELEASE_RETRIES, ret, errno);
if (retries >= CONFIG_NETUTILS_DHCPC_RELEASE_RETRIES)
{
nerr("ERROR: Failed to send DHCPRELEASE after %d attempts\n",
CONFIG_NETUTILS_DHCPC_RELEASE_RETRIES);
return ERROR;
}
usleep(1000 * CONFIG_NETUTILS_DHCPC_RELEASE_TRANSMISSION_DELAY_MS);
}
}
#ifdef CONFIG_NETUTILS_DHCPC_RELEASE_ENSURE_TRANSMISSION
/* Ensure the DHCPRELEASE packet has time to be transmitted.
* Since DHCP RELEASE has no ACK response and UDP is connectionless,
* we use a delay to give the network stack time to actually send
* the packet before the function returns.
*/
usleep(1000 * CONFIG_NETUTILS_DHCPC_RELEASE_TRANSMISSION_DELAY_MS);
#endif
#ifdef CONFIG_NETUTILS_DHCPC_RELEASE_CLEAR_IP
/* Clear all network configuration that was obtained via DHCP */
zero_addr.s_addr = INADDR_ANY;
ret = netlib_set_ipv4addr(pdhcpc->interface, &zero_addr);
if (ret < 0)
{
nwarn("Warning: Failed clear IP address from interface (errno=%d)\n",
errno);
}
ret = netlib_set_ipv4netmask(pdhcpc->interface, &zero_addr);
if (ret < 0)
{
nwarn("Warning: Failed clear netmask from interface (errno=%d)\n",
errno);
}
ret = netlib_set_dripv4addr(pdhcpc->interface, &zero_addr);
if (ret < 0)
{
nwarn("Warning: Failed clear gateway from interface (errno=%d)\n",
errno);
}
#endif
ninfo("DHCP released successfully\n");
return OK;
}