diff --git a/daemon/worker.c b/daemon/worker.c index 7ed015a96db24f43a52b0da1e2c2d20187e4c1da..112ce4eb0cf889b1c9b6f281c8cc67312a7cd912 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 8d9796c80ca2f3645121c9935b672592d93ecaa0..a5f653e4eddaf3a8437f91677287ce55fa3f0ef3 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -55,6 +55,7 @@ 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_NSRETRY_LIMIT 4 /* Maximum number of retries per query. */ /* * Defines. @@ -80,4 +81,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 d28184597ca9c9791ab6b1c15b46ae0bb9a4e9d9..99abc54832327fedde9177312aa368b20263c173 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -596,8 +596,18 @@ 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; + if (query->fails >= KR_QUERY_NSRETRY_LIMIT) { + query->fails = 0; /* Reset per-query counter. */ + return resolve_error(pkt, req); + } else { + 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 3fd91d7d0b4543d66f0d0bc14bfec15a94060160..e5c918443724cfdfc398ff82664fda0c874c9c1c 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -24,6 +24,7 @@ #include "lib/resolve.h" #include "lib/defines.h" #include "lib/generic/pack.h" +#include "contrib/ucw/lib.h" /** Some built-in unfairness ... */ #define FAVOUR_IPV6 20 /* 20ms bonus for v6 */ @@ -221,7 +222,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,12 +251,16 @@ 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) { - *cur = (*cur + score) / 2; - } else { - /* First measurement, reset */ - *cur = score; + /* First update is always set. */ + if (*cur == 0) { + umode = KR_NS_RESET; + } + /* Update score, by default smooth over last two measurements. */ + switch (umode) { + case KR_NS_UPDATE: *cur = (*cur + score) / 2; break; + case KR_NS_RESET: *cur = score; break; + case KR_NS_ADD: *cur = MIN(KR_NS_MAX_SCORE - 1, *cur + score); break; + default: break; } return kr_ok(); } @@ -274,4 +280,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 848b28f0b542be9886ddd3c62be7f773a6997776..9f2e0b784174c328ca4b24690ac3fa7b74e57ab0 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -35,6 +35,7 @@ enum kr_ns_score { KR_NS_TIMEOUT = (95 * KR_NS_MAX_SCORE) / 100, KR_NS_LONG = (3 * KR_NS_TIMEOUT) / 4, KR_NS_UNKNOWN = KR_NS_TIMEOUT / 2, + KR_NS_PENALTY = 100, KR_NS_GLUED = 10 }; @@ -47,6 +48,15 @@ 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 */ + KR_NS_ADD /**< Increment current value */ +}; + /** * NS reputation/QoS tracking. */ @@ -111,16 +121,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 or KR_NS_ADD) * @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 9b7416d2e5f68348fc77696d92c37430f8ece17d..3782fcd842d290c03ba863720c120434fe7be55c 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -457,17 +457,23 @@ 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); + /* Do not complete NS address resolution on soft-fail. */ + const int rcode = knot_wire_get_rcode(packet->wire); + if (rcode != KNOT_RCODE_SERVFAIL && rcode != KNOT_RCODE_REFUSED) { + qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4); + } else { /* Penalize SERVFAILs. */ + kr_nsrep_update_rtt(&qry->ns, src, KR_NS_PENALTY, ctx->cache_rtt, KR_NS_ADD); + } /* 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,13 +703,15 @@ 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)) { kr_nsrep_elect_addr(qry, request->ctx); } else if (!qry->ns.name || !(qry->flags & (QUERY_TCP|QUERY_STUB))) { /* Keep NS when requerying/stub. */ diff --git a/lib/rplan.h b/lib/rplan.h index a804658b18720b3ffbf56541f259c210a15ee9b3..d0e8e2f3fcb2c553c1641a44ec4ddb91e17e9603 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -46,7 +46,7 @@ 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. */ \ /** Query flags */ enum kr_query_flag { @@ -70,6 +70,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 5cb5935b3c139938689132c7199b14f3421bdffe..85cbf82244059113ff3149da04f99089dddc1c5b 160000 --- a/tests/deckard +++ b/tests/deckard @@ -1 +1 @@ -Subproject commit 5cb5935b3c139938689132c7199b14f3421bdffe +Subproject commit 85cbf82244059113ff3149da04f99089dddc1c5b diff --git a/tests/tests.mk b/tests/tests.mk index c971de628f3d92d2442d65b5d4afbdf4969df828..1a82d4fcba4792df01092f4cf728f4d6e3da5787 100644 --- a/tests/tests.mk +++ b/tests/tests.mk @@ -12,7 +12,7 @@ TEMPLATE := template/kresd.j2 $(deckard_DIR)/Makefile: @git submodule update --init --recursive check-integration: $(deckard_DIR)/Makefile - @[ ! -d $(deckard_DIR)/contrib/libswrap/obj ] && mkdir $(deckard_DIR)/contrib/libswrap/obj + @mkdir -p $(deckard_DIR)/contrib/libswrap/obj @$(MAKE) -s -C $(deckard_DIR) TESTS=$(TESTS) DAEMON=$(abspath daemon/kresd) TEMPLATE=$(TEMPLATE) DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) deckard: check-integration