]> 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 10:44:22 +0000 (12: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.

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

index 277601d5ab8c1f6aaed0fdb38f32a0cea8c8fcd2..b5593a7f59f20b0b87dc0d79ba8c9517599d5946 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 0cdf5b3e9cd749118bdcb1572ab2f0b147957130..30260afb2944bfb5e377aa5ea0de214caba7b239 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 430616355b80266f6adf11f727c3ace56ef6a14d..558b685caf945a23deb28cd24d05ad1b376e9fad 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 0e782f5cdece34623b4f3cb1bb9d26ffcb5c4a38..544a967ae94e871214ee1baa9650d5cb3f3ce1f6 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]);
        }