]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
netmgr: retry binding with IP_FREEBIND when EADDRNOTAVAIL is returned.
authorWitold Kręcicki <wpk@isc.org>
Tue, 21 Jul 2020 11:29:14 +0000 (13:29 +0200)
committerOndřej Surý <ondrej@isc.org>
Thu, 1 Oct 2020 14:44:43 +0000 (16:44 +0200)
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.

(cherry picked from commit a0f7d28967f95c904614bbe44efde789e430a80e)

lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/tcp.c
lib/isc/netmgr/udp.c

index 8f18f1823236860a0daba364047da82bb6ee8b30..d2cd5836e9154f6c9edc3c3e1e83ef01ce76fb95 100644 (file)
@@ -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
+ */
index aef7aa145b1c85f63b566132cb3a515fe0b12a20..c2f95ccbf9582707e3d23dead715ccf54e86da75 100644 (file)
@@ -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);
+}
index 01387527df5e335fae72676911b089027fa82915..9542bd16dae63a4a66db352f67b10243b573465d 100644 (file)
@@ -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);
index 9028c9ae34abf3dd0ee4c075e06098663ff2b1de..851e1f72b658b13321148c2d5c0bbde518c3b321 100644 (file)
@@ -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]);
        }