Commit d60557be authored by Matthijs Mekking's avatar Matthijs Mekking 🏡
Browse files

Merge branch '1074-matthijs-underflow-cachedb-statistics' into 'master'

Resolve "underflow in stats channel stale cached RRSIG gauge [ISC-support #14769]"

Closes #1074 and #602

See merge request !2237
parents 403cc1fa 4c0b0fa6
Pipeline #19533 passed with stages
in 58 seconds
5277. [bug] Cache DB statistics could underflow when serve-stale
was in use, because of a bug in counter maintenance
when RRsets become stale.
Functions for dumping statistics have been updated
to dump active, stale, and ancient statistic
counters. Ancient RRset counters are prefixed
with '~'; stale RRset counters are still prefixed
with '#'. [GL #602]
5276. [func] DNSSEC Lookaside Validation (DLV) is now obsolete;
all code enabling its use has been removed from the
validator, "delv", and the DNSSEC tools. [GL #7]
......
......@@ -1253,6 +1253,12 @@ rdtypestat_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
#endif
}
static bool
rdatastatstype_attr(dns_rdatastatstype_t type, unsigned int attr)
{
return ((DNS_RDATASTATSTYPE_ATTR(type) & attr) != 0);
}
static void
rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
stats_dumparg_t *dumparg = arg;
......@@ -1261,6 +1267,7 @@ rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
const char *typestr;
bool nxrrset = false;
bool stale = false;
bool ancient = false;
#ifdef HAVE_LIBXML2
void *writer;
int xmlrc;
......@@ -1282,19 +1289,16 @@ rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
typestr = typebuf;
}
if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXRRSET)
!= 0)
nxrrset = true;
if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_STALE)
!= 0)
stale = true;
nxrrset = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_NXRRSET);
stale = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_STALE);
ancient = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_ANCIENT);
switch (dumparg->type) {
case isc_statsformat_file:
fp = dumparg->arg;
fprintf(fp, "%20" PRIu64 " %s%s%s\n", val,
stale ? "#" : "", nxrrset ? "!" : "", typestr);
fprintf(fp, "%20" PRIu64 " %s%s%s%s\n", val,
ancient ? "~" : "", stale ? "#" : "",
nxrrset ? "!" : "", typestr);
break;
case isc_statsformat_xml:
#ifdef HAVE_LIBXML2
......@@ -1302,7 +1306,8 @@ rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
TRY0(xmlTextWriterWriteFormatString(writer, "%s%s%s",
TRY0(xmlTextWriterWriteFormatString(writer, "%s%s%s%s",
ancient ? "~" : "",
stale ? "#" : "",
nxrrset ? "!" : "", typestr));
TRY0(xmlTextWriterEndElement(writer)); /* name */
......@@ -1319,7 +1324,7 @@ rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
case isc_statsformat_json:
#ifdef HAVE_JSON_C
zoneobj = (json_object *) dumparg->arg;
snprintf(buf, sizeof(buf), "%s%s%s",
snprintf(buf, sizeof(buf), "%s%s%s%s", ancient ? "~" : "",
stale ? "#" : "", nxrrset ? "!" : "", typestr);
obj = json_object_new_int64(val);
if (obj == NULL)
......
......@@ -8,10 +8,10 @@
# information regarding copyright ownership.
rm -f dig.out.test*
rm -f ns1/named.conf
rm -f ns3/named.conf
rm -f ns3/root.bk
rm -f ns*/named.conf
rm -f ns*/root.bk
rm -f rndc.out.test*
rm -f */named.run */named.memstats
rm -f ns*/managed-keys.bind*
rm -f ns*/named_dump*
rm -f ns*/named.stats*
......@@ -27,7 +27,7 @@ options {
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion yes;
max-stale-ttl 7200;
max-stale-ttl 35;
stale-answer-ttl 3;
stale-answer-enable yes;
};
......
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
options {
query-source address 10.53.0.4;
notify-source 10.53.0.4;
transfer-source 10.53.0.4;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
recursion yes;
dump-file "named_dump4.db";
stale-answer-enable no;
};
zone "." {
type slave;
masters { 10.53.0.1; };
file "root.bk";
};
......@@ -16,3 +16,4 @@ $SHELL clean.sh
copy_setports ns1/named1.conf.in ns1/named.conf
copy_setports ns3/named.conf.in ns3/named.conf
copy_setports ns4/named.conf.in ns4/named.conf
This diff is collapsed.
......@@ -15227,14 +15227,29 @@ HOST-127.EXAMPLE. MX 0 .
</entry>
<entry colname="2">
<para>
The number of RRsets per RR type and nonexistent
names stored in the cache database.
If the exclamation mark (!) is printed for a RR
type, it means that particular type of RRset is
known to be nonexistent (this is also known as
"NXRRSET"). If a hash mark (#) is present then
the RRset is marked for garbage collection.
Maintained per view.
Statistics counters related to cache contents;
maintained per view.
</para>
<para>
The "NXDOMAIN" counter is the number of names
that have been cached as nonexistent.
Counters named for RR types indicate the
number of active RRsets for each type in the cache
database.
</para>
<para>
If an RR type name is preceded by an exclamation
mark (!), it represents the number of records in the
cache which indicate that the type does not exist
for a particular name (this is also known as "NXRRSET").
If an RR type name is preceded by a hash mark (#), it
represents the number of RRsets for this type that are
present in the cache but whose TTLs have expired; these
RRsets may only be used if stale answers are enabled.
If an RR type name is preceded by a tilde (~), it
represents the number of RRsets for this type that are
present in the cache database but are marked for garbage
collection; these RRsets cannot be used.
</para>
</entry>
</row>
......
......@@ -341,6 +341,16 @@
to root priming queries; this has been corrected. [GL #1092]
</para>
</listitem>
<listitem>
<para>
Cache database statistics counters could report invalid values
when stale answers were enabled, because of a bug in counter
maintenance when cache data becomes stale. The statistics counters
have been corrected to report the number of RRsets for each
RR type that are active, stale but still potentially served,
or stale and marked for deletion. [GL #602]
</para>
</listitem>
</itemizedlist>
</section>
......
......@@ -474,16 +474,18 @@ LIBDNS_EXTERNAL_DATA extern const char *dns_statscounter_names[];
* attribute is set, the base type is of no use.
*
* _STALE
* RRset type counters only. This indicates a record that marked for
* removal.
* RRset type counters only. This indicates a record that is stale
* but may still be served.
*
* Note: incrementing _STALE will decrement the corresponding non-stale
* counter.
* _ANCIENT
* RRset type counters only. This indicates a record that is marked for
* removal.
*/
#define DNS_RDATASTATSTYPE_ATTR_OTHERTYPE 0x0001
#define DNS_RDATASTATSTYPE_ATTR_NXRRSET 0x0002
#define DNS_RDATASTATSTYPE_ATTR_NXDOMAIN 0x0004
#define DNS_RDATASTATSTYPE_ATTR_STALE 0x0008
#define DNS_RDATASTATSTYPE_ATTR_ANCIENT 0x0010
/*%<
* Conversion macros among dns_rdatatype_t, attributes and isc_statscounter_t.
......
......@@ -194,7 +194,7 @@ typedef struct rdatasetheader {
rbtdb_serial_t serial;
dns_ttl_t rdh_ttl;
rbtdb_rdatatype_t type;
uint16_t attributes;
uint16_t attributes;
dns_trust_t trust;
struct noqname *noqname;
struct noqname *closest;
......@@ -813,6 +813,12 @@ update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) {
}
}
static bool
do_stats(rdatasetheader_t *header) {
return (EXISTS(header) &&
(header->attributes & RDATASET_ATTR_STATCOUNT) != 0);
}
static void
update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
bool increment)
......@@ -821,6 +827,10 @@ update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
dns_rdatastatstype_t base = 0;
dns_rdatastatstype_t type;
if (!do_stats(header)) {
return;
}
/* At the moment we count statistics only for cache DB */
INSIST(IS_CACHE(rbtdb));
......@@ -831,17 +841,23 @@ update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
base = RBTDB_RDATATYPE_EXT(header->type);
}
} else
} else {
base = RBTDB_RDATATYPE_BASE(header->type);
}
if (STALE(header))
if (STALE(header)) {
statattributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
}
if (ANCIENT(header)) {
statattributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
}
type = DNS_RDATASTATSTYPE_VALUE(base, statattributes);
if (increment)
if (increment) {
dns_rdatasetstats_increment(rbtdb->rrsetstats, type);
else
} else {
dns_rdatasetstats_decrement(rbtdb->rrsetstats, type);
}
}
static void
......@@ -1441,10 +1457,7 @@ free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset) {
unsigned int size;
int idx;
if (EXISTS(rdataset) &&
(rdataset->attributes & RDATASET_ATTR_STATCOUNT) != 0) {
update_rrsetstats(rbtdb, rdataset, false);
}
update_rrsetstats(rbtdb, rdataset, false);
idx = rdataset->node->locknum;
if (ISC_LINK_LINKED(rdataset, link)) {
......@@ -1507,24 +1520,48 @@ rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
static inline void
mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
/*
* If we are already ancient there is nothing to do.
*/
if (ANCIENT(header))
if (ANCIENT(header)) {
return;
}
/*
* Decrement the stats counter for the appropriate RRtype.
* If the STALE attribute is set, this will decrement the
* stale type counter, otherwise it decrements the active
* stats type counter.
*/
update_rrsetstats(rbtdb, header, false);
header->attributes |= RDATASET_ATTR_ANCIENT;
header->node->dirty = 1;
/* Increment the stats counter for the ancient RRtype. */
update_rrsetstats(rbtdb, header, true);
}
static inline void
mark_header_stale(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
/*
* If we have not been counted then there is nothing to do.
* If we are already stale there is nothing to do.
*/
if ((header->attributes & RDATASET_ATTR_STATCOUNT) == 0)
if (STALE(header)) {
return;
}
/* Decrement the stats counter for the appropriate RRtype.
* If the ANCIENT attribute is set (although it is very
* unlikely that an RRset goes from ANCIENT to STALE), this
* will decrement the ancient stale type counter, otherwise it
* decrements the active stats type counter.
*/
update_rrsetstats(rbtdb, header, false);
header->attributes |= RDATASET_ATTR_STALE;
if (EXISTS(header))
update_rrsetstats(rbtdb, header, true);
update_rrsetstats(rbtdb, header, true);
}
static inline void
......@@ -4320,7 +4357,7 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
* skip this record.
*/
if (KEEPSTALE(search->rbtdb) && stale > search->now) {
header->attributes |= RDATASET_ATTR_STALE;
mark_header_stale(search->rbtdb, header);
*header_prev = header;
return ((search->options & DNS_DBFIND_STALEOK) == 0);
}
......@@ -4328,7 +4365,7 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
/*
* This rdataset is stale. If no one else is using the
* node, we can clean it up right now, otherwise we mark
* it as stale, and the node as dirty, so it will get
* it as ancient, and the node as dirty, so it will get
* cleaned up later.
*/
if ((header->rdh_ttl < search->now - RBTDB_VIRTUAL) &&
......@@ -5289,7 +5326,8 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
mark_header_ancient(rbtdb, header);
if (log)
isc_log_write(dns_lctx, category, module,
level, "overmem cache: stale %s",
level,
"overmem cache: ancient %s",
printname);
} else if (force_expire) {
if (! RETAIN(header)) {
......@@ -5824,7 +5862,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
* which covers all types (NXDOMAIN,
* NODATA(QTYPE=ANY)),
*
* We make all other data stale so that the
* We make all other data ancient so that the
* only rdataset that can be found at this
* node is the negative cache entry.
*/
......@@ -5839,7 +5877,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
}
/*
* Otherwise look for any RRSIGs of the given
* type so they can be marked stale later.
* type so they can be marked ancient later.
*/
for (topheader = rbtnode->data;
topheader != NULL;
......@@ -5851,9 +5889,9 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
/*
* We're adding something that isn't a
* negative cache entry. Look for an extant
* non-stale NXDOMAIN/NODATA(QTYPE=ANY) negative
* non-ancient NXDOMAIN/NODATA(QTYPE=ANY) negative
* cache entry. If we're adding an RRSIG, also
* check for an extant non-stale NODATA ncache
* check for an extant non-ancient NODATA ncache
* entry which covers the same type as the RRSIG.
*/
for (topheader = rbtnode->data;
......@@ -6533,7 +6571,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
* If we're adding a delegation type, adding to the auxiliary NSEC tree,
* or the DB is a cache in an overmem state, hold an exclusive lock on
* the tree. In the latter case the lock does not necessarily have to
* be acquired but it will help purge stale entries more effectively.
* be acquired but it will help purge ancient entries more effectively.
*/
if (IS_CACHE(rbtdb) && isc_mem_isovermem(rbtdb->common.mctx))
cache_is_overmem = true;
......
......@@ -48,22 +48,36 @@ typedef enum {
* Ideally, we should have rdata handle this type of details.
*/
/*
* types, !types, nxdomain, stale types, stale !types, stale nxdomain
* types, !types, nxdomain, stale types, stale !types, stale nxdomain,
* ancient types, ancient !types, ancient nxdomain
*/
enum {
/* For 0-255, we use the rdtype value as counter indices */
rdtypecounter_dlv = 256, /* for dns_rdatatype_dlv */
rdtypecounter_others = 257, /* anything else */
rdtypecounter_max = 258,
/* The following are used for rdataset */
/* The following are used for nxrrset rdataset */
rdtypenxcounter_max = rdtypecounter_max * 2,
/* nxdomain counter */
rdtypecounter_nxdomain = rdtypenxcounter_max,
/* stale counters offset */
rdtypecounter_stale = rdtypecounter_nxdomain + 1,
rdatasettypecounter_max = rdtypecounter_stale * 2,
dnssec_keyid_max = 65535
rdtypecounter_stale_max = rdtypecounter_stale + rdtypecounter_max,
rdtypenxcounter_stale_max = rdtypecounter_stale_max + rdtypecounter_max,
rdtypecounter_stale_nxdomain = rdtypenxcounter_stale_max,
/* ancient counters offset */
rdtypecounter_ancient = rdtypecounter_stale_nxdomain + 1,
rdtypecounter_ancient_max = rdtypecounter_ancient + rdtypecounter_max,
rdtypenxcounter_ancient_max = rdtypecounter_ancient_max +
rdtypecounter_max,
rdtypecounter_ancient_nxdomain = rdtypenxcounter_ancient_max,
/* limit of number counter types */
rdatasettypecounter_max = rdtypecounter_ancient_nxdomain + 1,
};
/* dnssec maximum key id */
static int dnssec_keyid_max = 65535;
struct dns_stats {
unsigned int magic;
dns_statstype_t type;
......@@ -245,17 +259,17 @@ update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
counter += rdtypecounter_max;
}
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0) {
counter += rdtypecounter_ancient;
} else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
DNS_RDATASTATSTYPE_ATTR_STALE) != 0) {
counter += rdtypecounter_stale;
}
if (increment) {
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
DNS_RDATASTATSTYPE_ATTR_STALE) != 0) {
isc_stats_decrement(stats->counters, counter);
counter += rdtypecounter_stale;
}
isc_stats_increment(stats->counters, counter);
} else {
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
counter += rdtypecounter_stale;
isc_stats_decrement(stats->counters, counter);
}
}
......@@ -356,34 +370,46 @@ static void
rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
rdatadumparg_t *rdatadumparg = arg;
unsigned int attributes;
bool dump = true;
if (counter < rdtypecounter_max) {
dump_rdentry(counter, value, 0, rdatadumparg->fn,
rdatadumparg->arg);
} else if (counter < rdtypecounter_nxdomain) {
attributes = 0;
} else if (counter < rdtypenxcounter_max) {
counter -= rdtypecounter_max;
attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
dump_rdentry(counter, value, attributes, rdatadumparg->fn,
rdatadumparg->arg);
} else if (counter == rdtypecounter_nxdomain) {
dump_rdentry(0, value, DNS_RDATASTATSTYPE_ATTR_NXDOMAIN,
rdatadumparg->fn, rdatadumparg->arg);
} else if (counter < rdtypecounter_stale + rdtypecounter_max) {
counter = 0;
attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
} else if (counter < rdtypecounter_stale_max) {
counter -= rdtypecounter_stale;
attributes = DNS_RDATASTATSTYPE_ATTR_STALE;
dump_rdentry(counter, value, attributes, rdatadumparg->fn,
rdatadumparg->arg);
} else if (counter < rdtypecounter_stale + rdtypecounter_nxdomain) {
counter -= rdtypecounter_stale + rdtypecounter_max;
} else if (counter < rdtypenxcounter_stale_max) {
counter -= rdtypecounter_stale_max;
attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET |
DNS_RDATASTATSTYPE_ATTR_STALE;
dump_rdentry(counter, value, attributes, rdatadumparg->fn,
rdatadumparg->arg);
} else {
} else if (counter == rdtypecounter_stale_nxdomain) {
counter = 0;
attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN |
DNS_RDATASTATSTYPE_ATTR_STALE;
dump_rdentry(0, value, attributes, rdatadumparg->fn,
rdatadumparg->arg);
} else if (counter < rdtypecounter_ancient_max) {
counter -= rdtypecounter_ancient;
attributes = DNS_RDATASTATSTYPE_ATTR_ANCIENT;
} else if (counter < rdtypenxcounter_ancient_max) {
counter -= rdtypecounter_ancient_max;
attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET |
DNS_RDATASTATSTYPE_ATTR_ANCIENT;
} else if (counter == rdtypecounter_ancient_nxdomain) {
counter = 0;
attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN |
DNS_RDATASTATSTYPE_ATTR_ANCIENT;
} else {
/* Out of bounds, do not dump entry. */
dump = false;
}
if (dump) {
dump_rdentry(counter, value, attributes, rdatadumparg->fn,
rdatadumparg->arg);
}
}
......
......@@ -54,41 +54,70 @@ _teardown(void **state) {
}
static void
set_typestats(dns_stats_t *stats, dns_rdatatype_t type, bool stale) {
set_typestats(dns_stats_t *stats, dns_rdatatype_t type) {
dns_rdatastatstype_t which;
unsigned int attributes;
attributes = 0;
if (stale) {
attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
}
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_increment(stats, which);
attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
if (stale) {
attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
}
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_increment(stats, which);
}
static void
set_nxdomainstats(dns_stats_t *stats, bool stale) {
set_nxdomainstats(dns_stats_t *stats) {
dns_rdatastatstype_t which;
unsigned int attributes;
attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
if (stale) {
attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
}
which = DNS_RDATASTATSTYPE_VALUE(0, attributes);
dns_rdatasetstats_increment(stats, which);
}
static void
mark_stale(dns_stats_t *stats, dns_rdatatype_t type, int from, int to)
{
dns_rdatastatstype_t which;
unsigned int attributes;
attributes = from;
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_decrement(stats, which);
attributes |= to;
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_increment(stats, which);
attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET | from;
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_decrement(stats, which);
attributes |= to;
which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
dns_rdatasetstats_increment(stats, which);
}
static void
mark_nxdomain_stale(dns_stats_t *stats, int from, int to)
{
dns_rdatastatstype_t which;
unsigned int attributes;
attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN | from;
which = DNS_RDATASTATSTYPE_VALUE(0, attributes);
dns_rdatasetstats_decrement(stats, which);
attributes |= to;
which = DNS_RDATASTATSTYPE_VALUE(0, attributes);
dns_rdatasetstats_increment(stats, which);
}
#define ATTRIBUTE_SET(y) ((attributes & (y)) != 0)
static void
checkit1(dns_rdatastatstype_t which, uint64_t value, void *arg) {
verify_active_counters(dns_rdatastatstype_t which, uint64_t value, void *arg) {
unsigned int