Merge branch '665-rndc-fetchlimit' into 'main'

implement "rndc fetchlimit"

Closes #665

See merge request isc-projects/bind9!6362
This commit is contained in:
Evan Hunt 2022-07-07 03:05:53 +00:00
commit d862879fba
15 changed files with 317 additions and 75 deletions

View file

@ -1,3 +1,8 @@
5919. [func] The "rndc fetchlimit" command lists name servers
and domain names that are being rate-limited by
"fetches-per-server" or "fetches-per-zone" limits.
[GL #665]
5918. [test] Convert system tests to use a default HMAC algorithm
where the test is not HMAC specific. [GL #3433]

View file

@ -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)) {

View file

@ -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);

View file

@ -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);

View file

@ -16600,3 +16600,85 @@ 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[100];
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.");
}
putstr(text, "\nRate limited servers, view ");
putstr(text, view->name);
val = dns_resolver_getfetchesperzone(view->resolver);
s = snprintf(tbuf, sizeof(tbuf),
" (fetches-per-zone %u):", val);
if (s < 0 || (unsigned)s > sizeof(tbuf)) {
return (ISC_R_NOSPACE);
}
putstr(text, tbuf);
used = isc_buffer_usedlength(*text);
CHECK(dns_resolver_dumpquota(view->resolver, text));
if (used == isc_buffer_usedlength(*text)) {
putstr(text, "\n None.");
}
}
cleanup:
if (isc_buffer_usedlength(*text) > 0) {
(void)putnull(text);
}
return (result);
}

View file

@ -204,6 +204,13 @@ 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 that are currently being
rate-limited as a result of ``fetches-per-server`` settings, and
a list of domain names that are currently being rate-limited as
a result of ``fetches-per-zone`` settings.
.. option:: flush
This command flushes the server's cache.

View file

@ -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))
@ -131,9 +128,10 @@ for try in 1 2 3 4 5; do
success=$((success+1))
grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
fail=$(($fail+1))
stat 30 50 || ret=1
stat 40 40 || ret=1
allowed=$($RNDCCMD fetchlimit | awk '/lamesub/ { print $6 }')
[ "${allowed:-0}" -eq 40 ] || ret=1
[ $ret -eq 1 ] && break
$RNDCCMD recursing 2>&1 | sed 's/^/ns3 /' | cat_i
sleep 1
done
echo_i "$success successful valid queries, $fail SERVFAIL"

View file

@ -226,6 +226,14 @@ Manual.)
.UNINDENT
.INDENT 0.0
.TP
.B fetchlimit [view]
This command dumps a list of servers that are currently being
rate\-limited as a result of \fBfetches\-per\-server\fP settings, and
a list of domain names that are currently being rate\-limited as
a result of \fBfetches\-per\-zone\fP settings.
.UNINDENT
.INDENT 0.0
.TP
.B flush
This command flushes the server\(aqs cache.
.UNINDENT

View file

@ -25,7 +25,10 @@ Known Issues
New Features
~~~~~~~~~~~~
- None.
- The new ``rndc fetchlimit`` command prints a list of name server
addresses that are currently rate-limited due to ``fetches-per-server``
and domain names that are rate limited due to ``fetches-per-zone``.
:gl:`#665`
Removed Features
~~~~~~~~~~~~~~~~

View file

@ -2880,8 +2880,8 @@ dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry, bool debug,
}
}
void
dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) {
static void
dumpfind(dns_adbfind_t *find, FILE *f) {
char tmp[512];
const char *tmpp = NULL;
dns_adbaddrinfo_t *ai = NULL;
@ -2971,11 +2971,72 @@ print_find_list(FILE *f, dns_adbname_t *name) {
find = ISC_LIST_HEAD(name->finds);
while (find != NULL) {
dns_adb_dumpfind(find, f);
dumpfind(find, f);
find = ISC_LIST_NEXT(find, plink);
}
}
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;

View file

@ -454,29 +454,15 @@ dns_adb_destroyfind(dns_adbfind_t **find);
void
dns_adb_dump(dns_adb_t *adb, FILE *f);
/*%<
* This function is only used for debugging. It will dump as much of the
* state of the running system as possible.
* Used by "rndc dumpdb": Dump the state of the running ADB.
*
* Requires:
*
*\li adb be valid.
*\li adb is valid.
*
*\li f != NULL, and is a file open for writing.
*/
void
dns_adb_dumpfind(dns_adbfind_t *find, FILE *f);
/*%<
* This function is only used for debugging. Dump the data associated
* with a find.
*
* Requires:
*
*\li find is valid.
*
* \li f != NULL, and is a file open for writing.
*/
isc_result_t
dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
const dns_name_t *qname, dns_rdatatype_t type,
@ -762,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);
/*%<
@ -796,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

View file

@ -534,6 +534,9 @@ dns_resolver_setclientsperquery(dns_resolver_t *resolver, uint32_t min,
void
dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients);
uint32_t
dns_resolver_getfetchesperzone(dns_resolver_t *resolver);
void
dns_resolver_getclientsperquery(dns_resolver_t *resolver, uint32_t *cur,
uint32_t *min, uint32_t *max);
@ -703,6 +706,8 @@ dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which);
void
dns_resolver_dumpfetches(dns_resolver_t *resolver, isc_statsformat_t format,
FILE *fp);
isc_result_t
dns_resolver_dumpquota(dns_resolver_t *res, isc_buffer_t **buf);
#ifdef ENABLE_AFL
/*%

View file

@ -868,28 +868,6 @@ dns_view_dialup(dns_view_t *view);
* Perform dialup-time maintenance on the zones of 'view'.
*/
isc_result_t
dns_view_dumpdbtostream(dns_view_t *view, FILE *fp);
/*%<
* Dump the current state of the view 'view' to the stream 'fp'
* for purposes of analysis or debugging.
*
* Currently the dumped state includes the view's cache; in the future
* it may also include other state such as the address database.
* It will not not include authoritative data since it is voluminous and
* easily obtainable by other means.
*
* Requires:
*
*\li 'view' is valid.
*
*\li 'fp' refers to a file open for writing.
*
* Returns:
* \li ISC_R_SUCCESS The cache was successfully dumped.
* \li others An error occurred (see dns_master_dump)
*/
isc_result_t
dns_view_flushcache(dns_view_t *view, bool fixuponly);
/*%<

View file

@ -10075,6 +10075,7 @@ destroy(dns_resolver_t *res) {
}
isc_mem_put(res->mctx, res->tasks, res->ntasks * sizeof(res->tasks[0]));
RWLOCK(&res->hash_lock, isc_rwlocktype_write);
isc_ht_iter_create(res->buckets, &it);
for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
result = isc_ht_iter_delcurrent_next(it))
@ -10088,8 +10089,10 @@ destroy(dns_resolver_t *res) {
}
isc_ht_iter_destroy(&it);
isc_ht_destroy(&res->buckets);
RWUNLOCK(&res->hash_lock, isc_rwlocktype_write);
isc_rwlock_destroy(&res->hash_lock);
RWLOCK(&res->zonehash_lock, isc_rwlocktype_write);
isc_ht_iter_create(res->zonebuckets, &it);
for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
result = isc_ht_iter_delcurrent_next(it))
@ -10109,6 +10112,7 @@ destroy(dns_resolver_t *res) {
}
isc_ht_iter_destroy(&it);
isc_ht_destroy(&res->zonebuckets);
RWUNLOCK(&res->zonehash_lock, isc_rwlocktype_write);
isc_rwlock_destroy(&res->zonehash_lock);
if (res->dispatches4 != NULL) {
@ -11352,6 +11356,13 @@ dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients) {
atomic_store_release(&resolver->zspill, clients);
}
uint32_t
dns_resolver_getfetchesperzone(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (atomic_load_relaxed(&resolver->zspill));
}
bool
dns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
@ -11486,6 +11497,61 @@ dns_resolver_dumpfetches(dns_resolver_t *res, isc_statsformat_t format,
isc_ht_iter_destroy(&it);
}
isc_result_t
dns_resolver_dumpquota(dns_resolver_t *res, isc_buffer_t **buf) {
isc_result_t result;
isc_ht_iter_t *it = NULL;
uint_fast32_t spill;
REQUIRE(VALID_RESOLVER(res));
spill = atomic_load_acquire(&res->zspill);
if (spill == 0) {
return (ISC_R_SUCCESS);
}
RWLOCK(&res->zonehash_lock, isc_rwlocktype_read);
isc_ht_iter_create(res->zonebuckets, &it);
for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
result = isc_ht_iter_next(it))
{
zonebucket_t *bucket = NULL;
isc_ht_iter_current(it, (void **)&bucket);
LOCK(&bucket->lock);
for (fctxcount_t *fc = ISC_LIST_HEAD(bucket->list); fc != NULL;
fc = ISC_LIST_NEXT(fc, link))
{
char nb[DNS_NAME_FORMATSIZE], text[BUFSIZ];
if (fc->count < spill) {
continue;
}
dns_name_format(fc->domain, nb, sizeof(nb));
snprintf(text, sizeof(text),
"\n- %s: %u active (allowed %u spilled %u)",
nb, fc->count, fc->allowed, fc->dropped);
result = isc_buffer_reserve(buf, strlen(text));
if (result != ISC_R_SUCCESS) {
UNLOCK(&bucket->lock);
goto cleanup;
}
isc_buffer_putstr(*buf, text);
}
UNLOCK(&bucket->lock);
}
if (result == ISC_R_NOMORE) {
result = ISC_R_SUCCESS;
}
cleanup:
RWUNLOCK(&res->zonehash_lock, isc_rwlocktype_read);
isc_ht_iter_destroy(&it);
return (result);
}
void
dns_resolver_setquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which,
isc_result_t resp) {

View file

@ -1472,25 +1472,6 @@ dns_view_checksig(dns_view_t *view, isc_buffer_t *source, dns_message_t *msg) {
view->dynamickeys));
}
isc_result_t
dns_view_dumpdbtostream(dns_view_t *view, FILE *fp) {
isc_result_t result;
REQUIRE(DNS_VIEW_VALID(view));
(void)fprintf(fp, ";\n; Cache dump of view '%s'\n;\n", view->name);
result = dns_master_dumptostream(view->mctx, view->cachedb, NULL,
&dns_master_style_cache,
dns_masterformat_text, NULL, fp);
if (result != ISC_R_SUCCESS) {
return (result);
}
dns_adb_dump(view->adb, fp);
dns_resolver_printbadcache(view->resolver, fp);
dns_badcache_print(view->failcache, "SERVFAIL cache", fp);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_view_flushcache(dns_view_t *view, bool fixuponly) {
isc_result_t result;