This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push: new dfeeef41e 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 dfeeef41e is described below commit dfeeef41ed9bfed8951d1d42d9b84b833a4d129d Author: nuttxs <zhaoqing.zh...@sony.com> AuthorDate: Mon Jul 14 13:50:02 2025 +0800 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.zh...@sony.com> --- include/netutils/dhcpc.h | 1 + netutils/dhcpc/Kconfig | 38 ++++++++++++++++ netutils/dhcpc/dhcpc.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) diff --git a/include/netutils/dhcpc.h b/include/netutils/dhcpc.h index 541c01961..4a5c451bd 100644 --- a/include/netutils/dhcpc.h +++ b/include/netutils/dhcpc.h @@ -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); diff --git a/netutils/dhcpc/Kconfig b/netutils/dhcpc/Kconfig index 1016479a5..009ecaa9b 100644 --- a/netutils/dhcpc/Kconfig +++ b/netutils/dhcpc/Kconfig @@ -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 diff --git a/netutils/dhcpc/dhcpc.c b/netutils/dhcpc/dhcpc.c index 38de90b2e..724894bb9 100644 --- a/netutils/dhcpc/dhcpc.c +++ b/netutils/dhcpc/dhcpc.c @@ -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; +}