diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 277601d5ab..b5593a7f59 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -821,3 +821,9 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid); /*%< * Decrement socket-related statistics counters. */ + +isc_result_t +isc__nm_socket_freebind(const uv_handle_t *handle); +/*%< + * Set the IP_FREEBIND (or equivalent) socket option on the uv_handle + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 0cdf5b3e9c..30260afb29 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1519,3 +1519,54 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid) { isc_stats_decrement(mgr->stats, counterid); } } + +#define setsockopt_on(socket, level, name) \ + setsockopt(socket, level, name, &(int){ 1 }, sizeof(int)) + +isc_result_t +isc__nm_socket_freebind(const uv_handle_t *handle) { + /* + * Set the IP_FREEBIND (or equivalent option) on the uv_handle. + */ + isc_result_t result = ISC_R_SUCCESS; + uv_os_fd_t fd; + if (uv_fileno(handle, &fd) != 0) { + return (ISC_R_FAILURE); + } +#ifdef IP_FREEBIND + if (setsockopt_on(fd, IPPROTO_IP, IP_FREEBIND) == -1) { + return (ISC_R_FAILURE); + } +#elif defined(IP_BINDANY) || defined(IPV6_BINDANY) + struct sockaddr_in sockfd; + + if (getsockname(fd, (struct sockaddr *)&sockfd, + &(socklen_t){ sizeof(sockfd) }) == -1) + { + return (ISC_R_FAILURE); + } +#if defined(IP_BINDANY) + if (sockfd.sin_family == AF_INET) { + if (setsockopt_on(fd, IPPROTO_IP, IP_BINDANY) == -1) { + return (ISC_R_FAILURE); + } + } +#endif +#if defined(IPV6_BINDANY) + if (sockfd.sin_family == AF_INET6) { + if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_BINDANY) == -1) { + return (ISC_R_FAILURE); + } + } +#endif +#elif defined(SO_BINDANY) + if (setsockopt_on(fd, SOL_SOCKET, SO_BINDANY) == -1) { + return (ISC_R_FAILURE); + } +#else + UNUSED(handle); + UNUSED(fd); + result = ISC_R_NOTIMPLEMENTED; +#endif + return (result); +} diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 430616355b..558b685caf 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -340,6 +340,19 @@ isc__nm_async_tcplisten(isc__networker_t *worker, isc__netievent_t *ev0) { r = uv_tcp_bind(&sock->uv_handle.tcp, &sock->iface->addr.type.sa, flags); + if (r == UV_EADDRNOTAVAIL && + isc__nm_socket_freebind(&sock->uv_handle.handle) == ISC_R_SUCCESS) + { + /* + * Retry binding with IP_FREEBIND (or equivalent option) if the + * address is not available. This helps with IPv6 tentative + * addresses which are reported by the route socket, although + * named is not yet able to properly bind to them. + */ + r = uv_tcp_bind(&sock->uv_handle.tcp, + &sock->iface->addr.type.sa, flags); + } + if (r != 0) { isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]); uv_close(&sock->uv_handle.handle, tcp_close_cb); diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 0e782f5cde..544a967ae9 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -168,6 +168,20 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) { r = uv_udp_bind(&sock->uv_handle.udp, &sock->parent->iface->addr.type.sa, uv_bind_flags); + if (r == UV_EADDRNOTAVAIL && + isc__nm_socket_freebind(&sock->uv_handle.handle) == ISC_R_SUCCESS) + { + /* + * Retry binding with IP_FREEBIND (or equivalent option) if the + * address is not available. This helps with IPv6 tentative + * addresses which are reported by the route socket, although + * named is not yet able to properly bind to them. + */ + r = uv_udp_bind(&sock->uv_handle.udp, + &sock->parent->iface->addr.type.sa, + uv_bind_flags); + } + if (r < 0) { isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]); }