]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resolve: track rtt of all tried servers
authorMarek Vavrusa <marek@vavrusa.com>
Wed, 24 Aug 2016 00:28:47 +0000 (17:28 -0700)
committerMarek Vavrusa <marek@vavrusa.com>
Wed, 24 Aug 2016 00:28:47 +0000 (17:28 -0700)
when doing fast retransmit, each address may be
contacted 1-N times, but previously only cumulative
RTT was tracked for the NS that sent the answer.

now the approximate query start time is subtracted
from cumulative RTT to give an idea how long it
took since the query was actually sent, and all
the NSs that didn't respond within their retransmit
windows are penalised too

daemon/worker.h
lib/nsrep.c
lib/nsrep.h
lib/resolve.c

index 349a4d7daeb9ae5f9f3492a9ab0b971de75b0848..a02d46e2ca591fb22249e285a24395ae22e2df66 100644 (file)
@@ -21,7 +21,7 @@
 #include "lib/generic/map.h"
 
 /** @internal Number of request within timeout window. */
-#define MAX_PENDING (KR_NSREP_MAXADDR + (KR_NSREP_MAXADDR / 2))
+#define MAX_PENDING KR_NSREP_MAXADDR
 
 /** @cond internal Freelist of available mempools. */
 typedef array_t(void *) mp_freelist_t;
index cad0eb6b722013b75a2697b3aadd87cf50d52b87..71677277d80f8961d2cfee206cf97153cd98d7d4 100644 (file)
@@ -260,6 +260,7 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr,
        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;
+       case KR_NS_MAX:    *cur = MAX(*cur, score); break;
        default: break;
        }
        return kr_ok();
index be668d324122806bd2fe44b29f1c62fe4f065137..5ae272adf0e6ad90a24566b44793940b39809dd7 100644 (file)
@@ -54,7 +54,8 @@ enum kr_ns_rep {
 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 */
+       KR_NS_ADD,        /**< Increment current value */
+       KR_NS_MAX         /**< Set to maximum of current/proposed value. */
 };
 
 /**
index 976a9517a620a49be572125996c709c8a1bd2f8d..15db1e0151a0df936b93de6dff051e0451548f0f 100644 (file)
@@ -488,10 +488,93 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
        return request->state;
 }
 
+KR_PURE static bool kr_inaddr_equal(const struct sockaddr *a, const struct sockaddr *b)
+{
+       const int a_len = kr_inaddr_len(a);
+       const int b_len = kr_inaddr_len(b);
+       return a_len == b_len && memcmp(kr_inaddr(a), kr_inaddr(b), a_len) == 0;
+}
+
+static void update_nslist_rtt(struct kr_context *ctx, struct kr_query *qry, const struct sockaddr *src)
+{
+       /* Do not track in safe mode. */
+       if (qry->flags & QUERY_SAFEMODE) {
+               return;
+       }
+
+       /* Calculate total resolution time from the time the query was generated. */
+       struct timeval now;
+       gettimeofday(&now, NULL);
+       unsigned elapsed = time_diff(&qry->timestamp, &now);
+
+       /* NSs in the preference list prior to the one who responded will be penalised
+        * with the RETRY timer interval. This is because we know they didn't respond
+        * for N retries, so their RTT must be at least N * RETRY.
+        * The NS in the preference list that responded will have RTT relative to the
+        * time when the query was sent out, not when it was originated.
+        */
+       for (size_t i = 0; i < KR_NSREP_MAXADDR; ++i) {
+               const struct sockaddr *addr = &qry->ns.addr[i].ip;
+               if (addr->sa_family == AF_UNSPEC) {
+                       break;
+               }
+               /* If this address is the source of the answer, update its RTT */
+               if (kr_inaddr_equal(src, addr)) {
+                       kr_nsrep_update_rtt(&qry->ns, addr, elapsed, ctx->cache_rtt, KR_NS_UPDATE);
+                       WITH_DEBUG {
+                               char addr_str[INET6_ADDRSTRLEN];
+                               inet_ntop(addr->sa_family, kr_inaddr(addr), addr_str, sizeof(addr_str));
+                               DEBUG_MSG(qry, "<= server: '%s' rtt: %ld ms\n", addr_str, elapsed);
+                       }
+               } else {
+                       /* Response didn't come from this IP, but we know the RTT must be at least
+                        * several RETRY timer tries, e.g. if we have addresses [a, b, c] and we have
+                        * tried [a, b] when the answer from 'a' came after 350ms, then we know
+                        * that 'b' didn't respond for at least 350 - (1 * 300) ms. We can't say that
+                        * its RTT is 50ms, but we can say that its score shouldn't be less than 50. */
+                        kr_nsrep_update_rtt(&qry->ns, addr, elapsed, ctx->cache_rtt, KR_NS_MAX);
+                        WITH_DEBUG {
+                               char addr_str[INET6_ADDRSTRLEN];
+                               inet_ntop(addr->sa_family, kr_inaddr(addr), addr_str, sizeof(addr_str));
+                               DEBUG_MSG(qry, "<= server: '%s' rtt: >=%ld ms\n", addr_str, elapsed);
+                        }
+               }
+               /* Subtract query start time from elapsed time */
+               if (elapsed < KR_CONN_RETRY) {
+                       break;
+               }
+               elapsed = elapsed - KR_CONN_RETRY;
+       }
+}
+
+static void update_nslist_score(struct kr_request *request, struct kr_query *qry, const struct sockaddr *src, knot_pkt_t *packet)
+{
+       struct kr_context *ctx = request->ctx;
+       /* On sucessful answer, update preference list RTT and penalise timer  */
+       if (request->state != KNOT_STATE_FAIL) {
+               /* Update RTT information for preference list */
+               update_nslist_rtt(ctx, qry, src);
+               /* Do not complete NS address resolution on soft-fail. */
+               const int rcode = packet ? knot_wire_get_rcode(packet->wire) : 0;
+               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);
+               }
+       /* Penalise resolution failures except validation failures. */
+       } else if (!(qry->flags & QUERY_DNSSEC_BOGUS)) {
+               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));
+                       DEBUG_MSG(qry, "=> server: '%s' flagged as 'bad'\n", addr_str);
+               }
+       }
+}
+
 int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, knot_pkt_t *packet)
 {
        struct kr_rplan *rplan = &request->rplan;
-       struct kr_context *ctx = request->ctx;
 
        /* Empty resolution plan, push packet as the new query */
        if (packet && kr_rplan_empty(rplan)) {
@@ -533,35 +616,7 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
 
        /* Track RTT for iterative answers */
        if (src && !(qry->flags & QUERY_CACHED)) {
-               /* Sucessful answer, track RTT and lift any address resolution requests. */
-               if (request->state != KNOT_STATE_FAIL) {
-                       /* Do not track in safe mode. */
-                       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_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));
-                               }
-                       }
-                       /* Do not complete NS address resolution on soft-fail. */
-                       const int rcode = packet ? knot_wire_get_rcode(packet->wire) : 0;
-                       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_NS_RESET);
-                       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' flagged as 'bad'\n", addr_str);
-                       }
-               }
+               update_nslist_score(request, qry, src, packet);
        }
        /* Resolution failed, invalidate current NS. */
        if (request->state == KNOT_STATE_FAIL) {