From: Ondřej Surý Date: Sun, 15 Mar 2026 06:23:33 +0000 (+0100) Subject: Limit TCP pipelining per shared dispatch X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=385ceabe8f9d50c13f6781fbed79807ed77da69d;p=thirdparty%2Fbind9.git Limit TCP pipelining per shared dispatch 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'. --- diff --git a/bin/named/main.c b/bin/named/main.c index de12a973243..37b77bc2ed2 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -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); } diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index a6f7cc4e992..74209ff50b6 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -49,6 +49,13 @@ #include #include +/* + * 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);