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