Merge branch '2038-use-freebind-when-bind-fails-v9_16' into 'v9_16'
Resolve "Bind not handling interfaces changes correctly when listen-on-v6 any specified" See merge request isc-projects/bind9!3907
This commit is contained in:
5
CHANGES
5
CHANGES
@@ -1,3 +1,8 @@
|
||||
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]
|
||||
|
||||
5477. [bug] The idle timeout for connected TCP sockets is now
|
||||
derived from the client query processing timeout
|
||||
configured for a resolver. [GL #2024]
|
||||
|
||||
@@ -42,3 +42,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]
|
||||
|
||||
@@ -793,3 +793,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
|
||||
*/
|
||||
|
||||
@@ -1474,3 +1474,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);
|
||||
}
|
||||
|
||||
@@ -249,6 +249,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);
|
||||
|
||||
@@ -167,6 +167,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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user