Commit a0f7d289 authored by Witold Krecicki's avatar Witold Krecicki Committed by Ondřej Surý

netmgr: retry binding with IP_FREEBIND when EADDRNOTAVAIL is returned.

When a new IPv6 interface/address appears it's first in a tentative
state - in which we cannot bind to it, yet it's already being reported
by the route socket. Because of that BIND9 is unable to listen on any
newly detected IPv6 addresses. Fix it by setting IP_FREEBIND option (or
equivalent option on other OSes) and then retrying bind() call.
parent 2ac7748d
...@@ -821,3 +821,9 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid); ...@@ -821,3 +821,9 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid);
/*%< /*%<
* Decrement socket-related statistics counters. * 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) { ...@@ -1519,3 +1519,54 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid) {
isc_stats_decrement(mgr->stats, 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) { ...@@ -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, r = uv_tcp_bind(&sock->uv_handle.tcp, &sock->iface->addr.type.sa,
flags); 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) { if (r != 0) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]); isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
uv_close(&sock->uv_handle.handle, tcp_close_cb); 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) { ...@@ -168,6 +168,20 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
r = uv_udp_bind(&sock->uv_handle.udp, r = uv_udp_bind(&sock->uv_handle.udp,
&sock->parent->iface->addr.type.sa, uv_bind_flags); &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) { if (r < 0) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]); 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