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'.
This commit is contained in:
Ondřej Surý 2026-03-15 07:23:33 +01:00 committed by Ondřej Surý
parent 05e8b58307
commit 385ceabe8f
No known key found for this signature in database
GPG key ID: 2820F37E873DEA41
2 changed files with 28 additions and 0 deletions

View 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);
}

View file

@ -49,6 +49,13 @@
#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);