...
 
Commits (5)
  • Witold Krecicki's avatar
    netmgr: retry binding with IP_FREEBIND when EADDRNOTAVAIL is returned. · a0f7d289
    Witold Krecicki authored
    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.
    a0f7d289
  • Witold Krecicki's avatar
    94eda43a
  • Ondřej Surý's avatar
    Merge branch '2038-use-freebind-when-bind-fails' into 'main' · 8acdccc9
    Ondřej Surý authored
    Resolve "Bind not handling interfaces changes correctly when listen-on-v6  any  specified"
    
    Closes #2038
    
    See merge request !3873
    8acdccc9
  • Michal Nowak's avatar
    Add out-of-tree system test job · f90211f5
    Michal Nowak authored
    In addition to the new out-of-tree system test job, rename the
    variable holding the name of the out-of-tree directory to a more generic
    name.
    f90211f5
  • Michal Nowak's avatar
    Do not remove $systest on out-of-tree builds · dcecbbd1
    Michal Nowak authored
    Previously, $systest directory was removed for out-of-tree builds. For
    directories with compiled content (binaries), this breaks subsequent
    make invocations:
    
        make: Target 'check' not remade because of errors.
        Making all in dyndb/driver
        /bin/bash: line 20: cd: dyndb/driver: No such file or directory
        Making all in dlzexternal/driver
        /bin/bash: line 20: cd: dlzexternal/driver: No such file or directory
    
    Current approach removes only files known to Git and empty directories
    in $systest (including $systest).
    dcecbbd1
...@@ -195,7 +195,7 @@ stages: ...@@ -195,7 +195,7 @@ stages:
stage: build stage: build
before_script: before_script:
- test -w "${CCACHE_DIR}" && export PATH="/usr/lib/ccache:${PATH}" - test -w "${CCACHE_DIR}" && export PATH="/usr/lib/ccache:${PATH}"
- test -n "${OOT_BUILD_WORKSPACE}" && mkdir "${OOT_BUILD_WORKSPACE}" && cd "${OOT_BUILD_WORKSPACE}" - test -n "${OUT_OF_TREE_WORKSPACE}" && mkdir "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
script: script:
- *configure - *configure
- make -j${BUILD_PARALLEL_JOBS:-1} -k all V=1 - make -j${BUILD_PARALLEL_JOBS:-1} -k all V=1
...@@ -253,6 +253,7 @@ stages: ...@@ -253,6 +253,7 @@ stages:
<<: *default_triggering_rules <<: *default_triggering_rules
stage: system stage: system
before_script: before_script:
- test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
- *setup_interfaces - *setup_interfaces
- *setup_softhsm - *setup_softhsm
script: script:
...@@ -481,7 +482,7 @@ docs: ...@@ -481,7 +482,7 @@ docs:
stage: docs stage: docs
before_script: before_script:
- test -w "${CCACHE_DIR}" && export PATH="/usr/lib/ccache:${PATH}" - test -w "${CCACHE_DIR}" && export PATH="/usr/lib/ccache:${PATH}"
- test -n "${OOT_BUILD_WORKSPACE}" && mkdir "${OOT_BUILD_WORKSPACE}" && cd "${OOT_BUILD_WORKSPACE}" - test -n "${OUT_OF_TREE_WORKSPACE}" && mkdir "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
script: script:
- *configure - *configure
- make -j${BUILD_PARALLEL_JOBS:-1} -k doc V=1 - make -j${BUILD_PARALLEL_JOBS:-1} -k doc V=1
...@@ -697,10 +698,19 @@ gcc:out-of-tree: ...@@ -697,10 +698,19 @@ gcc:out-of-tree:
CONFIGURE: ../configure CONFIGURE: ../configure
EXTRA_CONFIGURE: "--enable-dnstap --with-libidn2 --with-lmdb" EXTRA_CONFIGURE: "--enable-dnstap --with-libidn2 --with-lmdb"
RUN_MAKE_INSTALL: 1 RUN_MAKE_INSTALL: 1
OOT_BUILD_WORKSPACE: workspace OUT_OF_TREE_WORKSPACE: workspace
<<: *base_image <<: *base_image
<<: *build_job <<: *build_job
system:gcc:out-of-tree:
variables:
OUT_OF_TREE_WORKSPACE: workspace
needs:
- job: gcc:out-of-tree
artifacts: true
<<: *base_image
<<: *system_test_job
# Jobs for tarball GCC builds on Debian 10 "buster" (amd64) # Jobs for tarball GCC builds on Debian 10 "buster" (amd64)
gcc:tarball: gcc:tarball:
......
5481. [placeholder] 5481. [bug] BIND 9 would fail to bind to IPv6 addresses in a
tentative state when a new IPv6 address was added to the
system, but the Duplicate Address Detection (DAD)
mechanism had not yet finished. [GL #2038]
5480. [placeholder] 5480. [placeholder]
......
...@@ -294,21 +294,27 @@ elif [ "$sanitizer_summaries" -ne 0 ]; then ...@@ -294,21 +294,27 @@ elif [ "$sanitizer_summaries" -ne 0 ]; then
echoinfo "I:$systest:$sanitizer_summaries sanitizer report(s) found" echoinfo "I:$systest:$sanitizer_summaries sanitizer report(s) found"
fi fi
print_outstanding_files() {
if test -d ${srcdir}/../../../.git; then
git status -su --ignored "${systest}" 2>/dev/null | \
sed -n -e 's|^?? \(.*\)|\1|p' \
-e 's|^!! \(.*/named.run\)$|\1|p' \
-e 's|^!! \(.*/named.memstats\)$|\1|p'
fi
}
if [ $status -ne 0 ]; then if [ $status -ne 0 ]; then
echofail "R:$systest:FAIL" echofail "R:$systest:FAIL"
else else
echopass "R:$systest:PASS" echopass "R:$systest:PASS"
if $clean; then if $clean; then
( cd "${systest}" && $SHELL clean.sh "$@" ) ( cd "${systest}" && $SHELL clean.sh "$@" )
if [ "${srcdir}" != "${builddir}" ]; then if [ "${srcdir}" = "${builddir}" ]; then
rm -rf "./${systest}" ## FIXME (this also removes compiled binaries) print_outstanding_files
fi else
if test -d ${srcdir}/../../../.git; then print_outstanding_files | xargs rm -rf
git status -su --ignored "${systest}" 2>/dev/null | \ find "./${systest}" \( -type d -empty -o -name Makefile.in \) -delete 2>/dev/null
sed -n -e 's|^?? \(.*\)|I:file \1 not removed|p' \ fi
-e 's|^!! \(.*/named.run\)$|I:file \1 not removed|p' \
-e 's|^!! \(.*/named.memstats\)$|I:file \1 not removed|p'
fi
fi fi
fi fi
......
...@@ -60,3 +60,12 @@ Bug Fixes ...@@ -60,3 +60,12 @@ Bug Fixes
- The introduction of KASP support broke whether the second field - The introduction of KASP support broke whether the second field
of sig-validity-interval was treated as days or hours. (Thanks to of sig-validity-interval was treated as days or hours. (Thanks to
Tony Finch.) [GL !3735] Tony Finch.) [GL !3735]
- The IPv6 Duplicate Address Detection (DAD) mechanism could cause the operating
system to report the new IPv6 addresses to the applications via the
getifaddrs() API in a tentative (DAD not yet finished) or duplicate (DAD
failed) state. Such addresses cannot be bound by an application, and named
failed to listen on IPv6 addresses after the DAD mechanism finished. It is
possible to work around the issue by setting the IP_FREEBIND option on the
socket and trying to bind() to the IPv6 address again if the first bind() call
fails with EADDRNOTAVAIL. [GL #2038]
...@@ -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]);
} }
......