Mathieu,
As the original issue was reported for BIND 9.11, I prepared a patch
(attached) which applies cleanly on top of BIND 9.11.2, which is the
latest maintenance release in the 9.11 branch.
Could you please test it and tell me whether the modified BIND behavior
it causes is acceptable to you? If it is, please do not pass the patch
on just yet; once it gets merged, I will refer you to the relevant git
commit.
Just applying the patch and recompiling BIND should be all that is
needed for FreeBSD to be handled in a bit more friendly manner. As I
mentioned before, the new configure option (--disable-tcp-fastopen) is
only there for other systems that we currently do not know of which
might exhibit a similar behavior.
Thanks!
diff --git a/configure b/configure
index e10f5134b9..44eb1d4865 100755
--- a/configure
+++ b/configure
@@ -1031,6 +1031,7 @@ enable_backtrace
enable_symtable
enable_ipv6
with_kame
+enable_tcp_fastopen
enable_getifaddrs
with_readline
enable_isc_spnego
@@ -1714,6 +1715,7 @@ Optional Features:
--enable-symtable use internal symbol table for backtrace
[all|minimal(default)|none]
--enable-ipv6 use IPv6 default=autodetect
+ --disable-tcp-fastopen disable TCP Fast Open support [default=autodetect]
--enable-getifaddrs enable the use of getifaddrs() [yes|no].
--disable-isc-spnego use SPNEGO from GSSAPI library
--disable-chroot disable chroot
@@ -18565,11 +18567,22 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
#
-# Look for TCP_FASTOPEN
+# Allow forcibly disabling TCP Fast Open support as autodetection might yield
+# confusing results on some systems (e.g. FreeBSD; see set_tcp_fastopen()
+# comment in lib/isc/unix/socket.c).
#
+
+# Check whether --enable-tcp_fastopen was given.
+if test "${enable_tcp_fastopen+set}" = set; then :
+ enableval=$enable_tcp_fastopen;
+fi
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TCP_FASTOPEN socket option" >&5
$as_echo_n "checking for TCP_FASTOPEN socket option... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+case "$enable_tcp_fastopen" in
+yes|''|autodetect)
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
@@ -18584,15 +18597,23 @@ if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "has_tfo" >/dev/null 2>&1; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- ISC_PLATFORM_HAVETFO="#define ISC_PLATFORM_HAVETFO 1"
+ ISC_PLATFORM_HAVETFO="#define ISC_PLATFORM_HAVETFO 1"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
- ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"
+ ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"
fi
rm -f conftest*
+ ;;
+ no)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5
+$as_echo "disabled" >&6; }
+ ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"
+ ;;
+esac
+
#
# Check for addrinfo
diff --git a/configure.in b/configure.in
index 7886208d6f..cb8061041b 100644
--- a/configure.in
+++ b/configure.in
@@ -3204,10 +3204,18 @@ AC_TRY_COMPILE([
AC_SUBST(ISC_PLATFORM_NEEDPORTT)
#
-# Look for TCP_FASTOPEN
+# Allow forcibly disabling TCP Fast Open support as autodetection might yield
+# confusing results on some systems (e.g. FreeBSD; see set_tcp_fastopen()
+# comment in lib/isc/unix/socket.c).
#
+
+AC_ARG_ENABLE(tcp_fastopen,
+ [ --disable-tcp-fastopen disable TCP Fast Open support [[default=autodetect]]])
+
AC_MSG_CHECKING(for TCP_FASTOPEN socket option)
-AC_EGREP_CPP(has_tfo, [
+case "$enable_tcp_fastopen" in
+yes|''|autodetect)
+ AC_EGREP_CPP(has_tfo, [
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
@@ -3215,10 +3223,17 @@ AC_EGREP_CPP(has_tfo, [
int has_tfo() { return (0); }
#endif
],
- [AC_MSG_RESULT(yes)
- ISC_PLATFORM_HAVETFO="#define ISC_PLATFORM_HAVETFO 1"],
- [AC_MSG_RESULT(no)
- ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"])
+ [AC_MSG_RESULT(yes)
+ ISC_PLATFORM_HAVETFO="#define ISC_PLATFORM_HAVETFO 1"],
+ [AC_MSG_RESULT(no)
+ ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"])
+
+ ;;
+ no)
+ AC_MSG_RESULT(disabled)
+ ISC_PLATFORM_HAVETFO="#undef ISC_PLATFORM_HAVETFO"
+ ;;
+esac
AC_SUBST(ISC_PLATFORM_HAVETFO)
#
diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c
index d820a6df01..25b2464a22 100644
--- a/lib/isc/unix/socket.c
+++ b/lib/isc/unix/socket.c
@@ -14,6 +14,9 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
#include <sys/time.h>
#include <sys/uio.h>
@@ -5645,6 +5648,69 @@ isc__socket_filter(isc_socket_t *sock0, const char *filter) {
#endif
}
+/*
+ * Try enabling TCP Fast Open for a given socket if the OS supports it.
+ */
+static void
+set_tcp_fastopen(isc__socket_t *sock, unsigned int backlog) {
+#if defined(ISC_PLATFORM_HAVETFO) && defined(TCP_FASTOPEN)
+ char strbuf[ISC_STRERRORSIZE];
+
+/*
+ * FreeBSD, as of versions 10.3 and 11.0, defines TCP_FASTOPEN while also
+ * shipping a default kernel without TFO support, so we special-case it by
+ * performing an additional runtime check for TFO support using sysctl to
+ * prevent setsockopt() errors from being logged.
+ */
+#if defined(__FreeBSD__) && defined(HAVE_SYSCTLBYNAME)
+#define SYSCTL_TFO "net.inet.tcp.fastopen.enabled"
+ unsigned int enabled;
+ size_t enabledlen = sizeof(enabled);
+ static isc_boolean_t tfo_notice_logged = ISC_FALSE;
+
+ if (sysctlbyname(SYSCTL_TFO, &enabled, &enabledlen, NULL, 0) < 0) {
+ /*
+ * This kernel does not support TCP Fast Open. There is
+ * nothing more we can do.
+ */
+ return;
+ } else if (enabled == 0) {
+ /*
+ * This kernel does support TCP Fast Open, but it is disabled
+ * by sysctl. Notify the user, but do not nag.
+ */
+ if (!tfo_notice_logged) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET, ISC_LOG_NOTICE,
+ "TCP_FASTOPEN support is disabled by "
+ "sysctl (" SYSCTL_TFO " = 0)");
+ tfo_notice_logged = ISC_TRUE;
+ }
+ return;
+ }
+#endif
+
+#ifdef __APPLE__
+ backlog = 1;
+#else
+ backlog = backlog / 2;
+ if (backlog == 0)
+ backlog = 1;
+#endif
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_FASTOPEN,
+ (void *)&backlog, sizeof(backlog)) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "setsockopt(%d, TCP_FASTOPEN) failed with %s",
+ sock->fd, strbuf);
+ /* TCP_FASTOPEN is experimental so ignore failures */
+ }
+#else
+ UNUSED(sock);
+ UNUSED(backlog);
+#endif
+}
+
/*
* Set up to listen on a given socket. We do this by creating an internal
* event that will be dispatched when the socket has read activity. The
@@ -5681,23 +5747,7 @@ isc__socket_listen(isc_socket_t *sock0, unsigned int backlog) {
return (ISC_R_UNEXPECTED);
}
-#if defined(ISC_PLATFORM_HAVETFO) && defined(TCP_FASTOPEN)
-#ifdef __APPLE__
- backlog = 1;
-#else
- backlog = backlog / 2;
- if (backlog == 0)
- backlog = 1;
-#endif
- if (setsockopt(sock->fd, IPPROTO_TCP, TCP_FASTOPEN,
- (void *)&backlog, sizeof(backlog)) < 0) {
- isc__strerror(errno, strbuf, sizeof(strbuf));
- UNEXPECTED_ERROR(__FILE__, __LINE__,
- "setsockopt(%d, TCP_FASTOPEN) failed with %s",
- sock->fd, strbuf);
- /* TCP_FASTOPEN is experimental so ignore failures */
- }
-#endif
+ set_tcp_fastopen(sock, backlog);
sock->listener = 1;