Commit 8acdccc9 authored by Ondřej Surý's avatar Ondřej Surý

Merge branch '2038-use-freebind-when-bind-fails' into 'main'

Resolve "Bind not handling interfaces changes correctly when listen-on-v6  any  specified"

Closes #2038

See merge request !3873
parents 2ac7748d 94eda43a
Pipeline #47984 passed with stages
in 2 minutes
5481. [placeholder]
5481. [bug] BIND 9 would fail to bind to IPv6 addresses in a
tentative state when a new IPv6 address was added to the
system, but the Duplicate Address Detection (DAD)
mechanism had not yet finished. [GL #2038]
5480. [placeholder]
......
......@@ -60,3 +60,12 @@ Bug Fixes
- The introduction of KASP support broke whether the second field
of sig-validity-interval was treated as days or hours. (Thanks to
Tony Finch.) [GL !3735]
- The IPv6 Duplicate Address Detection (DAD) mechanism could cause the operating
system to report the new IPv6 addresses to the applications via the
getifaddrs() API in a tentative (DAD not yet finished) or duplicate (DAD
failed) state. Such addresses cannot be bound by an application, and named
failed to listen on IPv6 addresses after the DAD mechanism finished. It is
possible to work around the issue by setting the IP_FREEBIND option on the
socket and trying to bind() to the IPv6 address again if the first bind() call
fails with EADDRNOTAVAIL. [GL #2038]
......@@ -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
*/
......@@ -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);
}
......@@ -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);
......
......@@ -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]);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment