]> 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>
Fri, 31 Jul 2020 11:33:06 +0000 (13:33 +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 cb3da7fc71f2ea0c5a42a2abc83a8bbb6820932a..5876bba328c9bbe9e36270b9af035839ccda26e0 100644 (file)
@@ -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
+ */
index 8ac42822c2385e86d037b88af6ee9a260ff7b622..a40af49ba682f07b451657ac20a2c17eae9779ff 100644 (file)
@@ -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);
+}
index c572777662dd66e040b3f161d8baab8aa0a6de2d..7e9644e4ff61f09da8edd79bf899fa1d7c17c4b7 100644 (file)
@@ -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);
index 6e2d2098cfd1f60866f9b398ee77027861934cbe..0d130453ff2f95f1683fc17c05a7de6ef6431eb4 100644 (file)
@@ -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]);
        }