From dfcf1d6f3c57f9a59e849a48669358a92d768f6d Mon Sep 17 00:00:00 2001 From: Deven Ducommun Date: Tue, 16 Jun 2026 21:27:31 -0700 Subject: [PATCH] listen: set IP_FREEBIND so binding to a not-yet-present address works When uhttpd is configured to listen on a specific address (instead of the 0.0.0.0/[::] wildcard) and starts before the network is fully configured, bind() fails with EADDRNOTAVAIL ("Address not available") and that listener is dropped. If it was the only configured address, uhttpd exits with "No sockets bound, unable to continue" and the admin interface stays unreachable until a manual restart. Set IP_FREEBIND / IPV6_FREEBIND on the socket before bind() so the kernel allows binding to an address that is not assigned to a local interface yet. The socket starts serving as soon as the address is configured, which removes the startup race without relying on the init script's interface-up trigger (which misses DHCP, alias and VLAN addresses that are not statically mapped to an interface). The options are best effort and guarded with #ifdef; a failed setsockopt is logged but not fatal, so kernels without FREEBIND keep the previous behaviour. Signed-off-by: Deven Ducommun --- listen.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/listen.c b/listen.c index c5ba16f..9795ad4 100644 --- a/listen.c +++ b/listen.c @@ -169,6 +169,24 @@ int uh_socket_bind(const char *host, const char *port, bool tls) goto error; } + /* + * Allow binding to an address that is not (yet) assigned to a + * local interface. Without this, starting before the network is + * fully configured makes bind() fail with EADDRNOTAVAIL when a + * specific listen address is used, and uhttpd never recovers. + * Best effort: ignore failures so older kernels keep working. + */ +#if defined(IP_FREEBIND) + if (p->ai_family == AF_INET && + setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &yes, sizeof(yes)) < 0) + perror("setsockopt(IP_FREEBIND)"); +#endif +#if defined(IPV6_FREEBIND) + if (p->ai_family == AF_INET6 && + setsockopt(sock, IPPROTO_IPV6, IPV6_FREEBIND, &yes, sizeof(yes)) < 0) + perror("setsockopt(IPV6_FREEBIND)"); +#endif + /* bind */ if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { perror("bind()");