]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Limit TCP pipelining per shared dispatch
authorOndřej Surý <ondrej@isc.org>
Sun, 15 Mar 2026 06:23:33 +0000 (07:23 +0100)
committerOndřej Surý <ondrej@isc.org>
Wed, 6 May 2026 13:05:48 +0000 (15:05 +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'.

(cherry picked from commit 385ceabe8f9d50c13f6781fbed79807ed77da69d)

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

index 98fd1aa3e9d235c69155a850598816cdb8b61a45..f3b2ec80f5a9a75b7a6abb7e768bac3ab23ad536 100644 (file)
@@ -122,6 +122,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 program_name[NAME_MAX] = "named";
@@ -809,6 +810,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 f7e339a4c41b8d0ad1ece10b447df0f52b4b3251..ed31f0c448d7b061bc6930f879fc5a241c578b66 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 {
@@ -1567,6 +1574,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);