]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resolve,layer/iterate: processing for REFUSED & SERVFAIL rcodes
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Tue, 3 May 2016 09:10:23 +0000 (11:10 +0200)
committerMarek Vavrusa <marek@vavrusa.com>
Fri, 6 May 2016 06:36:19 +0000 (23:36 -0700)
daemon/worker.c
lib/defines.h
lib/layer/iterate.c
lib/nsrep.c
lib/nsrep.h
lib/resolve.c
lib/rplan.h
tests/deckard

index 7ed015a96db24f43a52b0da1e2c2d20187e4c1da..112ce4eb0cf889b1c9b6f281c8cc67312a7cd912 100644 (file)
@@ -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 */
index 8d9796c80ca2f3645121c9935b672592d93ecaa0..895a1b66bf912dd608c2a40a333f40bd7d8713b7 100644 (file)
@@ -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 */
index d28184597ca9c9791ab6b1c15b46ae0bb9a4e9d9..92209af281b9578556d6e8380be1eaf79aac894f 100644 (file)
@@ -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 : "??");
index 3fd91d7d0b4543d66f0d0bc14bfec15a94060160..0fad08163952e2a9f7d873a2bddf3661bfe17d1a 100644 (file)
@@ -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
+}
index 848b28f0b542be9886ddd3c62be7f773a6997776..70a73aac313e34b26de5058c2f4065aa4fb99e37 100644 (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.
index 9b7416d2e5f68348fc77696d92c37430f8ece17d..dd70be6b7d571a74d95f82b40e9d2da159b8fda5 100644 (file)
@@ -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. */
index a804658b18720b3ffbf56541f259c210a15ee9b3..3b2d965b7ba8f177e32da89f51425ebe1498248c 100644 (file)
@@ -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;
index 900b169f958dcebf2fb7bba69538e6d316c06b7c..85cbf82244059113ff3149da04f99089dddc1c5b 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 900b169f958dcebf2fb7bba69538e6d316c06b7c
+Subproject commit 85cbf82244059113ff3149da04f99089dddc1c5b