...
 
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:
stage: build
before_script:
- 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:
- *configure
- make -j${BUILD_PARALLEL_JOBS:-1} -k all V=1
......@@ -253,6 +253,7 @@ stages:
<<: *default_triggering_rules
stage: system
before_script:
- test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
- *setup_interfaces
- *setup_softhsm
script:
......@@ -481,7 +482,7 @@ docs:
stage: docs
before_script:
- 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:
- *configure
- make -j${BUILD_PARALLEL_JOBS:-1} -k doc V=1
......@@ -697,10 +698,19 @@ gcc:out-of-tree:
CONFIGURE: ../configure
EXTRA_CONFIGURE: "--enable-dnstap --with-libidn2 --with-lmdb"
RUN_MAKE_INSTALL: 1
OOT_BUILD_WORKSPACE: workspace
OUT_OF_TREE_WORKSPACE: workspace
<<: *base_image
<<: *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)
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]
......
......@@ -294,21 +294,27 @@ elif [ "$sanitizer_summaries" -ne 0 ]; then
echoinfo "I:$systest:$sanitizer_summaries sanitizer report(s) found"
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
echofail "R:$systest:FAIL"
else
echopass "R:$systest:PASS"
if $clean; then
( cd "${systest}" && $SHELL clean.sh "$@" )
if [ "${srcdir}" != "${builddir}" ]; then
rm -rf "./${systest}" ## FIXME (this also removes compiled binaries)
fi
if test -d ${srcdir}/../../../.git; then
git status -su --ignored "${systest}" 2>/dev/null | \
sed -n -e 's|^?? \(.*\)|I:file \1 not removed|p' \
-e 's|^!! \(.*/named.run\)$|I:file \1 not removed|p' \
-e 's|^!! \(.*/named.memstats\)$|I:file \1 not removed|p'
fi
( cd "${systest}" && $SHELL clean.sh "$@" )
if [ "${srcdir}" = "${builddir}" ]; then
print_outstanding_files
else
print_outstanding_files | xargs rm -rf
find "./${systest}" \( -type d -empty -o -name Makefile.in \) -delete 2>/dev/null
fi
fi
fi
......
......@@ -60,3 +60,12 @@ Bug Fixes
- The introduction of KASP support broke whether the second field
of sig-validity-interval was treated as days or hours. (Thanks to
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);
/*%<
* 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) {
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) {
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);
......
......@@ -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]);
}
......