diff --git a/subsys/net/lib/dhcpv4/Kconfig b/subsys/net/lib/dhcpv4/Kconfig index a1e8b6cf39e..bf9b6a9fbdd 100644 --- a/subsys/net/lib/dhcpv4/Kconfig +++ b/subsys/net/lib/dhcpv4/Kconfig @@ -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 diff --git a/subsys/net/lib/dhcpv4/dhcpv4_server.c b/subsys/net/lib/dhcpv4/dhcpv4_server.c index f8a7e56f64a..4b232323a78 100644 --- a/subsys/net/lib/dhcpv4/dhcpv4_server.c +++ b/subsys/net/lib/dhcpv4/dhcpv4_server.c @@ -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);