From: Grigorii Demidov Date: Tue, 3 May 2016 09:10:23 +0000 (+0200) Subject: lib/resolve,layer/iterate: processing for REFUSED & SERVFAIL rcodes X-Git-Tag: v1.0.0~24^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc9ef68147ce79dc857a06a40781a0bf5aa0cc5b;p=thirdparty%2Fknot-resolver.git lib/resolve,layer/iterate: processing for REFUSED & SERVFAIL rcodes --- diff --git a/daemon/worker.c b/daemon/worker.c index 7ed015a96..112ce4eb0 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -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 */ diff --git a/lib/defines.h b/lib/defines.h index 8d9796c80..895a1b66b 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -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 */ diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index d28184597..92209af28 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -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 : "??"); diff --git a/lib/nsrep.c b/lib/nsrep.c index 3fd91d7d0..0fad08163 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -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 +} diff --git a/lib/nsrep.h b/lib/nsrep.h index 848b28f0b..70a73aac3 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -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. diff --git a/lib/resolve.c b/lib/resolve.c index 9b7416d2e..dd70be6b7 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -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. */ diff --git a/lib/rplan.h b/lib/rplan.h index a804658b1..3b2d965b7 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -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; diff --git a/tests/deckard b/tests/deckard index 900b169f9..85cbf8224 160000 --- a/tests/deckard +++ b/tests/deckard @@ -1 +1 @@ -Subproject commit 900b169f958dcebf2fb7bba69538e6d316c06b7c +Subproject commit 85cbf82244059113ff3149da04f99089dddc1c5b