]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Limit TCP pipelining per shared dispatch
authorOndřej Surý <ondrej@sury.org>
Sun, 15 Mar 2026 06:23:33 +0000 (07:23 +0100)
committerOndřej Surý <ondrej@isc.org>
Tue, 14 Apr 2026 15:48:16 +0000 (17:48 +0200)
Cap the number of in-flight queries on a single shared TCP dispatch.
When the limit is reached, the dispatch is removed from the hash
table so subsequent queries get a fresh connection.  The existing
dispatch continues serving its queries until they complete.

This bounds the blast radius of a connection drop: at most N queries
fail simultaneously instead of all queries to that server.

The default limit is 256.  It can be overridden for testing via
'named -T tcppipelining=N'.

bin/named/main.c
lib/dns/dispatch.c

index de12a973243ed42677ae19cb9a82cba2c928d077..37b77bc2ed27d4d4cd68eb96d893eac21660d180 100644 (file)
@@ -126,6 +126,7 @@ 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;
 
 static bool want_stats = false;
 static char absolute_conffile[PATH_MAX];
@@ -739,6 +740,13 @@ parse_T_opt(char *option) {
                dns_adb_entrywindow = atoi(option + 15);
        } else if (!strncmp(option, "adbcachemin=", 12)) {
                dns_adb_cachemin = atoi(option + 12);
+       } else if (!strncmp(option, "tcppipelining=", 14)) {
+               size_t pipelining = atoi(option + 14);
+               if (pipelining < 1) {
+                       named_main_earlyfatal("tcppipelining must be at "
+                                             "least 1");
+               }
+               dns_dispatch_tcppipelining = pipelining;
        } else {
                fprintf(stderr, "unknown -T flag '%s'\n", option);
        }
index a6f7cc4e992448a4f8a49341666da253bc43bde1..74209ff50b6e5b95ef0fc0a51edc09f8ac697636 100644 (file)
 #include <dns/transport.h>
 #include <dns/types.h>
 
+/*
+ * Maximum number of queries to pipeline on a single shared TCP dispatch.
+ * Once reached, the dispatch is removed from the hash table so new queries
+ * get a fresh connection.  Can be overridden via 'named -T tcppipelining=N'.
+ */
+size_t dns_dispatch_tcppipelining = 256;
+
 typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
 
 struct dns_dispatchmgr {
@@ -1550,6 +1557,19 @@ fail:
 
        disp->requests++;
 
+       /*
+        * If this shared TCP dispatch has reached the pipelining limit,
+        * remove it from the hash table so new queries get a fresh
+        * connection.  The dispatch continues to serve its existing
+        * queries until they complete.
+        */
+       if (disp->socktype == isc_socktype_tcp &&
+           (disp->options & DNS_DISPATCHOPT_FIXEDID) == 0 &&
+           disp->requests >= dns_dispatch_tcppipelining)
+       {
+               (void)cds_lfht_del(disp->mgr->tcps[isc_tid()], &disp->ht_node);
+       }
+
        inc_stats(disp->mgr, (disp->socktype == isc_socktype_udp)
                                     ? dns_resstatscounter_disprequdp
                                     : dns_resstatscounter_dispreqtcp);