2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00

new: usr: Redesign the unreachable primaries cache

Previously, the cache for the unreachable primary servers was limited
to 10 entries (LRU) and a fixed 10 minutes delay for each entry, unless
removed forcibly by a new entry. The cache is now redesigned to remove the
10 entry limitation and to introduce delay values with exponential
backoff time - initially an unreachable primary server is cached as
being unreachable for 10 seconds, but each time the cache entry is expired
and the same server is added again during the eligibility period of the next
120 seconds, the delay time is doubled up until to the maximum of 640
seconds.

Closes #3992

Merge branch '3992-unreachable-cache-redesign' into 'main'

See merge request isc-projects/bind9!10393
This commit is contained in:
Arаm Sаrgsyаn 2025-06-04 10:22:38 +00:00
commit b814434836
13 changed files with 808 additions and 253 deletions

View File

@ -87,6 +87,13 @@ The numbers in parentheses in the following text refer to the numbered items in
from the primary (as described in section 2 a. above). If the zone file has from the primary (as described in section 2 a. above). If the zone file has
changed, propagation is practically immediate. changed, propagation is practically immediate.
.. Note:: When a primary server is unreachable, :iscman:`named` initially caches
that information for 10 seconds. After that initial period, :iscman:`named`
may try that server again; if the server remains unresponsive,
:iscman:`named` caches its unreachable status for up to 640 seconds using an
exponential backoff. During that time :iscman:`named` does not try to contact
the affected server. The cache can be cleared using :option:`rndc flush`.
The authoritative samples all use NOTIFY but identify the statements used, so The authoritative samples all use NOTIFY but identify the statements used, so
that they can be removed if not required. that they can be removed if not required.

View File

@ -131,6 +131,7 @@ libdns_la_HEADERS = \
include/dns/tsig.h \ include/dns/tsig.h \
include/dns/ttl.h \ include/dns/ttl.h \
include/dns/types.h \ include/dns/types.h \
include/dns/unreachcache.h \
include/dns/update.h \ include/dns/update.h \
include/dns/validator.h \ include/dns/validator.h \
include/dns/view.h \ include/dns/view.h \
@ -247,6 +248,7 @@ libdns_la_SOURCES = \
tsig.c \ tsig.c \
tsig_p.h \ tsig_p.h \
ttl.c \ ttl.c \
unreachcache.c \
update.c \ update.c \
validator.c \ validator.c \
view.c \ view.c \

View File

@ -167,6 +167,7 @@ typedef struct dns_tsigkeyring dns_tsigkeyring_t;
typedef struct dns_tsigkey dns_tsigkey_t; typedef struct dns_tsigkey dns_tsigkey_t;
typedef uint32_t dns_ttl_t; typedef uint32_t dns_ttl_t;
typedef uint32_t dns_typepair_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_update_state dns_update_state_t;
typedef struct dns_validator dns_validator_t; typedef struct dns_validator dns_validator_t;
typedef struct dns_view dns_view_t; typedef struct dns_view dns_view_t;

View 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
*/

View File

@ -178,6 +178,7 @@ struct dns_view {
dns_dlzdblist_t dlz_unsearched; dns_dlzdblist_t dlz_unsearched;
uint32_t fail_ttl; uint32_t fail_ttl;
dns_badcache_t *failcache; dns_badcache_t *failcache;
dns_unreachcache_t *unreachcache;
unsigned int udpsize; unsigned int udpsize;
uint32_t sig0key_checks_limit; uint32_t sig0key_checks_limit;
uint32_t sig0message_checks_limit; uint32_t sig0message_checks_limit;

View File

@ -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 * 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 void
dns_zonemgr_set_tlsctx_cache(dns_zonemgr_t *zmgr, dns_zonemgr_set_tlsctx_cache(dns_zonemgr_t *zmgr,
isc_tlsctx_cache_t *tlsctx_cache); isc_tlsctx_cache_t *tlsctx_cache);

436
lib/dns/unreachcache.c Normal file
View 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();
}

View File

@ -60,6 +60,7 @@
#include <dns/time.h> #include <dns/time.h>
#include <dns/transport.h> #include <dns/transport.h>
#include <dns/tsig.h> #include <dns/tsig.h>
#include <dns/unreachcache.h>
#include <dns/view.h> #include <dns/view.h>
#include <dns/zone.h> #include <dns/zone.h>
#include <dns/zt.h> #include <dns/zt.h>
@ -84,6 +85,11 @@
*/ */
#define DEFAULT_EDNS_BUFSIZE 1232 #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 void
dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
dns_dispatchmgr_t *dispatchmgr, dns_rdataclass_t rdclass, 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->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); isc_mutex_init(&view->new_zone_lock);
dns_order_create(view->mctx, &view->order); dns_order_create(view->mctx, &view->order);
@ -355,6 +365,9 @@ destroy(dns_view_t *view) {
if (view->failcache != NULL) { if (view->failcache != NULL) {
dns_badcache_destroy(&view->failcache); 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->new_zone_lock);
isc_mutex_destroy(&view->lock); isc_mutex_destroy(&view->lock);
isc_refcount_destroy(&view->references); isc_refcount_destroy(&view->references);
@ -1391,6 +1404,9 @@ dns_view_flushcache(dns_view_t *view, bool fixuponly) {
if (view->failcache != NULL) { if (view->failcache != NULL) {
dns_badcache_flush(view->failcache); dns_badcache_flush(view->failcache);
} }
if (view->unreachcache != NULL) {
dns_unreachcache_flush(view->unreachcache);
}
rcu_read_lock(); rcu_read_lock();
adb = rcu_dereference(view->adb); adb = rcu_dereference(view->adb);

View File

@ -42,6 +42,7 @@
#include <dns/trace.h> #include <dns/trace.h>
#include <dns/transport.h> #include <dns/transport.h>
#include <dns/tsig.h> #include <dns/tsig.h>
#include <dns/unreachcache.h>
#include <dns/view.h> #include <dns/view.h>
#include <dns/xfrin.h> #include <dns/xfrin.h>
#include <dns/zone.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); zmgr = dns_zone_getmgr(xfr->zone);
if (zmgr != NULL) { if (zmgr != NULL) {
dns_zonemgr_unreachabledel(zmgr, &xfr->primaryaddr, dns_view_t *view = dns_zone_getview(xfr->zone);
&xfr->sourceaddr); dns_unreachcache_remove(view->unreachcache, &xfr->primaryaddr,
&xfr->sourceaddr);
} }
if (xfr->tsigkey != NULL && xfr->tsigkey->key != NULL) { if (xfr->tsigkey != NULL && xfr->tsigkey->key != NULL) {
@ -1480,16 +1482,16 @@ failure:
case ISC_R_CONNREFUSED: case ISC_R_CONNREFUSED:
case ISC_R_TIMEDOUT: 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 server has a permanent networking error or
* the connection attempt as timed out. * the connection attempt as timed out.
*/ */
zmgr = dns_zone_getmgr(xfr->zone); zmgr = dns_zone_getmgr(xfr->zone);
if (zmgr != NULL) { if (zmgr != NULL) {
isc_time_t now = isc_time_now(); dns_view_t *view = dns_zone_getview(xfr->zone);
dns_unreachcache_add(view->unreachcache,
dns_zonemgr_unreachableadd(zmgr, &xfr->primaryaddr, &xfr->primaryaddr,
&xfr->sourceaddr, &now); &xfr->sourceaddr);
} }
break; break;
default: default:

View File

@ -87,6 +87,7 @@
#include <dns/time.h> #include <dns/time.h>
#include <dns/tsig.h> #include <dns/tsig.h>
#include <dns/ttl.h> #include <dns/ttl.h>
#include <dns/unreachcache.h>
#include <dns/update.h> #include <dns/update.h>
#include <dns/xfrin.h> #include <dns/xfrin.h>
#include <dns/zone.h> #include <dns/zone.h>
@ -600,9 +601,6 @@ typedef enum {
* load. */ * load. */
} dns_zoneloadflag_t; } dns_zoneloadflag_t;
#define UNREACH_CACHE_SIZE 10U
#define UNREACH_HOLD_TIME 600 /* 10 minutes */
#define CHECK(op) \ #define CHECK(op) \
do { \ do { \
result = (op); \ result = (op); \
@ -610,14 +608,6 @@ typedef enum {
goto failure; \ goto failure; \
} while (0) } 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 { struct dns_zonemgr {
unsigned int magic; unsigned int magic;
isc_mem_t *mctx; isc_mem_t *mctx;
@ -632,7 +622,6 @@ struct dns_zonemgr {
isc_ratelimiter_t *startupnotifyrl; isc_ratelimiter_t *startupnotifyrl;
isc_ratelimiter_t *startuprefreshrl; isc_ratelimiter_t *startuprefreshrl;
isc_rwlock_t rwlock; isc_rwlock_t rwlock;
isc_rwlock_t urlock;
/* Locked by rwlock. */ /* Locked by rwlock. */
dns_zonelist_t zones; dns_zonelist_t zones;
@ -648,10 +637,6 @@ struct dns_zonemgr {
unsigned int serialqueryrate; unsigned int serialqueryrate;
unsigned int startupserialqueryrate; unsigned int startupserialqueryrate;
/* Locked by urlock. */
/* LRU cache */
struct dns_unreachable unreachable[UNREACH_CACHE_SIZE];
dns_keymgmt_t *keymgmt; dns_keymgmt_t *keymgmt;
isc_tlsctx_cache_t *tlsctx_cache; isc_tlsctx_cache_t *tlsctx_cache;
@ -13309,7 +13294,6 @@ stub_glue_response(void *arg) {
uint32_t addr_count, cnamecnt; uint32_t addr_count, cnamecnt;
isc_result_t result; isc_result_t result;
isc_sockaddr_t curraddr; isc_sockaddr_t curraddr;
isc_time_t now;
dns_rdataset_t *addr_rdataset = NULL; dns_rdataset_t *addr_rdataset = NULL;
dns_dbnode_t *node = NULL; dns_dbnode_t *node = NULL;
@ -13319,8 +13303,6 @@ stub_glue_response(void *arg) {
ENTER; ENTER;
now = isc_time_now();
LOCK_ZONE(zone); LOCK_ZONE(zone);
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { 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)); isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
if (dns_request_getresult(request) != ISC_R_SUCCESS) { if (dns_request_getresult(request) != ISC_R_SUCCESS) {
dns_zonemgr_unreachableadd(zone->zmgr, &curraddr, dns_unreachcache_add(zone->view->unreachcache, &curraddr,
&zone->sourceaddr, &now); &zone->sourceaddr);
dns_zone_log(zone, ISC_LOG_INFO, dns_zone_log(zone, ISC_LOG_INFO,
"could not refresh stub from primary %s" "could not refresh stub from primary %s"
" (source %s): %s", " (source %s): %s",
@ -13487,7 +13469,7 @@ cleanup:
/* If last request, release all related resources */ /* If last request, release all related resources */
if (atomic_fetch_sub_release(&stub->pending_requests, 1) == 1) { if (atomic_fetch_sub_release(&stub->pending_requests, 1) == 1) {
isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args)); 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); UNLOCK_ZONE(zone);
stub->magic = 0; stub->magic = 0;
dns_zone_idetach(&stub->zone); dns_zone_idetach(&stub->zone);
@ -13762,8 +13744,8 @@ stub_callback(void *arg) {
} }
FALLTHROUGH; FALLTHROUGH;
default: default:
dns_zonemgr_unreachableadd(zone->zmgr, &curraddr, dns_unreachcache_add(zone->view->unreachcache, &curraddr,
&zone->sourceaddr, &now); &zone->sourceaddr);
dns_zone_log(zone, ISC_LOG_INFO, dns_zone_log(zone, ISC_LOG_INFO,
"could not refresh stub from primary " "could not refresh stub from primary "
"%s (source %s): %s", "%s (source %s): %s",
@ -14117,9 +14099,9 @@ refresh_callback(void *arg) {
zone->type == dns_zone_redirect) && zone->type == dns_zone_redirect) &&
DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH)) DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH))
{ {
if (!dns_zonemgr_unreachable( if (dns_unreachcache_find(
zone->zmgr, &curraddr, zone->view->unreachcache, &curraddr,
&zone->sourceaddr, &now)) &zone->sourceaddr) != ISC_R_SUCCESS)
{ {
DNS_ZONE_SETFLAG( DNS_ZONE_SETFLAG(
zone, zone,
@ -14361,8 +14343,8 @@ refresh_callback(void *arg) {
DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) || DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
isc_serial_gt(serial, oldserial)) isc_serial_gt(serial, oldserial))
{ {
if (dns_zonemgr_unreachable(zone->zmgr, &curraddr, if (dns_unreachcache_find(zone->view->unreachcache, &curraddr,
&zone->sourceaddr, &now)) &zone->sourceaddr) == ISC_R_SUCCESS)
{ {
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN,
ISC_LOG_INFO, ISC_LOG_INFO,
@ -15727,7 +15709,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
UNLOCK_ZONE(zone); UNLOCK_ZONE(zone);
if (to != NULL) { if (to != NULL) {
dns_zonemgr_unreachabledel(zone->zmgr, from, to); dns_unreachcache_remove(zone->view->unreachcache, from, to);
} }
dns_zone_refresh(zone); dns_zone_refresh(zone);
return ISC_R_SUCCESS; return ISC_R_SUCCESS;
@ -18521,7 +18503,6 @@ got_transfer_quota(void *arg) {
isc_netaddr_t primaryip; isc_netaddr_t primaryip;
isc_sockaddr_t primaryaddr; isc_sockaddr_t primaryaddr;
isc_sockaddr_t sourceaddr; isc_sockaddr_t sourceaddr;
isc_time_t now;
dns_transport_type_t soa_transport_type = DNS_TRANSPORT_NONE; dns_transport_type_t soa_transport_type = DNS_TRANSPORT_NONE;
const char *soa_before = ""; const char *soa_before = "";
bool loaded; bool loaded;
@ -18533,12 +18514,10 @@ got_transfer_quota(void *arg) {
return; return;
} }
now = isc_time_now();
primaryaddr = dns_remote_curraddr(&zone->primaries); primaryaddr = dns_remote_curraddr(&zone->primaries);
isc_sockaddr_format(&primaryaddr, primary, sizeof(primary)); isc_sockaddr_format(&primaryaddr, primary, sizeof(primary));
if (dns_zonemgr_unreachable(zone->zmgr, &primaryaddr, &zone->sourceaddr, if (dns_unreachcache_find(zone->view->unreachcache, &primaryaddr,
&now)) &zone->sourceaddr) == ISC_R_SUCCESS)
{ {
isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source)); isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO, 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->zones);
ISC_LIST_INIT(zmgr->waiting_for_xfrin); ISC_LIST_INIT(zmgr->waiting_for_xfrin);
ISC_LIST_INIT(zmgr->xfrin_in_progress); 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); isc_rwlock_init(&zmgr->rwlock);
/* Unreachable lock. */
isc_rwlock_init(&zmgr->urlock);
isc_ratelimiter_create(loop, &zmgr->checkdsrl); isc_ratelimiter_create(loop, &zmgr->checkdsrl);
isc_ratelimiter_create(loop, &zmgr->notifyrl); isc_ratelimiter_create(loop, &zmgr->notifyrl);
isc_ratelimiter_create(loop, &zmgr->refreshrl); 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, isc_mem_cput(zmgr->mctx, zmgr->mctxpool, zmgr->workers,
sizeof(zmgr->mctxpool[0])); sizeof(zmgr->mctxpool[0]));
isc_rwlock_destroy(&zmgr->urlock);
isc_rwlock_destroy(&zmgr->rwlock); isc_rwlock_destroy(&zmgr->rwlock);
isc_rwlock_destroy(&zmgr->tlsctx_cache_rwlock); isc_rwlock_destroy(&zmgr->tlsctx_cache_rwlock);
@ -19701,106 +19672,6 @@ dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
return zmgr->serialqueryrate; 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 void
dns_zone_stopxfr(dns_zone_t *zone) { dns_zone_stopxfr(dns_zone_t *zone) {
dns_xfrin_t *xfr = NULL; dns_xfrin_t *xfr = NULL;

View File

@ -50,6 +50,7 @@ check_PROGRAMS = \
time_test \ time_test \
transport_test \ transport_test \
tsig_test \ tsig_test \
unreachcache_test \
update_test \ update_test \
zonefile_test \ zonefile_test \
zonemgr_test \ zonemgr_test \

View 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

View File

@ -125,75 +125,10 @@ ISC_LOOP_TEST_IMPL(zonemgr_createzone) {
isc_loopmgr_shutdown(loopmgr); 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_LIST_START
ISC_TEST_ENTRY_CUSTOM(zonemgr_create, setup_test, teardown_test) 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_managezone, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(zonemgr_createzone, 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_LIST_END
ISC_TEST_MAIN ISC_TEST_MAIN