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 <[email protected]>
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 <[email protected]>
---
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;
+}