Commit bc9ef681 authored by Grigorii Demidov's avatar Grigorii Demidov Committed by Marek Vavrusa

lib/resolve,layer/iterate: processing for REFUSED & SERVFAIL rcodes

parent cf8e5717
......@@ -493,7 +493,8 @@ static void on_timeout(uv_timer_t *req)
inet_ntop(choice->sa_family, kr_inaddr(choice), addr_str, sizeof(addr_str));
QRDEBUG(qry, "wrkr", "=> server: '%s' flagged as 'bad'\n", addr_str);
}
kr_nsrep_update_rtt(&qry->ns, choice, KR_NS_TIMEOUT, worker->engine->resolver.cache_rtt);
kr_nsrep_update_rtt(&qry->ns, choice, KR_NS_TIMEOUT,
worker->engine->resolver.cache_rtt, KR_NS_UPDATE);
}
}
/* Release timer handle */
......
......@@ -55,6 +55,10 @@ static inline int __attribute__((__cold__)) kr_error(int x) {
#define KR_ITER_LIMIT 50 /* Built-in iterator limit */
#define KR_CNAME_CHAIN_LIMIT 40 /* Built-in maximum CNAME chain length */
#define KR_TIMEOUT_LIMIT 4 /* Maximum number of retries after timeout. */
#define KR_QUERY_FAIL_LIMIT 40 /* Limit to the number of SERVFAIL\REFUSED
* responses per query
*/
#define KR_QUERY_NSRETRY_LIMIT 3 /* Maximum number of retries for single ns */
/*
* Defines.
......@@ -80,4 +84,4 @@ void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
#define kr_asan_poison(addr, size)
#define kr_asan_unpoison(addr, size)
#endif
/* @endcond */
\ No newline at end of file
/* @endcond */
......@@ -596,8 +596,20 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
switch(knot_wire_get_rcode(pkt->wire)) {
case KNOT_RCODE_NOERROR:
case KNOT_RCODE_NXDOMAIN:
case KNOT_RCODE_REFUSED:
break; /* OK */
case KNOT_RCODE_REFUSED:
case KNOT_RCODE_SERVFAIL: {
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
query->fails += 1;
query->ns.fails += 1;
if (query->fails >= KR_QUERY_FAIL_LIMIT ||
query->ns.fails >= KR_QUERY_NSRETRY_LIMIT) {
return resolve_error(pkt, req);
} else {
query->flags |= QUERY_SERVFAIL;
return KNOT_STATE_CONSUME;
}
}
case KNOT_RCODE_FORMERR:
case KNOT_RCODE_NOTIMPL:
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
......
......@@ -176,6 +176,7 @@ int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len)
qry->ns.name = (const uint8_t *)"";
qry->ns.score = KR_NS_UNKNOWN;
qry->ns.reputation = 0;
qry->ns.fails = 0;
update_nsrep(&qry->ns, 0, addr, addr_len);
update_nsrep(&qry->ns, 1, NULL, 0);
return kr_ok();
......@@ -185,6 +186,7 @@ int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len)
(ns)->ctx = (ctx_); \
(ns)->addr[0].ip.sa_family = AF_UNSPEC; \
(ns)->reputation = 0; \
(ns)->fails = 0; \
(ns)->score = KR_NS_MAX_SCORE + 1; \
} while (0)
......@@ -221,7 +223,8 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx)
#undef ELECT_INIT
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache)
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr,
unsigned score, kr_nsrep_lru_t *cache, int umode)
{
if (!ns || !cache || ns->addr[0].ip.sa_family == AF_UNSPEC) {
return kr_error(EINVAL);
......@@ -249,13 +252,15 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsign
if (score <= KR_NS_GLUED) {
score = KR_NS_GLUED + 1;
}
/* Set initial value or smooth over last two measurements */
if (*cur != 0) {
if ((*cur != 0) && (umode == KR_NS_UPDATE)) {
/* In KR_NS_UPDATE mode, calculate smooth over last two measurements */
*cur = (*cur + score) / 2;
} else {
/* First measurement, reset */
/* First measurement or KR_NS_RESET mode, reset */
*cur = score;
}
return kr_ok();
}
......@@ -274,4 +279,4 @@ int kr_nsrep_update_rep(struct kr_nsrep *ns, unsigned reputation, kr_nsrep_lru_t
}
*cur = reputation;
return kr_ok();
}
\ No newline at end of file
}
......@@ -47,6 +47,14 @@ enum kr_ns_rep {
KR_NS_NOEDNS = 1 << 2 /**< NS has no EDNS support */
};
/**
* NS RTT update modes.
*/
enum kr_ns_update_mode {
KR_NS_UPDATE = 0, /**< Update as smooth over last two measurements */
KR_NS_RESET /**< Set to given value */
};
/**
* NS reputation/QoS tracking.
*/
......@@ -64,6 +72,7 @@ struct kr_nsrep
{
unsigned score; /**< NS score */
unsigned reputation; /**< NS reputation */
unsigned fails; /**< NS fail counter */
const knot_dname_t *name; /**< NS name */
struct kr_context *ctx; /**< Resolution context */
union {
......@@ -111,16 +120,18 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx);
/**
* Update NS address RTT information.
*
* @brief Reputation is smoothed over last N measurements.
* @brief In KR_NS_UPDATE mode reputation is smoothed over last N measurements.
*
* @param ns updated NS representation
* @param addr chosen address (NULL for first)
* @param score new score (i.e. RTT), see enum kr_ns_score
* @param cache LRU cache
* @param umode update mode (KR_NS_UPDATE or KR_NS_RESET)
* @return 0 on success, error code on failure
*/
KR_EXPORT
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache);
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr,
unsigned score, kr_nsrep_lru_t *cache, int umode);
/**
* Update NSSET reputation information.
......
......@@ -457,17 +457,19 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
if (!(qry->flags & QUERY_SAFEMODE)) {
struct timeval now;
gettimeofday(&now, NULL);
kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt, KR_NS_UPDATE);
WITH_DEBUG {
char addr_str[INET6_ADDRSTRLEN];
inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
DEBUG_MSG(qry, "<= server: '%s' rtt: %ld ms\n", addr_str, time_diff(&qry->timestamp, &now));
}
}
qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
if (!(qry->flags & QUERY_SERVFAIL)) {
qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
}
/* Do not penalize validation timeouts. */
} else if (!(qry->flags & QUERY_DNSSEC_BOGUS)) {
kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt);
kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt, KR_NS_RESET);
WITH_DEBUG {
char addr_str[INET6_ADDRSTRLEN];
inet_ntop(src->sa_family, kr_inaddr(src), addr_str, sizeof(addr_str));
......@@ -697,14 +699,18 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
ns_election:
/* If the query has already selected a NS and is waiting for IPv4/IPv6 record,
/* If the query has got REFUSED & SERVFAIL, retry with current src up to KR_QUERY_NSRETRY_LIMIT.
* If the query has already selected a NS and is waiting for IPv4/IPv6 record,
* elect best address only, otherwise elect a completely new NS.
*/
if(++ns_election_iter >= KR_ITER_LIMIT) {
DEBUG_MSG(qry, "=> couldn't converge NS selection, bail out\n");
return KNOT_STATE_FAIL;
}
if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
if (qry->flags & QUERY_SERVFAIL) {
qry->flags &= ~QUERY_SERVFAIL;
} else if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
kr_nsrep_elect_addr(qry, request->ctx);
} else if (!qry->ns.name || !(qry->flags & (QUERY_TCP|QUERY_STUB))) { /* Keep NS when requerying/stub. */
/* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */
......
......@@ -46,7 +46,8 @@
X(ALWAYS_CUT, 1 << 18) /**< Always recover zone cut (even if cached). */ \
X(DNSSEC_WEXPAND, 1 << 19) /**< Query response has wildcard expansion. */ \
X(PERMISSIVE, 1 << 20) /**< Permissive resolver mode. */ \
X(STRICT, 1 << 21) /**< Strict resolver mode. */
X(STRICT, 1 << 21) /**< Strict resolver mode. */ \
X(SERVFAIL, 1 << 22) /**< Query response is SERVFAIL or REFUSED. */
/** Query flags */
enum kr_query_flag {
......@@ -70,6 +71,7 @@ struct kr_query {
uint16_t id;
uint32_t flags;
uint32_t secret;
uint16_t fails;
struct timeval timestamp;
struct kr_zonecut zone_cut;
struct kr_nsrep ns;
......
Subproject commit 900b169f958dcebf2fb7bba69538e6d316c06b7c
Subproject commit 85cbf82244059113ff3149da04f99089dddc1c5b
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment