mirror of
https://github.com/isc-projects/bind9.git
synced 2026-04-24 07:41:10 -04:00
Refactor dns_badcache to use cds_lfht lock-free hashtable
The dns_badcache unit had (yet another) own locked hashtable implementation. Replace the hashtable used by dns_badcache with lock-free cds_lfht implementation from liburcu.
This commit is contained in:
parent
c4bc43c8a7
commit
4dacdde28f
12 changed files with 629 additions and 406 deletions
|
|
@ -236,7 +236,7 @@ AC_MSG_RESULT([$RCU_FLAVOR])
|
|||
# not add it automatically - we need to add it explicitly in such case.
|
||||
#
|
||||
PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.13.0 liburcu-cds >= 0.13.0], [],
|
||||
[PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR liburcu-cds],
|
||||
[PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.10.0 liburcu-cds >= 0.10.0],
|
||||
[LIBURCU_LIBS="$LIBURCU_LIBS -lurcu-common"])])
|
||||
|
||||
AC_DEFINE_UNQUOTED([RCU_FLAVOR], ["$RCU_FLAVOR"], [Chosen Userspace-RCU flavor])
|
||||
|
|
|
|||
|
|
@ -22,9 +22,14 @@
|
|||
#include <isc/log.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/rwlock.h>
|
||||
#include <isc/spinlock.h>
|
||||
#include <isc/stdtime.h>
|
||||
#include <isc/string.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/time.h>
|
||||
#include <isc/urcu.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/badcache.h>
|
||||
|
|
@ -37,476 +42,353 @@ typedef struct dns_bcentry dns_bcentry_t;
|
|||
|
||||
struct dns_badcache {
|
||||
unsigned int magic;
|
||||
isc_rwlock_t lock;
|
||||
isc_mem_t *mctx;
|
||||
|
||||
isc_mutex_t *tlocks;
|
||||
dns_bcentry_t **table;
|
||||
|
||||
atomic_uint_fast32_t count;
|
||||
atomic_uint_fast32_t sweep;
|
||||
|
||||
unsigned int minsize;
|
||||
unsigned int size;
|
||||
struct cds_lfht *ht;
|
||||
atomic_bool purge_in_progress;
|
||||
};
|
||||
|
||||
#define BADCACHE_MAGIC ISC_MAGIC('B', 'd', 'C', 'a')
|
||||
#define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
|
||||
|
||||
#define BADCACHE_INIT_SIZE (1 << 10) /* Must be power of 2 */
|
||||
#define BADCACHE_MIN_SIZE (1 << 8) /* Must be power of 2 */
|
||||
|
||||
struct dns_bcentry {
|
||||
dns_bcentry_t *next;
|
||||
isc_mem_t *mctx;
|
||||
dns_rdatatype_t type;
|
||||
isc_time_t expire;
|
||||
uint32_t flags;
|
||||
unsigned int hashval;
|
||||
_Atomic(isc_stdtime_t) expire;
|
||||
atomic_uint_fast32_t flags;
|
||||
dns_fixedname_t fname;
|
||||
dns_name_t *name;
|
||||
|
||||
struct cds_lfht_node ht_node;
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
static void
|
||||
badcache_resize(dns_badcache_t *bc, isc_time_t *now);
|
||||
bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp);
|
||||
|
||||
isc_result_t
|
||||
dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
unsigned int i;
|
||||
static void
|
||||
bcentry_destroy(struct rcu_head *rcu_head);
|
||||
|
||||
REQUIRE(bcp != NULL && *bcp == NULL);
|
||||
dns_badcache_t *
|
||||
dns_badcache_new(isc_mem_t *mctx) {
|
||||
REQUIRE(mctx != NULL);
|
||||
|
||||
bc = isc_mem_get(mctx, sizeof(*bc));
|
||||
|
||||
dns_badcache_t *bc = isc_mem_get(mctx, sizeof(*bc));
|
||||
*bc = (dns_badcache_t){
|
||||
.size = size,
|
||||
.minsize = size,
|
||||
.magic = BADCACHE_MAGIC,
|
||||
};
|
||||
|
||||
bc->ht = cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
|
||||
CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
|
||||
INSIST(bc->ht != NULL);
|
||||
|
||||
isc_mem_attach(mctx, &bc->mctx);
|
||||
isc_rwlock_init(&bc->lock);
|
||||
|
||||
bc->table = isc_mem_getx(bc->mctx, sizeof(bc->table[0]) * size,
|
||||
ISC_MEM_ZERO);
|
||||
bc->tlocks = isc_mem_getx(bc->mctx, sizeof(bc->tlocks[0]) * size,
|
||||
ISC_MEM_ZERO);
|
||||
for (i = 0; i < size; i++) {
|
||||
isc_mutex_init(&bc->tlocks[i]);
|
||||
}
|
||||
|
||||
bc->magic = BADCACHE_MAGIC;
|
||||
|
||||
*bcp = bc;
|
||||
return (ISC_R_SUCCESS);
|
||||
return (bc);
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_destroy(dns_badcache_t **bcp) {
|
||||
dns_badcache_t *bc;
|
||||
unsigned int i;
|
||||
|
||||
REQUIRE(bcp != NULL && *bcp != NULL);
|
||||
bc = *bcp;
|
||||
REQUIRE(VALID_BADCACHE(*bcp));
|
||||
|
||||
dns_badcache_t *bc = *bcp;
|
||||
*bcp = NULL;
|
||||
|
||||
dns_badcache_flush(bc);
|
||||
|
||||
bc->magic = 0;
|
||||
isc_rwlock_destroy(&bc->lock);
|
||||
for (i = 0; i < bc->size; i++) {
|
||||
isc_mutex_destroy(&bc->tlocks[i]);
|
||||
|
||||
dns_bcentry_t *bad = NULL;
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(bc->ht, &iter, bad, ht_node) {
|
||||
INSIST(!cds_lfht_del(bc->ht, &bad->ht_node));
|
||||
bcentry_destroy(&bad->rcu_head);
|
||||
}
|
||||
isc_mem_put(bc->mctx, bc->table, sizeof(bc->table[0]) * bc->size);
|
||||
isc_mem_put(bc->mctx, bc->tlocks, sizeof(bc->tlocks[0]) * bc->size);
|
||||
RUNTIME_CHECK(!cds_lfht_destroy(bc->ht, NULL));
|
||||
|
||||
isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
|
||||
}
|
||||
|
||||
static int
|
||||
bcentry_match(struct cds_lfht_node *ht_node, const void *key) {
|
||||
const dns_name_t *name = key;
|
||||
dns_bcentry_t *bad = caa_container_of(ht_node, dns_bcentry_t, ht_node);
|
||||
|
||||
return (dns_name_equal(bad->name, name));
|
||||
}
|
||||
|
||||
static dns_bcentry_t *
|
||||
bcentry_new(dns_badcache_t *bc, const dns_name_t *name,
|
||||
const dns_rdatatype_t type, const uint32_t flags,
|
||||
const isc_stdtime_t expire) {
|
||||
dns_bcentry_t *bad = isc_mem_get(bc->mctx, sizeof(*bad));
|
||||
*bad = (dns_bcentry_t){
|
||||
.type = type,
|
||||
.flags = flags,
|
||||
.expire = expire,
|
||||
};
|
||||
isc_mem_attach(bc->mctx, &bad->mctx);
|
||||
|
||||
bad->name = dns_fixedname_initname(&bad->fname);
|
||||
dns_name_copy(name, bad->name);
|
||||
|
||||
return (bad);
|
||||
}
|
||||
|
||||
static void
|
||||
badcache_resize(dns_badcache_t *bc, isc_time_t *now) {
|
||||
dns_bcentry_t **newtable, *bad, *next;
|
||||
isc_mutex_t *newlocks;
|
||||
unsigned int newsize, i;
|
||||
bool grow;
|
||||
bcentry_destroy(struct rcu_head *rcu_head) {
|
||||
dns_bcentry_t *bad = caa_container_of(rcu_head, dns_bcentry_t,
|
||||
rcu_head);
|
||||
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
isc_mem_putanddetach(&bad->mctx, bad, sizeof(*bad));
|
||||
}
|
||||
|
||||
static void
|
||||
bcentry_evict(struct cds_lfht *ht, dns_bcentry_t *bad) {
|
||||
/*
|
||||
* XXXWPK we will have a thundering herd problem here,
|
||||
* as all threads will wait on the RWLOCK when there's
|
||||
* a need to resize badcache.
|
||||
* However, it happens so rarely it should not be a
|
||||
* performance issue. This is because we double the
|
||||
* size every time we grow it, and we don't shrink
|
||||
* unless the number of entries really shrunk. In a
|
||||
* high load situation, the number of badcache entries
|
||||
* will eventually stabilize.
|
||||
* The hashtable isn't locked in a traditional sense, so multiple
|
||||
* threads can lookup and evict the same record at the same time.
|
||||
*
|
||||
* This is amplified by the bcentry_purge_next() that walks a few more
|
||||
* records in the hashtable and evicts them if they are expired.
|
||||
*
|
||||
* We need to destroy the bcentry only once - from the thread that has
|
||||
* deleted the entry from the hashtable, all other calls to this
|
||||
* function were redundant.
|
||||
*/
|
||||
if (atomic_load_relaxed(&bc->count) > bc->size * 8) {
|
||||
grow = true;
|
||||
} else if (atomic_load_relaxed(&bc->count) < bc->size * 2 &&
|
||||
bc->size > bc->minsize)
|
||||
{
|
||||
grow = false;
|
||||
} else {
|
||||
/* Someone resized it already, bail. */
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
return;
|
||||
if (!cds_lfht_del(ht, &bad->ht_node)) {
|
||||
call_rcu(&bad->rcu_head, bcentry_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now) {
|
||||
if (cds_lfht_is_node_deleted(&bad->ht_node)) {
|
||||
return (false);
|
||||
} else if (atomic_load_relaxed(&bad->expire) < now) {
|
||||
bcentry_evict(ht, bad);
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (grow) {
|
||||
newsize = bc->size * 2 + 1;
|
||||
} else {
|
||||
newsize = (bc->size - 1) / 2;
|
||||
#ifdef __clang_analyzer__
|
||||
/*
|
||||
* XXXWPK there's a bug in clang static analyzer -
|
||||
* `value % newsize` is considered undefined even though
|
||||
* we check if newsize is larger than 0. This helps.
|
||||
*/
|
||||
newsize += 1;
|
||||
#endif
|
||||
}
|
||||
RUNTIME_CHECK(newsize > 0);
|
||||
return (true);
|
||||
}
|
||||
|
||||
newtable = isc_mem_getx(bc->mctx, sizeof(dns_bcentry_t *) * newsize,
|
||||
ISC_MEM_ZERO);
|
||||
#define cds_lfht_for_each_entry_next(ht, iter, pos, member) \
|
||||
for (cds_lfht_next(ht, iter), \
|
||||
pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
|
||||
__typeof__(*(pos)), member); \
|
||||
pos != NULL; /**/ \
|
||||
cds_lfht_next(ht, iter), \
|
||||
pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
|
||||
__typeof__(*(pos)), member))
|
||||
|
||||
newlocks = isc_mem_get(bc->mctx, sizeof(isc_mutex_t) * newsize);
|
||||
|
||||
/* Copy existing mutexes */
|
||||
for (i = 0; i < newsize && i < bc->size; i++) {
|
||||
newlocks[i] = bc->tlocks[i];
|
||||
}
|
||||
/* Initialize additional mutexes if we're growing */
|
||||
for (i = bc->size; i < newsize; i++) {
|
||||
isc_mutex_init(&newlocks[i]);
|
||||
}
|
||||
/* Destroy extra mutexes if we're shrinking */
|
||||
for (i = newsize; i < bc->size; i++) {
|
||||
isc_mutex_destroy(&bc->tlocks[i]);
|
||||
}
|
||||
|
||||
for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
|
||||
for (bad = bc->table[i]; bad != NULL; bad = next) {
|
||||
next = bad->next;
|
||||
if (isc_time_compare(&bad->expire, now) < 0) {
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
} else {
|
||||
bad->next = newtable[bad->hashval % newsize];
|
||||
newtable[bad->hashval % newsize] = bad;
|
||||
}
|
||||
static void
|
||||
bcentry_purge_next(struct cds_lfht *ht, struct cds_lfht_iter *iter,
|
||||
isc_stdtime_t now) {
|
||||
/* Lazy-purge the table */
|
||||
size_t count = 10;
|
||||
dns_bcentry_t *bad;
|
||||
cds_lfht_for_each_entry_next(ht, iter, bad, ht_node) {
|
||||
if (!bcentry_alive(ht, bad, now)) {
|
||||
break;
|
||||
}
|
||||
if (--count == 0) {
|
||||
break;
|
||||
}
|
||||
bc->table[i] = NULL;
|
||||
}
|
||||
|
||||
isc_mem_put(bc->mctx, bc->tlocks, sizeof(isc_mutex_t) * bc->size);
|
||||
bc->tlocks = newlocks;
|
||||
|
||||
isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size);
|
||||
bc->size = newsize;
|
||||
bc->table = newtable;
|
||||
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
|
||||
dns_rdatatype_t type, bool update, uint32_t flags,
|
||||
isc_time_t *expire) {
|
||||
unsigned int hashval, hash;
|
||||
dns_bcentry_t *bad, *prev, *next;
|
||||
isc_time_t now;
|
||||
bool resize = false;
|
||||
|
||||
isc_stdtime_t expire) {
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(expire != NULL);
|
||||
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
if (expire < now) {
|
||||
expire = now;
|
||||
}
|
||||
|
||||
now = isc_time_now();
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(bc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
hashval = dns_name_hash(name);
|
||||
hash = hashval % bc->size;
|
||||
LOCK(&bc->tlocks[hash]);
|
||||
prev = NULL;
|
||||
for (bad = bc->table[hash]; bad != NULL; bad = next) {
|
||||
next = bad->next;
|
||||
if (bad->type == type && dns_name_equal(name, bad->name)) {
|
||||
if (update) {
|
||||
bad->expire = *expire;
|
||||
bad->flags = flags;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isc_time_compare(&bad->expire, &now) < 0) {
|
||||
if (prev == NULL) {
|
||||
bc->table[hash] = bad->next;
|
||||
} else {
|
||||
prev->next = bad->next;
|
||||
}
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
} else {
|
||||
prev = bad;
|
||||
dns_bcentry_t *bad = NULL;
|
||||
uint32_t hashval = dns_name_hash(name);
|
||||
|
||||
struct cds_lfht_iter iter;
|
||||
dns_bcentry_t *found = NULL;
|
||||
cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
|
||||
&iter, bad, ht_node) {
|
||||
if (bcentry_alive(ht, bad, now) && bad->type == type) {
|
||||
found = bad;
|
||||
/*
|
||||
* We could bail-out on first match, but:
|
||||
* 1. there could be duplicate .type entries
|
||||
* 2. we want to check expire for all entries
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (bad == NULL) {
|
||||
unsigned int count;
|
||||
isc_buffer_t buffer;
|
||||
|
||||
bad = isc_mem_get(bc->mctx, sizeof(*bad));
|
||||
*bad = (dns_bcentry_t){ .type = type,
|
||||
.hashval = hashval,
|
||||
.expire = *expire,
|
||||
.flags = flags,
|
||||
.next = bc->table[hash] };
|
||||
|
||||
isc_buffer_init(&buffer, bad + 1, name->length);
|
||||
bad->name = dns_fixedname_initname(&bad->fname);
|
||||
dns_name_copy(name, bad->name);
|
||||
bc->table[hash] = bad;
|
||||
|
||||
count = atomic_fetch_add_relaxed(&bc->count, 1);
|
||||
if ((count > bc->size * 8) ||
|
||||
(count < bc->size * 2 && bc->size > bc->minsize))
|
||||
{
|
||||
resize = true;
|
||||
}
|
||||
} else {
|
||||
bad->expire = *expire;
|
||||
if (found == NULL) {
|
||||
/*
|
||||
* In theory, this could result in multiple entries for the same
|
||||
* type, but we don't care much, as they are going to be roughly
|
||||
* the same, and the last will always trump and the former
|
||||
* entries will expire (see above).
|
||||
*/
|
||||
bad = bcentry_new(bc, name, type, flags, expire);
|
||||
cds_lfht_add(ht, hashval, &bad->ht_node);
|
||||
} else if (update) {
|
||||
atomic_store_relaxed(&found->expire, expire);
|
||||
atomic_store_relaxed(&found->flags, flags);
|
||||
}
|
||||
|
||||
UNLOCK(&bc->tlocks[hash]);
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
if (resize) {
|
||||
badcache_resize(bc, &now);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
bool
|
||||
isc_result_t
|
||||
dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
|
||||
dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now) {
|
||||
dns_bcentry_t *bad, *prev, *next;
|
||||
bool answer = false;
|
||||
unsigned int i;
|
||||
unsigned int hash;
|
||||
|
||||
dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now) {
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
REQUIRE(name != NULL);
|
||||
REQUIRE(now != NULL);
|
||||
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
isc_result_t result = ISC_R_NOTFOUND;
|
||||
|
||||
/*
|
||||
* XXXMUKS: dns_name_equal() is expensive as it does a
|
||||
* octet-by-octet comparison, and it can be made better in two
|
||||
* ways here. First, lowercase the names (use
|
||||
* dns_name_downcase() instead of dns_name_copy() in
|
||||
* dns_badcache_add()) so that dns_name_caseequal() can be used
|
||||
* which the compiler will emit as SIMD instructions. Second,
|
||||
* don't put multiple copies of the same name in the chain (or
|
||||
* multiple names will have to be matched for equality), but use
|
||||
* name->link to store the type specific part.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(bc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
if (atomic_load_relaxed(&bc->count) == 0) {
|
||||
goto skip;
|
||||
dns_bcentry_t *bad = NULL;
|
||||
uint32_t hashval = dns_name_hash(name);
|
||||
|
||||
struct cds_lfht_iter iter;
|
||||
dns_bcentry_t *found = NULL;
|
||||
cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
|
||||
&iter, bad, ht_node) {
|
||||
if (bad->type == type && bcentry_alive(ht, bad, now)) {
|
||||
found = bad;
|
||||
}
|
||||
}
|
||||
|
||||
hash = dns_name_hash(name) % bc->size;
|
||||
prev = NULL;
|
||||
LOCK(&bc->tlocks[hash]);
|
||||
for (bad = bc->table[hash]; bad != NULL; bad = next) {
|
||||
next = bad->next;
|
||||
/*
|
||||
* Search the hash list. Clean out expired records as we go.
|
||||
*/
|
||||
if (isc_time_compare(&bad->expire, now) < 0) {
|
||||
if (prev != NULL) {
|
||||
prev->next = bad->next;
|
||||
} else {
|
||||
bc->table[hash] = bad->next;
|
||||
}
|
||||
if (found) {
|
||||
result = ISC_R_SUCCESS;
|
||||
if (flagp != NULL) {
|
||||
*flagp = atomic_load_relaxed(&found->flags);
|
||||
}
|
||||
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub(&bc->count, 1);
|
||||
continue;
|
||||
}
|
||||
if (bad->type == type && dns_name_equal(name, bad->name)) {
|
||||
if (flagp != NULL) {
|
||||
*flagp = bad->flags;
|
||||
}
|
||||
answer = true;
|
||||
break;
|
||||
}
|
||||
prev = bad;
|
||||
}
|
||||
UNLOCK(&bc->tlocks[hash]);
|
||||
skip:
|
||||
|
||||
/*
|
||||
* Slow sweep to clean out stale records.
|
||||
*/
|
||||
i = atomic_fetch_add(&bc->sweep, 1) % bc->size;
|
||||
if (isc_mutex_trylock(&bc->tlocks[i]) == ISC_R_SUCCESS) {
|
||||
bad = bc->table[i];
|
||||
if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
|
||||
bc->table[i] = bad->next;
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
}
|
||||
UNLOCK(&bc->tlocks[i]);
|
||||
bcentry_purge_next(ht, &iter, now);
|
||||
}
|
||||
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
return (answer);
|
||||
rcu_read_unlock();
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_flush(dns_badcache_t *bc) {
|
||||
dns_bcentry_t *entry, *next;
|
||||
unsigned int i;
|
||||
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
|
||||
for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
|
||||
for (entry = bc->table[i]; entry != NULL; entry = next) {
|
||||
next = entry->next;
|
||||
isc_mem_put(bc->mctx, entry, sizeof(*entry));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
}
|
||||
bc->table[i] = NULL;
|
||||
struct cds_lfht *ht =
|
||||
cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
|
||||
CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
/* First swap the hashtables */
|
||||
rcu_read_lock();
|
||||
ht = rcu_xchg_pointer(&bc->ht, ht);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Make sure nobody is using the old hash table */
|
||||
synchronize_rcu();
|
||||
|
||||
/* Flush the old hash table */
|
||||
dns_bcentry_t *bad = NULL;
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
|
||||
INSIST(!cds_lfht_del(ht, &bad->ht_node));
|
||||
bcentry_destroy(&bad->rcu_head);
|
||||
}
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
RUNTIME_CHECK(!cds_lfht_destroy(ht, NULL));
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) {
|
||||
dns_bcentry_t *bad, *prev, *next;
|
||||
isc_time_t now;
|
||||
unsigned int hash;
|
||||
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
REQUIRE(name != NULL);
|
||||
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(bc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
now = isc_time_now();
|
||||
hash = dns_name_hash(name) % bc->size;
|
||||
LOCK(&bc->tlocks[hash]);
|
||||
prev = NULL;
|
||||
for (bad = bc->table[hash]; bad != NULL; bad = next) {
|
||||
int n;
|
||||
next = bad->next;
|
||||
n = isc_time_compare(&bad->expire, &now);
|
||||
if (n < 0 || dns_name_equal(name, bad->name)) {
|
||||
if (prev == NULL) {
|
||||
bc->table[hash] = bad->next;
|
||||
} else {
|
||||
prev->next = bad->next;
|
||||
}
|
||||
dns_bcentry_t *bad = NULL;
|
||||
uint32_t hashval = dns_name_hash(name);
|
||||
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
} else {
|
||||
prev = bad;
|
||||
}
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name,
|
||||
&iter, bad, ht_node) {
|
||||
bcentry_evict(ht, bad);
|
||||
}
|
||||
UNLOCK(&bc->tlocks[hash]);
|
||||
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_read);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) {
|
||||
dns_bcentry_t *bad, *prev, *next;
|
||||
unsigned int i;
|
||||
int n;
|
||||
isc_time_t now;
|
||||
dns_bcentry_t *bad;
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
REQUIRE(name != NULL);
|
||||
|
||||
/*
|
||||
* We write lock the tree to avoid relocking every node
|
||||
* individually.
|
||||
*/
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(bc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
now = isc_time_now();
|
||||
|
||||
for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
|
||||
prev = NULL;
|
||||
for (bad = bc->table[i]; bad != NULL; bad = next) {
|
||||
next = bad->next;
|
||||
n = isc_time_compare(&bad->expire, &now);
|
||||
if (n < 0 || dns_name_issubdomain(bad->name, name)) {
|
||||
if (prev == NULL) {
|
||||
bc->table[i] = bad->next;
|
||||
} else {
|
||||
prev->next = bad->next;
|
||||
}
|
||||
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
} else {
|
||||
prev = bad;
|
||||
}
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
|
||||
if (dns_name_issubdomain(bad->name, name)) {
|
||||
bcentry_evict(ht, bad);
|
||||
} else if (!bcentry_alive(ht, bad, now)) {
|
||||
/* Flush all the expired entries */
|
||||
}
|
||||
}
|
||||
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void
|
||||
bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
|
||||
dns_name_format(bad->name, namebuf, sizeof(namebuf));
|
||||
dns_rdatatype_format(bad->type, typebuf, sizeof(typebuf));
|
||||
fprintf(fp, "; %s/%s [ttl %" PRIu32 "]\n", namebuf, typebuf,
|
||||
atomic_load_relaxed(&bad->expire) - now);
|
||||
}
|
||||
|
||||
void
|
||||
dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
dns_bcentry_t *bad, *next, *prev;
|
||||
isc_time_t now;
|
||||
unsigned int i;
|
||||
uint64_t t;
|
||||
dns_bcentry_t *bad;
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
|
||||
REQUIRE(VALID_BADCACHE(bc));
|
||||
REQUIRE(cachename != NULL);
|
||||
REQUIRE(fp != NULL);
|
||||
|
||||
/*
|
||||
* We write lock the tree to avoid relocking every node
|
||||
* individually.
|
||||
*/
|
||||
RWLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
fprintf(fp, ";\n; %s\n;\n", cachename);
|
||||
|
||||
now = isc_time_now();
|
||||
for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) {
|
||||
prev = NULL;
|
||||
for (bad = bc->table[i]; bad != NULL; bad = next) {
|
||||
next = bad->next;
|
||||
if (isc_time_compare(&bad->expire, &now) < 0) {
|
||||
if (prev != NULL) {
|
||||
prev->next = bad->next;
|
||||
} else {
|
||||
bc->table[i] = bad->next;
|
||||
}
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(bc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
isc_mem_put(bc->mctx, bad, sizeof(*bad));
|
||||
atomic_fetch_sub_relaxed(&bc->count, 1);
|
||||
continue;
|
||||
}
|
||||
prev = bad;
|
||||
dns_name_format(bad->name, namebuf, sizeof(namebuf));
|
||||
dns_rdatatype_format(bad->type, typebuf,
|
||||
sizeof(typebuf));
|
||||
t = isc_time_microdiff(&bad->expire, &now);
|
||||
t /= 1000;
|
||||
fprintf(fp,
|
||||
"; %s/%s [ttl "
|
||||
"%" PRIu64 "]\n",
|
||||
namebuf, typebuf, t);
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
|
||||
if (bcentry_alive(ht, bad, now)) {
|
||||
bcentry_print(bad, now, fp);
|
||||
}
|
||||
}
|
||||
RWUNLOCK(&bc->lock, isc_rwlocktype_write);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/mem.h>
|
||||
#include <isc/stdtime.h>
|
||||
|
||||
#include <dns/types.h>
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
|
@ -53,8 +56,8 @@ ISC_LANG_BEGINDECLS
|
|||
*** Functions
|
||||
***/
|
||||
|
||||
isc_result_t
|
||||
dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp);
|
||||
dns_badcache_t *
|
||||
dns_badcache_new(isc_mem_t *mctx);
|
||||
/*%
|
||||
* Allocate and initialize a badcache and store it in '*bcp'.
|
||||
*
|
||||
|
|
@ -76,7 +79,7 @@ dns_badcache_destroy(dns_badcache_t **bcp);
|
|||
void
|
||||
dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
|
||||
dns_rdatatype_t type, bool update, uint32_t flags,
|
||||
isc_time_t *expire);
|
||||
isc_stdtime_t expire);
|
||||
/*%
|
||||
* Adds a badcache entry to the badcache 'bc' for name 'name' and
|
||||
* type 'type'. If an entry already exists, then it will be updated if
|
||||
|
|
@ -89,14 +92,14 @@ dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
|
|||
* \li expire != NULL
|
||||
*/
|
||||
|
||||
bool
|
||||
isc_result_t
|
||||
dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
|
||||
dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now);
|
||||
dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now);
|
||||
/*%
|
||||
* Returns true if a record is found in the badcache 'bc' matching
|
||||
* Returns ISC_R_SUCCESS if a record is found in the badcache 'bc' matching
|
||||
* 'name' and 'type', with an expiration date later than 'now'.
|
||||
* If 'flagp' is not NULL, then '*flagp' is updated to the flags
|
||||
* that were stored in the badcache entry. Returns false if
|
||||
* that were stored in the badcache entry. Returns ISC_R_NOTFOUND if
|
||||
* no matching record is found.
|
||||
*
|
||||
* Requires:
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name,
|
|||
* \li name to be valid.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc_result_t
|
||||
dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name,
|
||||
dns_rdatatype_t type, isc_time_t *now);
|
||||
/*%<
|
||||
|
|
|
|||
|
|
@ -236,7 +236,6 @@ STATIC_ASSERT(NS_PROCESSING_LIMIT > NS_RR_LIMIT,
|
|||
*/
|
||||
#define MAX_EDNS0_TIMEOUTS 3
|
||||
|
||||
#define DNS_RESOLVER_BADCACHESIZE 1021
|
||||
#define DNS_RESOLVER_BADCACHETTL(fctx) \
|
||||
(((fctx)->res->lame_ttl > 30) ? (fctx)->res->lame_ttl : 30)
|
||||
|
||||
|
|
@ -9906,7 +9905,6 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr,
|
|||
isc_tlsctx_cache_t *tlsctx_cache,
|
||||
dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
|
||||
dns_resolver_t **resp) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
dns_resolver_t *res = NULL;
|
||||
|
||||
/*
|
||||
|
|
@ -9951,11 +9949,7 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr,
|
|||
#endif
|
||||
isc_refcount_init(&res->references, 1);
|
||||
|
||||
result = dns_badcache_init(res->mctx, DNS_RESOLVER_BADCACHESIZE,
|
||||
&res->badcache);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
res->badcache = dns_badcache_new(res->mctx);
|
||||
|
||||
/* This needs to be case sensitive to not lowercase options and type */
|
||||
isc_hashmap_create(view->mctx, RES_DOMAIN_HASH_BITS,
|
||||
|
|
@ -9984,11 +9978,6 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr,
|
|||
*resp = res;
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
cleanup:
|
||||
dns_view_weakdetach(&res->view);
|
||||
isc_mem_putanddetach(&res->mctx, res, sizeof(*res));
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -10730,14 +10719,15 @@ dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name,
|
|||
#endif /* ifdef ENABLE_AFL */
|
||||
{
|
||||
dns_badcache_add(resolver->badcache, name, type, false, 0,
|
||||
expire);
|
||||
isc_time_seconds(expire));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
isc_result_t
|
||||
dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name,
|
||||
dns_rdatatype_t type, isc_time_t *now) {
|
||||
return (dns_badcache_find(resolver->badcache, name, type, NULL, now));
|
||||
return (dns_badcache_find(resolver->badcache, name, type, NULL,
|
||||
isc_time_seconds(now)));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -857,7 +857,9 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
|
|||
|
||||
disassociate_rdatasets(val);
|
||||
|
||||
if (dns_resolver_getbadcache(val->view->resolver, name, type, &now)) {
|
||||
result = dns_resolver_getbadcache(val->view->resolver, name, type,
|
||||
&now);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
dns_name_format(name, namebuf, sizeof(namebuf));
|
||||
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
|
||||
validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)",
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@
|
|||
goto cleanup; \
|
||||
} while (0)
|
||||
|
||||
#define DNS_VIEW_DELONLYHASH 111
|
||||
#define DNS_VIEW_FAILCACHESIZE 1021
|
||||
#define DNS_VIEW_DELONLYHASH 111
|
||||
|
||||
/*%
|
||||
* Default EDNS0 buffer size
|
||||
|
|
@ -146,11 +145,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, const char *name,
|
|||
|
||||
dns_tsigkeyring_create(view->mctx, &view->dynamickeys);
|
||||
|
||||
result = dns_badcache_init(view->mctx, DNS_VIEW_FAILCACHESIZE,
|
||||
&view->failcache);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup_dynkeys;
|
||||
}
|
||||
view->failcache = dns_badcache_new(view->mctx);
|
||||
|
||||
isc_mutex_init(&view->new_zone_lock);
|
||||
|
||||
|
|
@ -188,7 +183,6 @@ cleanup_new_zone_lock:
|
|||
isc_mutex_destroy(&view->new_zone_lock);
|
||||
dns_badcache_destroy(&view->failcache);
|
||||
|
||||
cleanup_dynkeys:
|
||||
if (view->dynamickeys != NULL) {
|
||||
dns_tsigkeyring_detach(&view->dynamickeys);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -943,9 +943,10 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
|
|||
isc_interval_set(&i, client->view->fail_ttl, 0);
|
||||
result = isc_time_nowplusinterval(&expire, &i);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
dns_badcache_add(
|
||||
client->view->failcache, client->query.qname,
|
||||
client->query.qtype, true, flags, &expire);
|
||||
dns_badcache_add(client->view->failcache,
|
||||
client->query.qname,
|
||||
client->query.qtype, true, flags,
|
||||
isc_time_seconds(&expire));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7000,7 +7000,7 @@ cleanup:
|
|||
*/
|
||||
isc_result_t
|
||||
ns__query_sfcache(query_ctx_t *qctx) {
|
||||
bool failcache;
|
||||
isc_result_t failcache;
|
||||
uint32_t flags;
|
||||
|
||||
/*
|
||||
|
|
@ -7013,20 +7013,22 @@ ns__query_sfcache(query_ctx_t *qctx) {
|
|||
flags = 0;
|
||||
#ifdef ENABLE_AFL
|
||||
if (qctx->client->manager->sctx->fuzztype == isc_fuzz_resolver) {
|
||||
failcache = false;
|
||||
} else {
|
||||
failcache = ISC_R_NOTFOUND;
|
||||
} else
|
||||
#endif /* ifdef ENABLE_AFL */
|
||||
{
|
||||
failcache = dns_badcache_find(
|
||||
qctx->view->failcache, qctx->client->query.qname,
|
||||
qctx->qtype, &flags, &qctx->client->tnow);
|
||||
qctx->qtype, &flags,
|
||||
isc_time_seconds(&qctx->client->tnow));
|
||||
}
|
||||
#else /* ifdef ENABLE_AFL */
|
||||
failcache = dns_badcache_find(qctx->view->failcache,
|
||||
qctx->client->query.qname, qctx->qtype,
|
||||
&flags, &qctx->client->tnow);
|
||||
#endif /* ifdef ENABLE_AFL */
|
||||
if (failcache &&
|
||||
(((flags & NS_FAILCACHE_CD) != 0) ||
|
||||
((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0)))
|
||||
|
||||
if (failcache != ISC_R_SUCCESS) {
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
if (((flags & NS_FAILCACHE_CD) != 0) ||
|
||||
((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0))
|
||||
{
|
||||
if (isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ LDADD += \
|
|||
|
||||
check_PROGRAMS = \
|
||||
acl_test \
|
||||
badcache_test \
|
||||
db_test \
|
||||
dbdiff_test \
|
||||
dbiterator_test \
|
||||
|
|
|
|||
347
tests/dns/badcache_test.c
Normal file
347
tests/dns/badcache_test.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sched.h> /* IWYU pragma: keep */
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define UNIT_TESTING
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/commandline.h>
|
||||
#include <isc/md.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/urcu.h>
|
||||
#include <isc/util.h>
|
||||
#include <isc/uv.h>
|
||||
|
||||
#include <dns/badcache.h>
|
||||
#include <dns/compress.h>
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdatatype.h>
|
||||
|
||||
#include <tests/dns.h>
|
||||
|
||||
static uint32_t
|
||||
crc32(const uint8_t *buf, size_t size) {
|
||||
uint32_t crc;
|
||||
crc = 0xFFFFFFFF;
|
||||
while (size--) {
|
||||
crc = crc ^ *buf++;
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
uint32_t mask = -(crc & 1);
|
||||
crc = (crc >> 1) ^ (0xedb88320 & mask);
|
||||
}
|
||||
}
|
||||
return (~crc);
|
||||
}
|
||||
|
||||
#define BADCACHE_TEST_FLAG 1 << 3
|
||||
|
||||
ISC_LOOP_TEST_IMPL(basic) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
|
||||
flags = 0;
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
flags = 0;
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
assert_int_equal(flags, 0);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(expire) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, now + 60);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now + 61);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_badcache_add(bc, name, dns_rdatatype_a, true, flags, now + 120);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now + 61);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(print) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_stdtime_t expire = now + 60;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
FILE *file = NULL;
|
||||
uint8_t buf[4096];
|
||||
size_t len;
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, expire);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, expire);
|
||||
|
||||
file = fopen("./badcache.out", "w");
|
||||
dns_badcache_print(bc, "badcache", file);
|
||||
fclose(file);
|
||||
|
||||
file = fopen("./badcache.out", "r");
|
||||
len = fread(buf, sizeof(buf[0]), ARRAY_SIZE(buf), file);
|
||||
assert_int_equal(len, 68);
|
||||
fclose(file);
|
||||
|
||||
/* Calculated as crc32 ./badcache.out */
|
||||
assert_int_equal(crc32(buf, len), 0x7c96678f);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(flush) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_badcache_flush(bc);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(flushname) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_flushname(bc, name);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(flushtree) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_flushtree(bc, name);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_int_equal(flags, BADCACHE_TEST_FLAG);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(purge) {
|
||||
dns_badcache_t *bc = NULL;
|
||||
dns_fixedname_t fname = { 0 };
|
||||
dns_name_t *name = dns_fixedname_initname(&fname);
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_result_t result;
|
||||
uint32_t flags = BADCACHE_TEST_FLAG;
|
||||
|
||||
bc = dns_badcache_new(mctx);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now - 60);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now - 60);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now - 60);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now + 30);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now + 30);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now + 30);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_name_fromstring2(name, "example.com.", NULL, 0, NULL);
|
||||
result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags,
|
||||
now + 30);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_badcache_destroy(&bc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
ISC_TEST_ENTRY_CUSTOM(basic, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(expire, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(print, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(flush, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(flushname, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(flushtree, setup_managers, teardown_managers)
|
||||
ISC_TEST_ENTRY_CUSTOM(purge, setup_managers, teardown_managers)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
||||
|
|
@ -122,7 +122,8 @@ run_sfcache_test(const ns__query_sfcache_test_params_t *test) {
|
|||
|
||||
dns_badcache_add(qctx->client->view->failcache, dns_rootname,
|
||||
dns_rdatatype_ns, true,
|
||||
test->cache_entry_flags, &expire);
|
||||
test->cache_entry_flags,
|
||||
isc_time_seconds(&expire));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue