Commit b2016adf authored by Witold Krecicki's avatar Witold Krecicki

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 f27c0c32
......@@ -49,6 +49,21 @@
#define ISC_NETMGR_RECVBUF_SIZE (65536)
#endif
/*
* setsockopt() option that lets us bind to a non-existing interface,
* useful for not fully initialized (tentative) IPv6 interfaces which
* are up, but not yet bindable.
*/
#undef FREEBIND_OPT
#if defined(IP_FREEBIND)
#define FREEBIND_OPT IP_FREEBIND
#elif defined(IP_BINDANY)
#define FREEBIND_OPT IP_BINDANY
#elif defined(SO_BINDANY)
#define FREEBIND_OPT SO_BINDANY
#endif
/*
* Single network event loop worker.
*/
......
......@@ -340,6 +340,23 @@ 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);
#ifdef FREEBIND_OPT
if (r == UV_EADDRNOTAVAIL) {
/*
* Retry binding with IP_FREEBIND (or equivalent option) if
* the address is not available. Helps with IPv6 tentative
* addresses which are reported by the route socket but we're
* not yet able to properly bind to them.
*/
uv_os_fd_t fd;
if (uv_fileno(&sock->uv_handle.handle, &fd) == 0) {
(void)setsockopt(fd, IPPROTO_IP, FREEBIND_OPT,
&(int){ 1 }, sizeof(int));
}
r = uv_tcp_bind(&sock->uv_handle.tcp,
&sock->iface->addr.type.sa, flags);
}
#endif
if (r != 0) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
uv_close(&sock->uv_handle.handle, tcp_close_cb);
......
......@@ -168,6 +168,25 @@ 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);
#ifdef FREEBIND_OPT
if (r == UV_EADDRNOTAVAIL) {
/*
* Retry binding with IP_FREEBIND (or equivalent option) if
* the address is not available. Helps with IPv6 tentative
* addresses which are reported by the route socket but we're
* not yet able to properly bind to them.
*/
uv_os_fd_t fd;
if (uv_fileno(&sock->uv_handle.handle, &fd) == 0) {
(void)setsockopt(fd, IPPROTO_IP, FREEBIND_OPT,
&(int){ 1 }, sizeof(int));
}
r = uv_udp_bind(&sock->uv_handle.udp,
&sock->parent->iface->addr.type.sa,
uv_bind_flags);
}
#endif
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