mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +00:00
Redesign the unreachable primaries cache
The cache for unreachable primaries was added to BIND 9 in 2006 via 1372e172d0e0b08996376b782a9041d1e3542489. It features a 10-slot LRU array with 600 seconds (10 minutes) fixed delay. During this time, any primary with a hiccup would be blocked for the whole block duration (unless overwritten by a different entry). As this design is not very flexible (i.e. the fixed delay and the fixed amount of the slots), redesign it based on the badcache.c module, which was implemented earlier for a similar mechanism. The differences between the new code and the badcache module were large enough to create a new module instead of trying to make the badcache module universal, which could complicate the implementation. The new design implements an exponential backoff for entries which are added again soon after expiring, i.e. the next expiration happens in double the amount of time of the previous expiration, but in no more time than the defined maximum value. The initial and the maximum expiration values are hard-coded, but, if required, it should be trivial to implement configurable knobs.
This commit is contained in:
parent
bb1458460b
commit
14915b0241
@ -131,6 +131,7 @@ libdns_la_HEADERS = \
|
||||
include/dns/tsig.h \
|
||||
include/dns/ttl.h \
|
||||
include/dns/types.h \
|
||||
include/dns/unreachcache.h \
|
||||
include/dns/update.h \
|
||||
include/dns/validator.h \
|
||||
include/dns/view.h \
|
||||
@ -247,6 +248,7 @@ libdns_la_SOURCES = \
|
||||
tsig.c \
|
||||
tsig_p.h \
|
||||
ttl.c \
|
||||
unreachcache.c \
|
||||
update.c \
|
||||
validator.c \
|
||||
view.c \
|
||||
|
@ -167,6 +167,7 @@ typedef struct dns_tsigkeyring dns_tsigkeyring_t;
|
||||
typedef struct dns_tsigkey dns_tsigkey_t;
|
||||
typedef uint32_t dns_ttl_t;
|
||||
typedef uint32_t dns_typepair_t;
|
||||
typedef struct dns_unreachcache dns_unreachcache_t;
|
||||
typedef struct dns_update_state dns_update_state_t;
|
||||
typedef struct dns_validator dns_validator_t;
|
||||
typedef struct dns_view dns_view_t;
|
||||
|
132
lib/dns/include/dns/unreachcache.h
Normal file
132
lib/dns/include/dns/unreachcache.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*****
|
||||
***** Module Info
|
||||
*****/
|
||||
|
||||
/*! \file dns/unreachcache.h
|
||||
* \brief
|
||||
* Defines dns_unreachcache_t, the "unreachable cache" object.
|
||||
*
|
||||
* Notes:
|
||||
*\li An unreachable cache object is a hash table of
|
||||
* isc_sockaddr_t/isc_sockaddr_t tuples, indicating whether a given tuple
|
||||
* is known to be "unreachable" in some sense (e.g. an unresponsive primary
|
||||
* server). This is currently used by the secondary servers for the
|
||||
* "unreachable cache".
|
||||
*
|
||||
* Reliability:
|
||||
*
|
||||
* Resources:
|
||||
*
|
||||
* Security:
|
||||
*
|
||||
* Standards:
|
||||
*/
|
||||
|
||||
/***
|
||||
*** Imports
|
||||
***/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/loop.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/stdtime.h>
|
||||
|
||||
#include <dns/types.h>
|
||||
|
||||
/***
|
||||
*** Functions
|
||||
***/
|
||||
|
||||
dns_unreachcache_t *
|
||||
dns_unreachcache_new(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
|
||||
const uint16_t expire_min_s, const uint16_t expire_max_s,
|
||||
const uint16_t backoff_eligible_s);
|
||||
/*%
|
||||
* Allocate and initialize an unreachable cache. A newly entered entry expires
|
||||
* in 'expire_min_s' seconds, a duplicate entry refreshes the expire timer.
|
||||
* However, after expiring, if the same entry is added again in less that the
|
||||
* 'backoff_eligible_s' time, then the next expire happens in a double amount of
|
||||
* time of the previous expiration, but no more than in 'expire_max_s' seconds.
|
||||
*
|
||||
* Requires:
|
||||
* \li mctx != NULL
|
||||
* \li expire_min_s > 0
|
||||
* \li expire_min_s <= expire_max_s
|
||||
*/
|
||||
|
||||
void
|
||||
dns_unreachcache_destroy(dns_unreachcache_t **ucp);
|
||||
/*%
|
||||
* Flush and then free unreachcache in 'ucp'. '*ucp' is set to NULL on return.
|
||||
*
|
||||
* Requires:
|
||||
* \li '*ucp' to be a valid unreachcache
|
||||
*/
|
||||
|
||||
void
|
||||
dns_unreachcache_add(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local);
|
||||
/*%
|
||||
* Adds an unreachcache entry to the unreachcache 'uc' for addresses 'remote'
|
||||
* and 'local'. If an entry already exists, then it is refreshed. See also
|
||||
* the documentation of the dns_unreachcache_new() function.
|
||||
*
|
||||
* Requires:
|
||||
* \li uc to be a valid unreachcache.
|
||||
* \li remote != NULL
|
||||
* \li local != NULL
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
dns_unreachcache_find(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local);
|
||||
/*%
|
||||
* Returns ISC_R_SUCCESS if a record is found in the unreachcache 'uc' matching
|
||||
* 'remote' and 'local', with an expiration date later than 'now'. Returns
|
||||
* ISC_R_NOTFOUND otherwise.
|
||||
*
|
||||
* Requires:
|
||||
* \li uc to be a valid unreachcache.
|
||||
* \li remote != NULL
|
||||
* \li local != NULL
|
||||
*/
|
||||
|
||||
void
|
||||
dns_unreachcache_remove(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local);
|
||||
/*%
|
||||
* Removes a record that is found in the unreachcache 'uc' matching 'remote' and
|
||||
* 'local', if it exists.
|
||||
*
|
||||
* Requires:
|
||||
* \li uc to be a valid unreachcache.
|
||||
* \li remote != NULL
|
||||
* \li local != NULL
|
||||
* \li now != NULL
|
||||
*/
|
||||
|
||||
void
|
||||
dns_unreachcache_flush(dns_unreachcache_t *uc);
|
||||
/*%
|
||||
* Flush the entire unreachable cache.
|
||||
*
|
||||
* Requires:
|
||||
* \li uc to be a valid unreachcache
|
||||
*/
|
@ -178,6 +178,7 @@ struct dns_view {
|
||||
dns_dlzdblist_t dlz_unsearched;
|
||||
uint32_t fail_ttl;
|
||||
dns_badcache_t *failcache;
|
||||
dns_unreachcache_t *unreachcache;
|
||||
unsigned int udpsize;
|
||||
uint32_t sig0key_checks_limit;
|
||||
uint32_t sig0message_checks_limit;
|
||||
|
@ -1953,44 +1953,6 @@ dns_zone_getxfr(dns_zone_t *zone, dns_xfrin_t **xfrp, bool *is_firstrefresh,
|
||||
* ISC_R_FAILURE error while trying to get the transfer information
|
||||
*/
|
||||
|
||||
void
|
||||
dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local, isc_time_t *now);
|
||||
/*%<
|
||||
* Add the pair of addresses to the unreachable cache.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'zmgr' to be a valid zone manager.
|
||||
*\li 'remote' to be a valid sockaddr.
|
||||
*\li 'local' to be a valid sockaddr.
|
||||
*/
|
||||
|
||||
bool
|
||||
dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local, isc_time_t *now);
|
||||
/*%<
|
||||
* Returns true if the given local/remote address pair
|
||||
* is found in the zone maanger's unreachable cache.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'zmgr' to be a valid zone manager.
|
||||
*\li 'remote' to be a valid sockaddr.
|
||||
*\li 'local' to be a valid sockaddr.
|
||||
*\li 'now' != NULL
|
||||
*/
|
||||
|
||||
void
|
||||
dns_zonemgr_unreachabledel(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local);
|
||||
/*%<
|
||||
* Remove the pair of addresses from the unreachable cache.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'zmgr' to be a valid zone manager.
|
||||
*\li 'remote' to be a valid sockaddr.
|
||||
*\li 'local' to be a valid sockaddr.
|
||||
*/
|
||||
|
||||
void
|
||||
dns_zonemgr_set_tlsctx_cache(dns_zonemgr_t *zmgr,
|
||||
isc_tlsctx_cache_t *tlsctx_cache);
|
||||
|
436
lib/dns/unreachcache.c
Normal file
436
lib/dns/unreachcache.c
Normal file
@ -0,0 +1,436 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/async.h>
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/hash.h>
|
||||
#include <isc/log.h>
|
||||
#include <isc/loop.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/rwlock.h>
|
||||
#include <isc/sockaddr.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/fixedname.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdatatype.h>
|
||||
#include <dns/types.h>
|
||||
#include <dns/unreachcache.h>
|
||||
|
||||
typedef struct dns_ucentry dns_ucentry_t;
|
||||
|
||||
typedef struct dns_uckey {
|
||||
const isc_sockaddr_t *remote;
|
||||
const isc_sockaddr_t *local;
|
||||
} dns__uckey_t;
|
||||
|
||||
struct dns_unreachcache {
|
||||
unsigned int magic;
|
||||
isc_mem_t *mctx;
|
||||
uint16_t expire_min_s;
|
||||
uint16_t expire_max_s;
|
||||
uint16_t backoff_eligible_s;
|
||||
struct cds_lfht *ht;
|
||||
struct cds_list_head *lru;
|
||||
uint32_t nloops;
|
||||
};
|
||||
|
||||
#define UNREACHCACHE_MAGIC ISC_MAGIC('U', 'R', 'C', 'a')
|
||||
#define VALID_UNREACHCACHE(m) ISC_MAGIC_VALID(m, UNREACHCACHE_MAGIC)
|
||||
|
||||
#define UNREACHCACHE_INIT_SIZE (1 << 4) /* Must be power of 2 */
|
||||
#define UNREACHCACHE_MIN_SIZE (1 << 5) /* Must be power of 2 */
|
||||
|
||||
struct dns_ucentry {
|
||||
isc_loop_t *loop;
|
||||
isc_stdtime_t expire;
|
||||
unsigned int exp_backoff_n;
|
||||
uint16_t wait_time;
|
||||
bool confirmed;
|
||||
|
||||
struct cds_lfht_node ht_node;
|
||||
struct rcu_head rcu_head;
|
||||
struct cds_list_head lru_head;
|
||||
|
||||
isc_sockaddr_t remote;
|
||||
isc_sockaddr_t local;
|
||||
};
|
||||
|
||||
static void
|
||||
ucentry_destroy(struct rcu_head *rcu_head);
|
||||
|
||||
static bool
|
||||
ucentry_alive(struct cds_lfht *ht, dns_ucentry_t *unreach, isc_stdtime_t now,
|
||||
bool alive_or_waiting);
|
||||
|
||||
dns_unreachcache_t *
|
||||
dns_unreachcache_new(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
|
||||
const uint16_t expire_min_s, const uint16_t expire_max_s,
|
||||
const uint16_t backoff_eligible_s) {
|
||||
REQUIRE(loopmgr != NULL);
|
||||
REQUIRE(expire_min_s > 0);
|
||||
REQUIRE(expire_min_s <= expire_max_s);
|
||||
|
||||
uint32_t nloops = isc_loopmgr_nloops(loopmgr);
|
||||
dns_unreachcache_t *uc = isc_mem_get(mctx, sizeof(*uc));
|
||||
*uc = (dns_unreachcache_t){
|
||||
.magic = UNREACHCACHE_MAGIC,
|
||||
.expire_min_s = expire_min_s,
|
||||
.expire_max_s = expire_max_s,
|
||||
.backoff_eligible_s = backoff_eligible_s,
|
||||
.nloops = nloops,
|
||||
};
|
||||
|
||||
uc->ht = cds_lfht_new(UNREACHCACHE_INIT_SIZE, UNREACHCACHE_MIN_SIZE, 0,
|
||||
CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
|
||||
INSIST(uc->ht != NULL);
|
||||
|
||||
uc->lru = isc_mem_cget(mctx, uc->nloops, sizeof(uc->lru[0]));
|
||||
for (size_t i = 0; i < uc->nloops; i++) {
|
||||
CDS_INIT_LIST_HEAD(&uc->lru[i]);
|
||||
}
|
||||
|
||||
isc_mem_attach(mctx, &uc->mctx);
|
||||
|
||||
return uc;
|
||||
}
|
||||
|
||||
void
|
||||
dns_unreachcache_destroy(dns_unreachcache_t **ucp) {
|
||||
REQUIRE(ucp != NULL && *ucp != NULL);
|
||||
REQUIRE(VALID_UNREACHCACHE(*ucp));
|
||||
dns_unreachcache_t *uc = *ucp;
|
||||
*ucp = NULL;
|
||||
uc->magic = 0;
|
||||
|
||||
dns_ucentry_t *unreach = NULL;
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(uc->ht, &iter, unreach, ht_node) {
|
||||
INSIST(!cds_lfht_del(uc->ht, &unreach->ht_node));
|
||||
ucentry_destroy(&unreach->rcu_head);
|
||||
}
|
||||
RUNTIME_CHECK(!cds_lfht_destroy(uc->ht, NULL));
|
||||
|
||||
isc_mem_cput(uc->mctx, uc->lru, uc->nloops, sizeof(uc->lru[0]));
|
||||
|
||||
isc_mem_putanddetach(&uc->mctx, uc, sizeof(dns_unreachcache_t));
|
||||
}
|
||||
|
||||
static int
|
||||
ucentry_match(struct cds_lfht_node *ht_node, const void *key0) {
|
||||
const dns__uckey_t *key = key0;
|
||||
dns_ucentry_t *unreach = caa_container_of(ht_node, dns_ucentry_t,
|
||||
ht_node);
|
||||
|
||||
return isc_sockaddr_equal(&unreach->remote, key->remote) &&
|
||||
isc_sockaddr_equal(&unreach->local, key->local);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
ucentry_hash(const dns__uckey_t *key) {
|
||||
return isc_sockaddr_hash(key->remote, false) ^
|
||||
isc_sockaddr_hash(key->local, false);
|
||||
}
|
||||
|
||||
static dns_ucentry_t *
|
||||
ucentry_lookup(struct cds_lfht *ht, uint32_t hashval, dns__uckey_t *key) {
|
||||
struct cds_lfht_iter iter;
|
||||
|
||||
cds_lfht_lookup(ht, hashval, ucentry_match, key, &iter);
|
||||
|
||||
return cds_lfht_entry(cds_lfht_iter_get_node(&iter), dns_ucentry_t,
|
||||
ht_node);
|
||||
}
|
||||
|
||||
static dns_ucentry_t *
|
||||
ucentry_new(isc_loop_t *loop, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local, const isc_stdtime_t expire,
|
||||
const isc_stdtime_t wait_time) {
|
||||
isc_mem_t *mctx = isc_loop_getmctx(loop);
|
||||
dns_ucentry_t *unreach = isc_mem_get(mctx, sizeof(*unreach));
|
||||
*unreach = (dns_ucentry_t){
|
||||
.remote = *remote,
|
||||
.local = *local,
|
||||
.expire = expire,
|
||||
.wait_time = wait_time,
|
||||
.loop = isc_loop_ref(loop),
|
||||
.lru_head = CDS_LIST_HEAD_INIT(unreach->lru_head),
|
||||
};
|
||||
|
||||
return unreach;
|
||||
}
|
||||
|
||||
static void
|
||||
ucentry_destroy(struct rcu_head *rcu_head) {
|
||||
dns_ucentry_t *unreach = caa_container_of(rcu_head, dns_ucentry_t,
|
||||
rcu_head);
|
||||
isc_loop_t *loop = unreach->loop;
|
||||
isc_mem_t *mctx = isc_loop_getmctx(loop);
|
||||
|
||||
isc_mem_put(mctx, unreach, sizeof(*unreach));
|
||||
isc_loop_unref(loop);
|
||||
}
|
||||
|
||||
static void
|
||||
ucentry_evict_async(void *arg) {
|
||||
dns_ucentry_t *unreach = arg;
|
||||
|
||||
RUNTIME_CHECK(unreach->loop == isc_loop());
|
||||
|
||||
cds_list_del(&unreach->lru_head);
|
||||
call_rcu(&unreach->rcu_head, ucentry_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
ucentry_evict(struct cds_lfht *ht, dns_ucentry_t *unreach) {
|
||||
if (!cds_lfht_del(ht, &unreach->ht_node)) {
|
||||
if (unreach->loop == isc_loop()) {
|
||||
ucentry_evict_async(unreach);
|
||||
return;
|
||||
}
|
||||
isc_async_run(unreach->loop, ucentry_evict_async, unreach);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ucentry_alive(struct cds_lfht *ht, dns_ucentry_t *unreach, isc_stdtime_t now,
|
||||
bool alive_or_waiting) {
|
||||
if (cds_lfht_is_node_deleted(&unreach->ht_node)) {
|
||||
return false;
|
||||
} else if (unreach->expire < now) {
|
||||
bool is_waiting = unreach->expire + unreach->wait_time >= now;
|
||||
|
||||
if (is_waiting) {
|
||||
/*
|
||||
* Wait some minimum time before evicting an expired
|
||||
* entry so we can support exponential backoff for
|
||||
* nodes which enter again shortly after expiring.
|
||||
*
|
||||
* The return value depends on whether the caller is
|
||||
* interested to know if the node is in either active or
|
||||
* waiting state (i.e. not eviceted), or is interested
|
||||
* only if it's still alive (i.e. not expired).
|
||||
*/
|
||||
return alive_or_waiting;
|
||||
}
|
||||
|
||||
/* The entry is already expired, evict it before returning. */
|
||||
ucentry_evict(ht, unreach);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ucentry_purge(struct cds_lfht *ht, struct cds_list_head *lru,
|
||||
isc_stdtime_t now) {
|
||||
size_t count = 10;
|
||||
dns_ucentry_t *unreach;
|
||||
cds_list_for_each_entry_rcu(unreach, lru, lru_head) {
|
||||
if (ucentry_alive(ht, unreach, now, true)) {
|
||||
break;
|
||||
}
|
||||
if (--count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ucentry_backoff(const dns_unreachcache_t *uc, const isc_stdtime_t now,
|
||||
dns_ucentry_t *new, const dns_ucentry_t *old) {
|
||||
/*
|
||||
* Perform exponential backoff if this is an expired entry wating to be
|
||||
* evicted. Otherwise it's a duplicate entry and no backoff is required
|
||||
* as we will just update the cache with a new entry that has the same
|
||||
* expiration time as the old one, but calculated freshly, based on the
|
||||
* current time.
|
||||
*/
|
||||
if (old->expire < now) {
|
||||
new->exp_backoff_n = old->exp_backoff_n + 1;
|
||||
} else {
|
||||
new->exp_backoff_n = old->exp_backoff_n;
|
||||
}
|
||||
for (size_t i = 0; i < new->exp_backoff_n; i++) {
|
||||
new->expire += uc->expire_min_s;
|
||||
if (new->expire > now + uc->expire_max_s) {
|
||||
new->expire = now + uc->expire_max_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dns_unreachcache_add(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local) {
|
||||
REQUIRE(VALID_UNREACHCACHE(uc));
|
||||
REQUIRE(remote != NULL);
|
||||
REQUIRE(local != NULL);
|
||||
|
||||
isc_loop_t *loop = isc_loop();
|
||||
uint32_t tid = isc_tid();
|
||||
struct cds_list_head *lru = &uc->lru[tid];
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
isc_stdtime_t expire = now + uc->expire_min_s;
|
||||
bool exp_backoff_activated = false;
|
||||
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(uc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
dns__uckey_t key = {
|
||||
.remote = remote,
|
||||
.local = local,
|
||||
};
|
||||
uint32_t hashval = ucentry_hash(&key);
|
||||
|
||||
dns_ucentry_t *unreach = ucentry_new(loop, remote, local, expire,
|
||||
uc->backoff_eligible_s);
|
||||
struct cds_lfht_node *ht_node;
|
||||
do {
|
||||
ht_node = cds_lfht_add_unique(ht, hashval, ucentry_match, &key,
|
||||
&unreach->ht_node);
|
||||
if (ht_node != &unreach->ht_node) {
|
||||
/* The entry already exists, get it. */
|
||||
dns_ucentry_t *found = caa_container_of(
|
||||
ht_node, dns_ucentry_t, ht_node);
|
||||
|
||||
/*
|
||||
* Consider unreachability as confirmed only if
|
||||
* an entry is submitted at least twice, i.e. there
|
||||
* was an older entry (which is exactly this case).
|
||||
*/
|
||||
unreach->confirmed = true;
|
||||
|
||||
/*
|
||||
* Recalculate the expire time of the new entry based
|
||||
* on the old entry's exponential backoff value.
|
||||
*/
|
||||
if (!exp_backoff_activated) {
|
||||
exp_backoff_activated = true;
|
||||
ucentry_backoff(uc, now, unreach, found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Evict the old entry, so we can try to insert the new
|
||||
* one again.
|
||||
*/
|
||||
ucentry_evict(ht, found);
|
||||
}
|
||||
} while (ht_node != &unreach->ht_node);
|
||||
|
||||
/* No locking, instead we are using per-thread lists */
|
||||
cds_list_add_tail_rcu(&unreach->lru_head, lru);
|
||||
|
||||
ucentry_purge(ht, lru, now);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_unreachcache_find(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local) {
|
||||
REQUIRE(VALID_UNREACHCACHE(uc));
|
||||
REQUIRE(remote != NULL);
|
||||
REQUIRE(local != NULL);
|
||||
|
||||
isc_result_t result = ISC_R_NOTFOUND;
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(uc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
dns__uckey_t key = {
|
||||
.remote = remote,
|
||||
.local = local,
|
||||
};
|
||||
uint32_t hashval = ucentry_hash(&key);
|
||||
|
||||
dns_ucentry_t *found = ucentry_lookup(ht, hashval, &key);
|
||||
if (found != NULL && found->confirmed &&
|
||||
ucentry_alive(ht, found, now, false))
|
||||
{
|
||||
result = ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t tid = isc_tid();
|
||||
struct cds_list_head *lru = &uc->lru[tid];
|
||||
ucentry_purge(ht, lru, now);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
dns_unreachcache_remove(dns_unreachcache_t *uc, const isc_sockaddr_t *remote,
|
||||
const isc_sockaddr_t *local) {
|
||||
REQUIRE(VALID_UNREACHCACHE(uc));
|
||||
REQUIRE(remote != NULL);
|
||||
REQUIRE(local != NULL);
|
||||
|
||||
isc_stdtime_t now = isc_stdtime_now();
|
||||
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(uc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
dns__uckey_t key = {
|
||||
.remote = remote,
|
||||
.local = local,
|
||||
};
|
||||
uint32_t hashval = ucentry_hash(&key);
|
||||
|
||||
dns_ucentry_t *found = ucentry_lookup(ht, hashval, &key);
|
||||
if (found != NULL) {
|
||||
ucentry_evict(ht, found);
|
||||
}
|
||||
|
||||
uint32_t tid = isc_tid();
|
||||
struct cds_list_head *lru = &uc->lru[tid];
|
||||
ucentry_purge(ht, lru, now);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void
|
||||
dns_unreachcache_flush(dns_unreachcache_t *uc) {
|
||||
REQUIRE(VALID_UNREACHCACHE(uc));
|
||||
|
||||
rcu_read_lock();
|
||||
struct cds_lfht *ht = rcu_dereference(uc->ht);
|
||||
INSIST(ht != NULL);
|
||||
|
||||
/* Flush the hash table */
|
||||
dns_ucentry_t *unreach;
|
||||
struct cds_lfht_iter iter;
|
||||
cds_lfht_for_each_entry(ht, &iter, unreach, ht_node) {
|
||||
ucentry_evict(ht, unreach);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
@ -60,6 +60,7 @@
|
||||
#include <dns/time.h>
|
||||
#include <dns/transport.h>
|
||||
#include <dns/tsig.h>
|
||||
#include <dns/unreachcache.h>
|
||||
#include <dns/view.h>
|
||||
#include <dns/zone.h>
|
||||
#include <dns/zt.h>
|
||||
@ -84,6 +85,11 @@
|
||||
*/
|
||||
#define DEFAULT_EDNS_BUFSIZE 1232
|
||||
|
||||
/* Exponental backoff from 10 seconds to 640 seconds */
|
||||
#define UNREACH_HOLD_TIME_INITIAL_SEC ((uint16_t)10)
|
||||
#define UNREACH_HOLD_TIME_MAX_SEC (UNREACH_HOLD_TIME_INITIAL_SEC << 6)
|
||||
#define UNREACH_BACKOFF_ELIGIBLE_SEC ((uint16_t)120)
|
||||
|
||||
void
|
||||
dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
|
||||
dns_dispatchmgr_t *dispatchmgr, dns_rdataclass_t rdclass,
|
||||
@ -149,6 +155,10 @@ dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
|
||||
|
||||
view->failcache = dns_badcache_new(view->mctx, loopmgr);
|
||||
|
||||
view->unreachcache = dns_unreachcache_new(
|
||||
view->mctx, loopmgr, UNREACH_HOLD_TIME_INITIAL_SEC,
|
||||
UNREACH_HOLD_TIME_MAX_SEC, UNREACH_BACKOFF_ELIGIBLE_SEC);
|
||||
|
||||
isc_mutex_init(&view->new_zone_lock);
|
||||
|
||||
dns_order_create(view->mctx, &view->order);
|
||||
@ -355,6 +365,9 @@ destroy(dns_view_t *view) {
|
||||
if (view->failcache != NULL) {
|
||||
dns_badcache_destroy(&view->failcache);
|
||||
}
|
||||
if (view->unreachcache != NULL) {
|
||||
dns_unreachcache_destroy(&view->unreachcache);
|
||||
}
|
||||
isc_mutex_destroy(&view->new_zone_lock);
|
||||
isc_mutex_destroy(&view->lock);
|
||||
isc_refcount_destroy(&view->references);
|
||||
@ -1391,6 +1404,9 @@ dns_view_flushcache(dns_view_t *view, bool fixuponly) {
|
||||
if (view->failcache != NULL) {
|
||||
dns_badcache_flush(view->failcache);
|
||||
}
|
||||
if (view->unreachcache != NULL) {
|
||||
dns_unreachcache_flush(view->unreachcache);
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
adb = rcu_dereference(view->adb);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <dns/trace.h>
|
||||
#include <dns/transport.h>
|
||||
#include <dns/tsig.h>
|
||||
#include <dns/unreachcache.h>
|
||||
#include <dns/view.h>
|
||||
#include <dns/xfrin.h>
|
||||
#include <dns/zone.h>
|
||||
@ -1448,8 +1449,9 @@ xfrin_connect_done(isc_result_t result, isc_region_t *region ISC_ATTR_UNUSED,
|
||||
|
||||
zmgr = dns_zone_getmgr(xfr->zone);
|
||||
if (zmgr != NULL) {
|
||||
dns_zonemgr_unreachabledel(zmgr, &xfr->primaryaddr,
|
||||
&xfr->sourceaddr);
|
||||
dns_view_t *view = dns_zone_getview(xfr->zone);
|
||||
dns_unreachcache_remove(view->unreachcache, &xfr->primaryaddr,
|
||||
&xfr->sourceaddr);
|
||||
}
|
||||
|
||||
if (xfr->tsigkey != NULL && xfr->tsigkey->key != NULL) {
|
||||
@ -1480,16 +1482,16 @@ failure:
|
||||
case ISC_R_CONNREFUSED:
|
||||
case ISC_R_TIMEDOUT:
|
||||
/*
|
||||
* Add the server to unreachable primaries table if
|
||||
* Add the server to unreachable primaries cache if
|
||||
* the server has a permanent networking error or
|
||||
* the connection attempt as timed out.
|
||||
*/
|
||||
zmgr = dns_zone_getmgr(xfr->zone);
|
||||
if (zmgr != NULL) {
|
||||
isc_time_t now = isc_time_now();
|
||||
|
||||
dns_zonemgr_unreachableadd(zmgr, &xfr->primaryaddr,
|
||||
&xfr->sourceaddr, &now);
|
||||
dns_view_t *view = dns_zone_getview(xfr->zone);
|
||||
dns_unreachcache_add(view->unreachcache,
|
||||
&xfr->primaryaddr,
|
||||
&xfr->sourceaddr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
157
lib/dns/zone.c
157
lib/dns/zone.c
@ -87,6 +87,7 @@
|
||||
#include <dns/time.h>
|
||||
#include <dns/tsig.h>
|
||||
#include <dns/ttl.h>
|
||||
#include <dns/unreachcache.h>
|
||||
#include <dns/update.h>
|
||||
#include <dns/xfrin.h>
|
||||
#include <dns/zone.h>
|
||||
@ -600,9 +601,6 @@ typedef enum {
|
||||
* load. */
|
||||
} dns_zoneloadflag_t;
|
||||
|
||||
#define UNREACH_CACHE_SIZE 10U
|
||||
#define UNREACH_HOLD_TIME 600 /* 10 minutes */
|
||||
|
||||
#define CHECK(op) \
|
||||
do { \
|
||||
result = (op); \
|
||||
@ -610,14 +608,6 @@ typedef enum {
|
||||
goto failure; \
|
||||
} while (0)
|
||||
|
||||
struct dns_unreachable {
|
||||
isc_sockaddr_t remote;
|
||||
isc_sockaddr_t local;
|
||||
atomic_uint_fast32_t expire;
|
||||
atomic_uint_fast32_t last;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
struct dns_zonemgr {
|
||||
unsigned int magic;
|
||||
isc_mem_t *mctx;
|
||||
@ -632,7 +622,6 @@ struct dns_zonemgr {
|
||||
isc_ratelimiter_t *startupnotifyrl;
|
||||
isc_ratelimiter_t *startuprefreshrl;
|
||||
isc_rwlock_t rwlock;
|
||||
isc_rwlock_t urlock;
|
||||
|
||||
/* Locked by rwlock. */
|
||||
dns_zonelist_t zones;
|
||||
@ -648,10 +637,6 @@ struct dns_zonemgr {
|
||||
unsigned int serialqueryrate;
|
||||
unsigned int startupserialqueryrate;
|
||||
|
||||
/* Locked by urlock. */
|
||||
/* LRU cache */
|
||||
struct dns_unreachable unreachable[UNREACH_CACHE_SIZE];
|
||||
|
||||
dns_keymgmt_t *keymgmt;
|
||||
|
||||
isc_tlsctx_cache_t *tlsctx_cache;
|
||||
@ -13309,7 +13294,6 @@ stub_glue_response(void *arg) {
|
||||
uint32_t addr_count, cnamecnt;
|
||||
isc_result_t result;
|
||||
isc_sockaddr_t curraddr;
|
||||
isc_time_t now;
|
||||
dns_rdataset_t *addr_rdataset = NULL;
|
||||
dns_dbnode_t *node = NULL;
|
||||
|
||||
@ -13319,8 +13303,6 @@ stub_glue_response(void *arg) {
|
||||
|
||||
ENTER;
|
||||
|
||||
now = isc_time_now();
|
||||
|
||||
LOCK_ZONE(zone);
|
||||
|
||||
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
|
||||
@ -13333,8 +13315,8 @@ stub_glue_response(void *arg) {
|
||||
isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
|
||||
|
||||
if (dns_request_getresult(request) != ISC_R_SUCCESS) {
|
||||
dns_zonemgr_unreachableadd(zone->zmgr, &curraddr,
|
||||
&zone->sourceaddr, &now);
|
||||
dns_unreachcache_add(zone->view->unreachcache, &curraddr,
|
||||
&zone->sourceaddr);
|
||||
dns_zone_log(zone, ISC_LOG_INFO,
|
||||
"could not refresh stub from primary %s"
|
||||
" (source %s): %s",
|
||||
@ -13487,7 +13469,7 @@ cleanup:
|
||||
/* If last request, release all related resources */
|
||||
if (atomic_fetch_sub_release(&stub->pending_requests, 1) == 1) {
|
||||
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args));
|
||||
stub_finish_zone_update(stub, now);
|
||||
stub_finish_zone_update(stub, isc_time_now());
|
||||
UNLOCK_ZONE(zone);
|
||||
stub->magic = 0;
|
||||
dns_zone_idetach(&stub->zone);
|
||||
@ -13762,8 +13744,8 @@ stub_callback(void *arg) {
|
||||
}
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
dns_zonemgr_unreachableadd(zone->zmgr, &curraddr,
|
||||
&zone->sourceaddr, &now);
|
||||
dns_unreachcache_add(zone->view->unreachcache, &curraddr,
|
||||
&zone->sourceaddr);
|
||||
dns_zone_log(zone, ISC_LOG_INFO,
|
||||
"could not refresh stub from primary "
|
||||
"%s (source %s): %s",
|
||||
@ -14117,9 +14099,9 @@ refresh_callback(void *arg) {
|
||||
zone->type == dns_zone_redirect) &&
|
||||
DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH))
|
||||
{
|
||||
if (!dns_zonemgr_unreachable(
|
||||
zone->zmgr, &curraddr,
|
||||
&zone->sourceaddr, &now))
|
||||
if (dns_unreachcache_find(
|
||||
zone->view->unreachcache, &curraddr,
|
||||
&zone->sourceaddr) != ISC_R_SUCCESS)
|
||||
{
|
||||
DNS_ZONE_SETFLAG(
|
||||
zone,
|
||||
@ -14361,8 +14343,8 @@ refresh_callback(void *arg) {
|
||||
DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
|
||||
isc_serial_gt(serial, oldserial))
|
||||
{
|
||||
if (dns_zonemgr_unreachable(zone->zmgr, &curraddr,
|
||||
&zone->sourceaddr, &now))
|
||||
if (dns_unreachcache_find(zone->view->unreachcache, &curraddr,
|
||||
&zone->sourceaddr) == ISC_R_SUCCESS)
|
||||
{
|
||||
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN,
|
||||
ISC_LOG_INFO,
|
||||
@ -15727,7 +15709,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
|
||||
UNLOCK_ZONE(zone);
|
||||
|
||||
if (to != NULL) {
|
||||
dns_zonemgr_unreachabledel(zone->zmgr, from, to);
|
||||
dns_unreachcache_remove(zone->view->unreachcache, from, to);
|
||||
}
|
||||
dns_zone_refresh(zone);
|
||||
return ISC_R_SUCCESS;
|
||||
@ -18521,7 +18503,6 @@ got_transfer_quota(void *arg) {
|
||||
isc_netaddr_t primaryip;
|
||||
isc_sockaddr_t primaryaddr;
|
||||
isc_sockaddr_t sourceaddr;
|
||||
isc_time_t now;
|
||||
dns_transport_type_t soa_transport_type = DNS_TRANSPORT_NONE;
|
||||
const char *soa_before = "";
|
||||
bool loaded;
|
||||
@ -18533,12 +18514,10 @@ got_transfer_quota(void *arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = isc_time_now();
|
||||
|
||||
primaryaddr = dns_remote_curraddr(&zone->primaries);
|
||||
isc_sockaddr_format(&primaryaddr, primary, sizeof(primary));
|
||||
if (dns_zonemgr_unreachable(zone->zmgr, &primaryaddr, &zone->sourceaddr,
|
||||
&now))
|
||||
if (dns_unreachcache_find(zone->view->unreachcache, &primaryaddr,
|
||||
&zone->sourceaddr) == ISC_R_SUCCESS)
|
||||
{
|
||||
isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
|
||||
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO,
|
||||
@ -19194,15 +19173,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_nm_t *netmgr, dns_zonemgr_t **zmgrp) {
|
||||
ISC_LIST_INIT(zmgr->zones);
|
||||
ISC_LIST_INIT(zmgr->waiting_for_xfrin);
|
||||
ISC_LIST_INIT(zmgr->xfrin_in_progress);
|
||||
memset(zmgr->unreachable, 0, sizeof(zmgr->unreachable));
|
||||
for (size_t i = 0; i < UNREACH_CACHE_SIZE; i++) {
|
||||
atomic_init(&zmgr->unreachable[i].expire, 0);
|
||||
}
|
||||
isc_rwlock_init(&zmgr->rwlock);
|
||||
|
||||
/* Unreachable lock. */
|
||||
isc_rwlock_init(&zmgr->urlock);
|
||||
|
||||
isc_ratelimiter_create(loop, &zmgr->checkdsrl);
|
||||
isc_ratelimiter_create(loop, &zmgr->notifyrl);
|
||||
isc_ratelimiter_create(loop, &zmgr->refreshrl);
|
||||
@ -19410,7 +19382,6 @@ zonemgr_free(dns_zonemgr_t *zmgr) {
|
||||
isc_mem_cput(zmgr->mctx, zmgr->mctxpool, zmgr->workers,
|
||||
sizeof(zmgr->mctxpool[0]));
|
||||
|
||||
isc_rwlock_destroy(&zmgr->urlock);
|
||||
isc_rwlock_destroy(&zmgr->rwlock);
|
||||
isc_rwlock_destroy(&zmgr->tlsctx_cache_rwlock);
|
||||
|
||||
@ -19701,106 +19672,6 @@ dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
|
||||
return zmgr->serialqueryrate;
|
||||
}
|
||||
|
||||
bool
|
||||
dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local, isc_time_t *now) {
|
||||
unsigned int i;
|
||||
uint32_t seconds = isc_time_seconds(now);
|
||||
uint32_t count = 0;
|
||||
|
||||
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
|
||||
|
||||
RWLOCK(&zmgr->urlock, isc_rwlocktype_read);
|
||||
for (i = 0; i < UNREACH_CACHE_SIZE; i++) {
|
||||
if (atomic_load(&zmgr->unreachable[i].expire) >= seconds &&
|
||||
isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
|
||||
isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
|
||||
{
|
||||
atomic_store_relaxed(&zmgr->unreachable[i].last,
|
||||
seconds);
|
||||
count = zmgr->unreachable[i].count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RWUNLOCK(&zmgr->urlock, isc_rwlocktype_read);
|
||||
return i < UNREACH_CACHE_SIZE && count > 1U;
|
||||
}
|
||||
|
||||
void
|
||||
dns_zonemgr_unreachabledel(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local) {
|
||||
unsigned int i;
|
||||
|
||||
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
|
||||
|
||||
RWLOCK(&zmgr->urlock, isc_rwlocktype_read);
|
||||
for (i = 0; i < UNREACH_CACHE_SIZE; i++) {
|
||||
if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
|
||||
isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
|
||||
{
|
||||
atomic_store_relaxed(&zmgr->unreachable[i].expire, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
RWUNLOCK(&zmgr->urlock, isc_rwlocktype_read);
|
||||
}
|
||||
|
||||
void
|
||||
dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
|
||||
isc_sockaddr_t *local, isc_time_t *now) {
|
||||
uint32_t seconds = isc_time_seconds(now);
|
||||
uint32_t expire = 0, last = seconds;
|
||||
unsigned int slot = UNREACH_CACHE_SIZE, oldest = 0;
|
||||
bool update_entry = true;
|
||||
REQUIRE(DNS_ZONEMGR_VALID(zmgr));
|
||||
|
||||
RWLOCK(&zmgr->urlock, isc_rwlocktype_write);
|
||||
for (unsigned int i = 0; i < UNREACH_CACHE_SIZE; i++) {
|
||||
/* Existing entry? */
|
||||
if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
|
||||
isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
|
||||
{
|
||||
update_entry = false;
|
||||
slot = i;
|
||||
expire = atomic_load_relaxed(
|
||||
&zmgr->unreachable[i].expire);
|
||||
break;
|
||||
}
|
||||
/* Pick first empty slot? */
|
||||
if (atomic_load_relaxed(&zmgr->unreachable[i].expire) < seconds)
|
||||
{
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
/* The worst case, least recently used slot? */
|
||||
if (atomic_load_relaxed(&zmgr->unreachable[i].last) < last) {
|
||||
last = atomic_load_relaxed(&zmgr->unreachable[i].last);
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* We haven't found any existing or free slots, use the oldest */
|
||||
if (slot == UNREACH_CACHE_SIZE) {
|
||||
slot = oldest;
|
||||
}
|
||||
|
||||
if (expire < seconds) {
|
||||
/* Expired or new entry, reset count to 1 */
|
||||
zmgr->unreachable[slot].count = 1;
|
||||
} else {
|
||||
zmgr->unreachable[slot].count++;
|
||||
}
|
||||
atomic_store_relaxed(&zmgr->unreachable[slot].expire,
|
||||
seconds + UNREACH_HOLD_TIME);
|
||||
atomic_store_relaxed(&zmgr->unreachable[slot].last, seconds);
|
||||
if (update_entry) {
|
||||
zmgr->unreachable[slot].remote = *remote;
|
||||
zmgr->unreachable[slot].local = *local;
|
||||
}
|
||||
|
||||
RWUNLOCK(&zmgr->urlock, isc_rwlocktype_write);
|
||||
}
|
||||
|
||||
void
|
||||
dns_zone_stopxfr(dns_zone_t *zone) {
|
||||
dns_xfrin_t *xfr = NULL;
|
||||
|
@ -50,6 +50,7 @@ check_PROGRAMS = \
|
||||
time_test \
|
||||
transport_test \
|
||||
tsig_test \
|
||||
unreachcache_test \
|
||||
update_test \
|
||||
zonefile_test \
|
||||
zonemgr_test \
|
||||
|
189
tests/dns/unreachcache_test.c
Normal file
189
tests/dns/unreachcache_test.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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/lib.h>
|
||||
#include <isc/md.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/sockaddr.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/urcu.h>
|
||||
#include <isc/util.h>
|
||||
#include <isc/uv.h>
|
||||
|
||||
#include <dns/compress.h>
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/lib.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdatatype.h>
|
||||
#include <dns/unreachcache.h>
|
||||
|
||||
#include <tests/dns.h>
|
||||
|
||||
#define EXPIRE_MIN_S 5
|
||||
#define EXPIRE_MAX_S 10
|
||||
#define BACKOFF_ELGIBLE_S 5
|
||||
|
||||
ISC_LOOP_TEST_IMPL(basic) {
|
||||
dns_unreachcache_t *uc = NULL;
|
||||
struct in_addr localhost4 = { 0 };
|
||||
isc_sockaddr_t src_addrv4 = { 0 }, dst_addrv4 = { 0 },
|
||||
src_addrv6 = { 0 }, dst_addrv6 = { 0 };
|
||||
const uint16_t src_port = 1234;
|
||||
const uint16_t dst_port = 5678;
|
||||
isc_result_t result;
|
||||
|
||||
isc_sockaddr_fromin(&src_addrv4, &localhost4, src_port);
|
||||
isc_sockaddr_fromin(&dst_addrv4, &localhost4, dst_port);
|
||||
isc_sockaddr_fromin6(&src_addrv6, &in6addr_loopback, src_port);
|
||||
isc_sockaddr_fromin6(&dst_addrv6, &in6addr_loopback, dst_port);
|
||||
|
||||
uc = dns_unreachcache_new(mctx, loopmgr, EXPIRE_MIN_S, EXPIRE_MAX_S,
|
||||
BACKOFF_ELGIBLE_S);
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
dns_unreachcache_add(uc, &dst_addrv6, &src_addrv6);
|
||||
|
||||
/* Added but unconfirmed (at least another add required to confirm). */
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv6, &src_addrv6);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
/* Confirmed. */
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
dns_unreachcache_add(uc, &dst_addrv6, &src_addrv6);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv6, &src_addrv6);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
/* Removal. */
|
||||
dns_unreachcache_remove(uc, &dst_addrv6, &src_addrv6);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv6, &src_addrv6);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
/* Swapped addresses, should be not found. */
|
||||
result = dns_unreachcache_find(uc, &src_addrv4, &dst_addrv4);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
result = dns_unreachcache_find(uc, &src_addrv6, &dst_addrv6);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_unreachcache_destroy(&uc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(expire) {
|
||||
dns_unreachcache_t *uc = NULL;
|
||||
struct in_addr localhost4 = { 0 };
|
||||
isc_sockaddr_t src_addrv4 = { 0 }, dst_addrv4 = { 0 };
|
||||
const uint16_t src_port = 1234;
|
||||
const uint16_t dst_port = 5678;
|
||||
isc_result_t result;
|
||||
|
||||
isc_sockaddr_fromin(&src_addrv4, &localhost4, src_port);
|
||||
isc_sockaddr_fromin(&dst_addrv4, &localhost4, dst_port);
|
||||
|
||||
uc = dns_unreachcache_new(mctx, loopmgr, EXPIRE_MIN_S, EXPIRE_MAX_S,
|
||||
BACKOFF_ELGIBLE_S);
|
||||
/* Two adds to "confirm" the addition. */
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
sleep(1);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
sleep(EXPIRE_MIN_S);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
/*
|
||||
* Because of the exponentatl backoff, the new quick addition after the
|
||||
* previous expiration should expire in 2 x EXPIRE_MIN_S seconds.
|
||||
*/
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
|
||||
sleep(1);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
sleep(EXPIRE_MIN_S);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
sleep(EXPIRE_MIN_S);
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_unreachcache_destroy(&uc);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_LOOP_TEST_IMPL(flush) {
|
||||
dns_unreachcache_t *uc = NULL;
|
||||
struct in_addr localhost4 = { 0 };
|
||||
isc_sockaddr_t src_addrv4 = { 0 }, dst_addrv4 = { 0 };
|
||||
const uint16_t src_port = 1234;
|
||||
const uint16_t dst_port = 5678;
|
||||
isc_result_t result;
|
||||
|
||||
isc_sockaddr_fromin(&src_addrv4, &localhost4, src_port);
|
||||
isc_sockaddr_fromin(&dst_addrv4, &localhost4, dst_port);
|
||||
|
||||
uc = dns_unreachcache_new(mctx, loopmgr, EXPIRE_MIN_S, EXPIRE_MAX_S,
|
||||
BACKOFF_ELGIBLE_S);
|
||||
/* Two adds to "confirm" the addition. */
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
dns_unreachcache_add(uc, &dst_addrv4, &src_addrv4);
|
||||
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_unreachcache_flush(uc);
|
||||
|
||||
result = dns_unreachcache_find(uc, &dst_addrv4, &src_addrv4);
|
||||
assert_int_equal(result, ISC_R_NOTFOUND);
|
||||
|
||||
dns_unreachcache_destroy(&uc);
|
||||
|
||||
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(flush, setup_managers, teardown_managers)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
@ -125,75 +125,10 @@ ISC_LOOP_TEST_IMPL(zonemgr_createzone) {
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
/* manage and release a zone */
|
||||
ISC_LOOP_TEST_IMPL(zonemgr_unreachable) {
|
||||
dns_zonemgr_t *myzonemgr = NULL;
|
||||
dns_zone_t *zone = NULL;
|
||||
isc_sockaddr_t addr1, addr2;
|
||||
struct in_addr in;
|
||||
isc_result_t result;
|
||||
isc_time_t now;
|
||||
|
||||
UNUSED(arg);
|
||||
|
||||
now = isc_time_now();
|
||||
|
||||
dns_zonemgr_create(mctx, netmgr, &myzonemgr);
|
||||
|
||||
result = dns_test_makezone("foo", &zone, NULL, false);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
result = dns_zonemgr_managezone(myzonemgr, zone);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
|
||||
in.s_addr = inet_addr("10.53.0.1");
|
||||
isc_sockaddr_fromin(&addr1, &in, 2112);
|
||||
in.s_addr = inet_addr("10.53.0.2");
|
||||
isc_sockaddr_fromin(&addr2, &in, 5150);
|
||||
assert_false(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
/*
|
||||
* We require multiple unreachableadd calls to mark a server as
|
||||
* unreachable.
|
||||
*/
|
||||
dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
|
||||
assert_false(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
|
||||
assert_true(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
|
||||
in.s_addr = inet_addr("10.53.0.3");
|
||||
isc_sockaddr_fromin(&addr2, &in, 5150);
|
||||
assert_false(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
/*
|
||||
* We require multiple unreachableadd calls to mark a server as
|
||||
* unreachable.
|
||||
*/
|
||||
dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
|
||||
dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
|
||||
assert_true(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
|
||||
dns_zonemgr_unreachabledel(myzonemgr, &addr1, &addr2);
|
||||
assert_false(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
|
||||
in.s_addr = inet_addr("10.53.0.2");
|
||||
isc_sockaddr_fromin(&addr2, &in, 5150);
|
||||
assert_true(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
dns_zonemgr_unreachabledel(myzonemgr, &addr1, &addr2);
|
||||
assert_false(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
|
||||
|
||||
dns_zonemgr_releasezone(myzonemgr, zone);
|
||||
dns_zone_detach(&zone);
|
||||
dns_zonemgr_shutdown(myzonemgr);
|
||||
dns_zonemgr_detach(&myzonemgr);
|
||||
assert_null(myzonemgr);
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
ISC_TEST_ENTRY_CUSTOM(zonemgr_create, setup_test, teardown_test)
|
||||
ISC_TEST_ENTRY_CUSTOM(zonemgr_managezone, setup_test, teardown_test)
|
||||
ISC_TEST_ENTRY_CUSTOM(zonemgr_createzone, setup_test, teardown_test)
|
||||
ISC_TEST_ENTRY_CUSTOM(zonemgr_unreachable, setup_test, teardown_test)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
||||
|
Loading…
x
Reference in New Issue
Block a user