From 617589747854f07a570ff459deeae2e082f36efb Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 26 May 2022 02:33:52 -0700 Subject: [PATCH] add "rndc fetchlimit" to show fetchlimited servers this command runs dns_adb_dumpquota() to display all servers in the ADB that are being actively fetchlimited by the fetches-per-server controls (i.e, servers with a nonzero average timeout ratio or with the quota having been reduced from the default value). the "fetchlimit" system test has been updated to use the new command to check quota values instead of "rndc dumpdb". --- bin/named/control.c | 2 + bin/named/include/named/control.h | 1 + bin/named/include/named/server.h | 7 +++ bin/named/server.c | 67 +++++++++++++++++++++ bin/rndc/rndc.rst | 5 ++ bin/tests/system/fetchlimit/tests.sh | 21 +++---- doc/man/rndc.8in | 6 ++ lib/dns/adb.c | 87 ++++++++++++++++++++++++++++ lib/dns/include/dns/adb.h | 26 +++++++++ 9 files changed, 210 insertions(+), 12 deletions(-) diff --git a/bin/named/control.c b/bin/named/control.c index 29c848b070..0b742407fe 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -218,6 +218,8 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly, result = ISC_R_SUCCESS; } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) { result = named_server_dumpstats(named_g_server); + } else if (command_compare(command, NAMED_COMMAND_FETCHLIMIT)) { + result = named_server_fetchlimit(named_g_server, lex, text); } else if (command_compare(command, NAMED_COMMAND_FLUSH)) { result = named_server_flushcache(named_g_server, lex); } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) { diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 1ea4c490fb..425b50696f 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -70,6 +70,7 @@ #define NAMED_COMMAND_DNSTAP "dnstap" #define NAMED_COMMAND_TCPTIMEOUTS "tcp-timeouts" #define NAMED_COMMAND_SERVESTALE "serve-stale" +#define NAMED_COMMAND_FETCHLIMIT "fetchlimit" isc_result_t named_controls_create(named_server_t *server, named_controls_t **ctrlsp); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index d1c97cd853..ce45b440c1 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -401,3 +401,10 @@ named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text); isc_result_t named_server_servestale(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Report fetch-limited ADB server addresses. + */ +isc_result_t +named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); diff --git a/bin/named/server.c b/bin/named/server.c index dcb7a708f4..b109683711 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -16600,3 +16600,70 @@ cleanup: return (result); } + +isc_result_t +named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_view_t *view = NULL; + char *ptr = NULL, *viewname = NULL; + bool first = true; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Look for the view name. */ + viewname = next_token(lex, text); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + char tbuf[BUFSIZ]; + unsigned int used; + uint32_t val; + int s; + + if (view->rdclass != dns_rdataclass_in) { + continue; + } + + if (viewname != NULL && strcasecmp(view->name, viewname) != 0) { + continue; + } + + if (view->adb == NULL) { + continue; + } + + if (!first) { + putstr(text, "\n"); + } + putstr(text, "Rate limited servers, view "); + putstr(text, view->name); + + dns_adb_getquota(view->adb, &val, NULL, NULL, NULL, NULL); + s = snprintf(tbuf, sizeof(tbuf), + " (fetches-per-server %u):", val); + if (s < 0 || (unsigned)s > sizeof(tbuf)) { + return (ISC_R_NOSPACE); + } + first = false; + putstr(text, tbuf); + used = isc_buffer_usedlength(*text); + CHECK(dns_adb_dumpquota(view->adb, text)); + if (used == isc_buffer_usedlength(*text)) { + putstr(text, "\n None."); + } + } + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index 6717e242d1..2236d1d911 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -204,6 +204,11 @@ Currently supported commands are: (See the ``dump-file`` option in the BIND 9 Administrator Reference Manual.) +.. option:: fetchlimit [view] + + This command dumps a list of servers which are currently being + rate-limited as a result of ``fetches-per-server`` settings. + .. option:: flush This command flushes the server's cache. diff --git a/bin/tests/system/fetchlimit/tests.sh b/bin/tests/system/fetchlimit/tests.sh index c10bee6677..a96caafa9d 100644 --- a/bin/tests/system/fetchlimit/tests.sh +++ b/bin/tests/system/fetchlimit/tests.sh @@ -58,12 +58,11 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -quota=$5 -[ ${5:-200} -lt 200 ] || ret=1 +quota=$2 +[ ${quota:-200} -lt 200 ] || ret=1 echo_i "checking servfail statistics" ret=0 @@ -92,12 +91,11 @@ for try in 1 2 3 4 5; do done echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -[ ${5:-${quota}} -lt $quota ] || ret=1 -quota=$5 +[ ${2:-${quota}} -lt $quota ] || ret=1 +quota=$2 for try in 1 2 3 4 5 6 7 8 9 10; do burst c $try @@ -107,12 +105,11 @@ for try in 1 2 3 4 5 6 7 8 9 10; do done echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -[ ${5:-${quota}} -gt $quota ] || ret=1 -quota=$5 +[ ${2:-${quota}} -gt $quota ] || ret=1 +quota=$2 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) diff --git a/doc/man/rndc.8in b/doc/man/rndc.8in index 4023b82ee2..c21fab7a61 100644 --- a/doc/man/rndc.8in +++ b/doc/man/rndc.8in @@ -226,6 +226,12 @@ Manual.) .UNINDENT .INDENT 0.0 .TP +.B fetchlimit [view] +This command dumps a list of servers which are currently being +rate\-limited as a result of \fBfetches\-per\-server\fP settings. +.UNINDENT +.INDENT 0.0 +.TP .B flush This command flushes the server\(aqs cache. .UNINDENT diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 7d549aa5ee..b97f3e6390 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -2976,6 +2976,67 @@ print_find_list(FILE *f, dns_adbname_t *name) { } } +static isc_result_t +putstr(isc_buffer_t **b, const char *str) { + isc_result_t result; + + result = isc_buffer_reserve(b, strlen(str)); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_buffer_putstr(*b, str); + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_adb_dumpquota(dns_adb_t *adb, isc_buffer_t **buf) { + isc_result_t result; + isc_ht_iter_t *it = NULL; + + REQUIRE(DNS_ADB_VALID(adb)); + + RWLOCK(&adb->entries_lock, isc_rwlocktype_read); + isc_ht_iter_create(adb->entrybuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbentrybucket_t *ebucket = NULL; + dns_adbentry_t *entry = NULL; + + isc_ht_iter_current(it, (void **)&ebucket); + LOCK(&ebucket->lock); + for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; + entry = ISC_LIST_NEXT(entry, plink)) + { + char addrbuf[ISC_NETADDR_FORMATSIZE]; + char text[BUFSIZ]; + isc_netaddr_t netaddr; + + if (entry->atr == 0.0 && entry->quota == adb->quota) { + continue; + } + + isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); + isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); + + snprintf(text, sizeof(text), + "\n- quota %s (%" PRIuFAST32 "/%d) atr %0.2f", + addrbuf, atomic_load_relaxed(&entry->quota), + adb->quota, entry->atr); + putstr(buf, text); + } + UNLOCK(&ebucket->lock); + } + RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + isc_ht_iter_destroy(&it); + + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + return (result); +} + static isc_result_t dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { isc_result_t result; @@ -3965,6 +4026,32 @@ dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq, double low, adb->atr_discount = discount; } +void +dns_adb_getquota(dns_adb_t *adb, uint32_t *quotap, uint32_t *freqp, + double *lowp, double *highp, double *discountp) { + REQUIRE(DNS_ADB_VALID(adb)); + + if (quotap != NULL) { + *quotap = adb->quota; + } + + if (freqp != NULL) { + *freqp = adb->atr_freq; + } + + if (lowp != NULL) { + *lowp = adb->atr_low; + } + + if (highp != NULL) { + *highp = adb->atr_high; + } + + if (discountp != NULL) { + *discountp = adb->atr_discount; + } +} + bool dns_adbentry_overquota(dns_adbentry_t *entry) { uint_fast32_t quota, active; diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 4bfe6d0a42..1daa6b8e08 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -748,6 +748,20 @@ dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq, double low, *\li 'adb' is valid. */ +void +dns_adb_getquota(dns_adb_t *adb, uint32_t *quotap, uint32_t *freqp, + double *lowp, double *highp, double *discountp); +/*%< + * Get the quota values set by dns_adb_setquota(). + * If any of the 'quotap', 'freqp', 'lowp', 'highp', and + * 'discountp' parameters are non-NULL, then the memory they + * point to will be updated to hold the corresponding quota + * or parameter value. + * + * Requires: + *\li 'adb' is valid. + */ + bool dns_adbentry_overquota(dns_adbentry_t *entry); /*%< @@ -782,4 +796,16 @@ dns_adb_getstats(dns_adb_t *adb); * Requires: * \li 'adb' is valid. */ + +isc_result_t +dns_adb_dumpquota(dns_adb_t *adb, isc_buffer_t **buf); +/*% + * Dump the addresses, current quota values, and current ATR values + * for all servers that are currently being fetchlimited. Servers + * for which the quota is still equal to the default and the ATR + * is zero are not printed. + * + * Requires: + * \li 'adb' is valid. + */ ISC_LANG_ENDDECLS