]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add the tcp-reuse-timeout option 12289/head
authorOndřej Surý <ondrej@isc.org>
Sun, 21 Jun 2026 18:20:35 +0000 (20:20 +0200)
committerOndřej Surý <ondrej@isc.org>
Wed, 24 Jun 2026 11:52:46 +0000 (13:52 +0200)
The idle timeout that bounds how long a reused outgoing TCP/TLS
connection is held open for reuse was only tunable through the 'named -T
tcpidletimeout' developer hook added earlier on this branch. Make it a
proper configuration option, tcp-reuse-timeout (options block, in units
of 100 milliseconds like the other tcp-*-timeout options), and drop the
-T hook.

bin/include/defaultconfig.h
bin/named/main.c
bin/named/server.c
doc/arm/reference.rst
doc/misc/options
lib/dns/dispatch.c
lib/dns/include/dns/dispatch.h
lib/isccfg/namedconf.c
tests/dns/dispatch_test.c

index d40b584d41e5a63b9cf60d3649b01888d6021913..469429711992817e2f2fa2c5342faa89feb8639c 100644 (file)
@@ -100,6 +100,7 @@ options {\n\
        tcp-listen-queue 10;\n\
        tcp-primaries-timeout 150;\n\
        tcp-receive-buffer 0;\n\
+       tcp-reuse-timeout 50;\n\
        tcp-send-buffer 0;\n\
        transfer-message-size 20480;\n\
        transfers-in 10;\n\
index f7f7afb4225dd17ac7aa4ebc8e791927d1228857..37b77bc2ed27d4d4cd68eb96d893eac21660d180 100644 (file)
@@ -127,7 +127,6 @@ extern unsigned int dns_zone_mkey_month;
 extern unsigned int dns_adb_entrywindow;
 extern unsigned int dns_adb_cachemin;
 extern size_t dns_dispatch_tcppipelining;
-extern unsigned int dns_dispatch_tcp_idle_timeout;
 
 static bool want_stats = false;
 static char absolute_conffile[PATH_MAX];
@@ -748,8 +747,6 @@ parse_T_opt(char *option) {
                                              "least 1");
                }
                dns_dispatch_tcppipelining = pipelining;
-       } else if (!strncmp(option, "tcpidletimeout=", 15)) {
-               dns_dispatch_tcp_idle_timeout = atoi(option + 15);
        } else {
                fprintf(stderr, "unknown -T flag '%s'\n", option);
        }
index fc53de5249482be08bca28623fa07bca5d75b573..be7ecd02251602659594f0cc9f90faf239b8c571 100644 (file)
 #define MAX_ADVERTISED_TIMEOUT UINT32_C(UINT16_MAX * 100)
 #define MIN_PRIMARIES_TIMEOUT  UINT32_C(2500)  /* 2.5 seconds */
 #define MAX_PRIMARIES_TIMEOUT  UINT32_C(120000) /* 2 minutes */
+#define MAX_REUSE_TIMEOUT      UINT32_C(120000) /* 2 minutes */
 
 /*%
  * Check an operation for failure.  Assumes that the function
@@ -7723,7 +7724,7 @@ apply_configuration(cfg_obj_t *effectiveconfig, cfg_obj_t *bindkeys,
        ns_altsecretlist_t altsecrets, tmpaltsecrets;
        uint32_t softquota = 0;
        uint32_t max;
-       uint64_t initial, idle, keepalive, advertised, primaries;
+       uint64_t initial, idle, keepalive, advertised, primaries, reuse;
        bool loadbalancesockets;
        bool exclusive = false;
        dns_aclenv_t *env =
@@ -8000,12 +8001,26 @@ apply_configuration(cfg_obj_t *effectiveconfig, cfg_obj_t *bindkeys,
                primaries = MIN_PRIMARIES_TIMEOUT;
        }
 
+       obj = NULL;
+       result = named_config_get(maps, "tcp-reuse-timeout", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       reuse = cfg_obj_asuint32(obj) * 100;
+       if (reuse > MAX_REUSE_TIMEOUT) {
+               cfg_obj_log(obj, ISC_LOG_WARNING,
+                           "tcp-reuse-timeout value is out of range: "
+                           "lowering to %" PRIu32,
+                           MAX_REUSE_TIMEOUT / 100);
+               reuse = MAX_REUSE_TIMEOUT;
+       }
+
        isc_nm_setinitialtimeout(initial);
        isc_nm_setprimariestimeout(primaries);
        isc_nm_setidletimeout(idle);
        isc_nm_setkeepalivetimeout(keepalive);
        isc_nm_setadvertisedtimeout(advertised);
 
+       dns_dispatchmgr_setreusetimeout(named_g_dispatchmgr, reuse);
+
 #define CAP_IF_NOT_ZERO(v, min, max) \
        if (v > 0 && v < min) {      \
                v = min;             \
index 86ae19f5ccf10168c8b04cb066cb1e56671083fc..e28b83c7f0cde868053f21033a6aeefd6004a814 100644 (file)
@@ -3924,6 +3924,20 @@ system.
    option are expected to use TCP connections for more than one message.
    This value can be updated at runtime by using :option:`rndc tcp-timeouts`.
 
+.. namedconf:statement:: tcp-reuse-timeout
+   :tags: query
+   :short: Sets the amount of time (in milliseconds) that an idle outgoing TCP connection is kept open for reuse.
+
+   This sets the amount of time, in units of 100 milliseconds, that an idle
+   outgoing TCP or TLS connection opened by :iscman:`named` (for example, to a
+   forwarder or an authoritative server) is kept open after its last outstanding
+   response has completed, so that it can be reused by a later query instead of
+   being closed and reopened. The default is 50 (5 seconds), and the maximum is
+   1200 (two minutes). A value of 0 disables keeping idle outgoing TCP or TLS
+   connections open for reuse; it does not affect sharing of a connection while
+   queries are still outstanding. Values above the maximum are adjusted with a
+   logged warning.
+
 .. namedconf:statement:: tcp-advertised-timeout
    :tags: query
    :short: Sets the timeout value (in milliseconds) that the server sends in responses containing the EDNS TCP keepalive option.
index 677c518124867a4d808285a9b771206841f63225..8303cdf98cdc60494d6e87cc26b0d570b4c61fd2 100644 (file)
@@ -308,6 +308,7 @@ options {
        tcp-listen-queue <integer>;
        tcp-primaries-timeout <integer>;
        tcp-receive-buffer <integer>;
+       tcp-reuse-timeout <integer>;
        tcp-send-buffer <integer>;
        tkey-gssapi-keytab <quoted_string>;
        tls-port <integer>;
index dc31532290a50263d45f446d528a3f6356907783..55cb9770c642c94846001a6fbb93d222fae276b2 100644 (file)
  */
 size_t dns_dispatch_tcppipelining = 256;
 
-/*
- * Idle timeout (in milliseconds) for a reused outgoing TCP connection.  While a
- * dispatch has no outstanding responses we keep a read pending so a
- * peer-initiated close is noticed promptly; this bounds how long such an idle
- * connection is kept open for reuse.  Can be overridden via
- * 'named -T tcpidletimeout=N'.
- */
-unsigned int dns_dispatch_tcp_idle_timeout = 5000;
-
 typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
 
 struct dns_dispatchmgr {
@@ -75,6 +66,8 @@ struct dns_dispatchmgr {
        dns_acl_t *blackhole;
        isc_stats_t *stats;
 
+       unsigned int reuse_timeout;
+
        uint32_t nloops;
 
        struct cds_lfht **tcps;
@@ -770,7 +763,7 @@ tcp_recv_processall(dns_displist_t *resps, isc_region_t *region) {
 
 static void
 tcp_startrecv_idle(dns_dispatch_t *disp) {
-       REQUIRE(dns_dispatch_tcp_idle_timeout > 0);
+       REQUIRE(disp->mgr->reuse_timeout > 0);
 
        /*
         * No outstanding responses, but the connection is still up and
@@ -780,7 +773,7 @@ tcp_startrecv_idle(dns_dispatch_t *disp) {
         * out again as a dead reused connection.
         */
        isc_nmhandle_cleartimeout(disp->handle);
-       isc_nmhandle_settimeout(disp->handle, dns_dispatch_tcp_idle_timeout);
+       isc_nmhandle_settimeout(disp->handle, disp->mgr->reuse_timeout);
        if (!disp->reading) {
                dispatch_log(disp, ISC_LOG_DEBUG(90), "keeping idle read on %p",
                             disp->handle);
@@ -947,7 +940,7 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
         */
        if (ISC_LIST_EMPTY(disp->active) &&
            disp->state == DNS_DISPATCHSTATE_CONNECTED &&
-           dns_dispatch_tcp_idle_timeout > 0)
+           disp->mgr->reuse_timeout > 0)
        {
                tcp_startrecv_idle(disp);
        }
@@ -1098,6 +1091,13 @@ dns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset,
        return setavailports(mgr, v4portset, v6portset);
 }
 
+void
+dns_dispatchmgr_setreusetimeout(dns_dispatchmgr_t *mgr,
+                               unsigned int reuse_timeout) {
+       REQUIRE(VALID_DISPATCHMGR(mgr));
+       mgr->reuse_timeout = reuse_timeout;
+}
+
 static void
 dispatchmgr_destroy(dns_dispatchmgr_t *mgr) {
        REQUIRE(VALID_DISPATCHMGR(mgr));
@@ -1799,7 +1799,7 @@ tcp_dispentry_cancel(dns_dispentry_t *resp, isc_result_t result) {
                         * dispatch is already shutting down, leave it alone so
                         * it can be torn down.
                         */
-                       if (dns_dispatch_tcp_idle_timeout > 0) {
+                       if (disp->mgr->reuse_timeout > 0) {
                                tcp_startrecv_idle(disp);
                        } else if (disp->reading) {
                                dispentry_log(resp, ISC_LOG_DEBUG(90),
index 1ee127115b38d0644e8f4a1a5ba38e872341570d..5f2432dd10d8aaf2cee5f88030b2b3cd22bd7fa1 100644 (file)
@@ -151,6 +151,15 @@ dns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset,
  *\li  v6portset is NULL or a valid port set
  */
 
+void
+dns_dispatchmgr_setreusetimeout(dns_dispatchmgr_t *mgr, unsigned int timeout);
+/*%<
+ * Sets the idle timeout (in milliseconds) for a reused outgoing TCP connection.
+ * While a dispatch has no outstanding responses we keep a read pending so a
+ * peer-initiated close is noticed promptly; this bounds how long such an idle
+ * connection is kept open for reuse.
+ */
+
 void
 dns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, isc_stats_t *stats);
 /*%<
index ea3b18aae6731af28bf429521ed766527c6472f8..30c3a7857e16c016a942c602c8b39aa2a87718e1 100644 (file)
@@ -1591,6 +1591,7 @@ static cfg_clausedef_t options_clauses[] = {
        { "tcp-listen-queue", &cfg_type_uint32, 0, NULL },
        { "tcp-primaries-timeout", &cfg_type_uint32, 0, NULL },
        { "tcp-receive-buffer", &cfg_type_uint32, 0, NULL },
+       { "tcp-reuse-timeout", &cfg_type_uint32, 0, NULL },
        { "tcp-send-buffer", &cfg_type_uint32, 0, NULL },
        { "tkey-dhkey", NULL, CFG_CLAUSEFLAG_ANCIENT, NULL },
        { "tkey-domain", &cfg_type_qstring, CFG_CLAUSEFLAG_ANCIENT, NULL },
index ce4eee28508367884218c928ff24108e9a81241d..91e7bb5e1ed77cd415be53a409dfacc1a66afc62 100644 (file)
@@ -1012,6 +1012,8 @@ ISC_LOOP_TEST_IMPL(dispatch_tcp_reuse_after_close) {
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
+       dns_dispatchmgr_setreusetimeout(test->dispatchmgr, 5000);
+
        result = dns_dispatch_createtcp(
                test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
                DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);