net: dhcpv4_server: Improve declined addresses management

In case conflict is detected (either due to receiving Decline message or
due to ICMP probe getting reply), the conflicting address becomes
blocked for further use.

Although the RFC is not specific about how long should the address be
blocked, it make sense to implement some fallback mechanisms to reuse
blocked addresses in the server, otherwise, after longer period of
operation, it may run out of usable address.

This commit adds a timeout for declined addresses, so that by default
the address is marked back as "free" after 24 hrs (default lease time).
It also implements a mechanism, which allows to re-use the oldest
declined entry in case the server runs out of fresh addresses to assign.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2024-02-12 09:46:48 +01:00 committed by Fabio Baltieri
parent 9e1eec9ced
commit e28428caae
2 changed files with 43 additions and 2 deletions

View File

@ -110,6 +110,17 @@ config NET_DHCPV4_SERVER_ADDR_LEASE_TIME
The lease time determines when the IPv4 address lease expires if the
client does not renew it.
config NET_DHCPV4_SERVER_ADDR_DECLINE_TIME
int "The time IPv4 addresses remains blocked after conflict detection (seconds)"
default 86400
help
In case IPv4 address becomes blocked (either because of receiving
Decline message or due to ICMP probe detecting conflict), the address
can no longer be assigned. This timeout specifies how long the address
remains in the Declined state.
Note, that the server may try to reuse the oldest declined address in
case it runs out of free addresses to assign.
config NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT
int "Timeout for ICMP probes sent by the server (miliseconds)"
default 1000

View File

@ -33,6 +33,7 @@ LOG_MODULE_REGISTER(net_dhcpv4_server, CONFIG_NET_DHCPV4_SERVER_LOG_LEVEL);
#define ADDRESS_RESERVED_TIMEOUT K_SECONDS(5)
#define ADDRESS_PROBE_TIMEOUT K_MSEC(CONFIG_NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT)
#define ADDRESS_DECLINED_TIMEOUT K_SECONDS(CONFIG_NET_DHCPV4_SERVER_ADDR_DECLINE_TIME)
#if (CONFIG_NET_DHCPV4_SERVER_ICMP_PROBE_TIMEOUT > 0)
#define DHCPV4_SERVER_ICMP_PROBE 1
@ -80,7 +81,8 @@ static void dhcpv4_server_timeout_recalc(struct dhcpv4_server_ctx *ctx)
struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
if (slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
slot->state == DHCPV4_SERVER_ADDR_ALLOCATED) {
slot->state == DHCPV4_SERVER_ADDR_ALLOCATED ||
slot->state == DHCPV4_SERVER_ADDR_DECLINED) {
if (sys_timepoint_cmp(slot->expiry, next) < 0) {
next = slot->expiry;
}
@ -738,6 +740,7 @@ static int echo_reply_handler(struct net_icmp_ctx *icmp_ctx,
net_sprint_ipv4_addr(&probe_ctx->slot->addr));
probe_ctx->slot->state = DHCPV4_SERVER_ADDR_DECLINED;
probe_ctx->slot->expiry = sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
/* Try to find next free address */
for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
@ -933,6 +936,27 @@ static void dhcpv4_handle_discover(struct dhcpv4_server_ctx *ctx,
}
}
/* In case no free address slot was found, as a last resort, try to
* reuse the oldest declined entry, if present.
*/
if (selected == NULL) {
for (int i = 0; i < ARRAY_SIZE(ctx->addr_pool); i++) {
struct dhcpv4_addr_slot *slot = &ctx->addr_pool[i];
if (slot->state != DHCPV4_SERVER_ADDR_DECLINED) {
continue;
}
/* Find first to expire (oldest) entry. */
if ((selected == NULL) ||
(sys_timepoint_cmp(slot->expiry,
selected->expiry) < 0)) {
selected = slot;
probe = true;
}
}
}
if (selected == NULL) {
LOG_ERR("No free address found in address pool");
} else {
@ -1190,7 +1214,8 @@ static void dhcpv4_handle_decline(struct dhcpv4_server_ctx *ctx,
(slot->state == DHCPV4_SERVER_ADDR_RESERVED ||
slot->state == DHCPV4_SERVER_ADDR_ALLOCATED)) {
slot->state = DHCPV4_SERVER_ADDR_DECLINED;
slot->expiry = sys_timepoint_calc(K_FOREVER);
slot->expiry =
sys_timepoint_calc(ADDRESS_DECLINED_TIMEOUT);
dhcpv4_server_timeout_recalc(ctx);
break;
}
@ -1280,6 +1305,11 @@ static void dhcpv4_server_timeout(struct k_work *work)
slot->state = DHCPV4_SERVER_ADDR_FREE;
}
}
if (slot->state == DHCPV4_SERVER_ADDR_DECLINED &&
sys_timepoint_expired(slot->expiry)) {
slot->state = DHCPV4_SERVER_ADDR_FREE;
}
}
dhcpv4_server_timeout_recalc(ctx);