]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/nsrep: persistent NS RTT/reputation tracking
authorMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 1 Jun 2015 08:50:13 +0000 (10:50 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 1 Jun 2015 08:50:13 +0000 (10:50 +0200)
score has now meaning of ‘RTT’, maximum RTT is 10s
which is also the penalty for timeout
unknown servers are favorized as 10ms servers to
encourage resolver to try them out, if they contain
unknown glue they are most favourable

lib/nsrep.c
lib/nsrep.h
lib/resolve.c
lib/resolve.h

index 2369a0457e13c2b4d9a57f7ac1ec51a6d7ab4e86..45b0be77069516b9e9525ec7431bf559df580618 100644 (file)
@@ -18,6 +18,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
+#include <libknot/internal/sockaddr.h>
 
 #include "lib/nsrep.h"
 #include "lib/defines.h"
@@ -36,6 +37,7 @@ static void update_nsrep(struct kr_nsrep *ns, const knot_dname_t *name, uint8_t
        ns->name = name;
        ns->score = score;
        if (addr == NULL) {
+               ns->addr.ip.sa_family = AF_UNSPEC;
                return;
        }
 
@@ -54,28 +56,68 @@ static void update_nsrep(struct kr_nsrep *ns, const knot_dname_t *name, uint8_t
 
 static int eval_nsrep(const char *k, void *v, void *baton)
 {
-       unsigned score = KR_NS_VALID;
        struct kr_nsrep *ns = baton;
+       unsigned score = ns->score;
        pack_t *addr_set = v;
        uint8_t *addr = NULL;
 
        /* Name server is better candidate if it has address record. */
-       if (addr_set->len > 0) {
-               addr = pack_head(*addr_set);
-               score += 1;
+       uint8_t *it = pack_head(*addr_set);
+       while (it != pack_tail(*addr_set)) {
+               void *val = pack_obj_val(it);
+               size_t len = pack_obj_len(it);
+               unsigned *cached = lru_get(ns->repcache, val, len);
+               unsigned addr_score = (cached) ? *cached : KR_NS_UNKNOWN / 2;
+               /** @todo Favorize IPv6 */
+               if (addr_score <= score) {
+                       addr = it;
+                       score = addr_score;
+               }
+               it = pack_obj_next(it);
+       }
+       /* No known address */
+       if (!addr) {
+               score = KR_NS_UNKNOWN;
        }
 
        /* Update best scoring nameserver. */
-       if (ns->score < score) {
+       if (score < ns->score) {
                update_nsrep(ns, (const knot_dname_t *)k, addr, score);
        }
 
        return kr_ok();
 }
 
-int kr_nsrep_elect(struct kr_nsrep *ns, map_t *nsset)
+int kr_nsrep_elect(struct kr_nsrep *ns, map_t *nsset, kr_nsrep_lru_t *repcache)
 {
+       ns->repcache = repcache;
        ns->addr.ip.sa_family = AF_UNSPEC;
-       ns->score = KR_NS_INVALID;
+       ns->score = KR_NS_MAX_SCORE + 1;
        return map_walk(nsset, eval_nsrep, ns);
 }
+
+int kr_nsrep_update(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *repcache)
+{
+       if (!ns || !repcache || ns->addr.ip.sa_family == AF_UNSPEC) {
+               return kr_error(EINVAL);
+       }
+
+       char *addr = kr_nsrep_inaddr(ns->addr);
+       size_t addr_len = kr_nsrep_inaddr_len(ns->addr);
+       unsigned *cur = lru_set(repcache, addr, addr_len);
+       if (!cur) {
+               return kr_error(ENOMEM);
+       }
+       /* Score limits */
+       if (score > KR_NS_MAX_SCORE) {
+               score = KR_NS_MAX_SCORE;
+       }
+       /* Set initial value or smooth over last two measurements */
+       if (*cur != 0) {
+               *cur = (*cur + score) / 2;
+       } else {
+       /* First measurement, reset */
+               *cur = score;
+       }
+       return kr_ok();
+}
index 13cdef59ecb1fd9bdd051c2c1bcf21e542ab6bbf..11dbc7e40f989b4029204ec0f06e16b76d507124 100644 (file)
 
 #include <netinet/in.h>
 #include <libknot/dname.h>
+#include <limits.h>
 
 #include "lib/generic/map.h"
+#include "lib/generic/lru.h"
 
 /** 
-  * Special values for nameserver score.
-  * All positive values mean valid nameserver.
+  * Special values for nameserver score (RTT in miliseconds)
   */
 enum kr_ns_score {
-       KR_NS_INVALID = 0,
-       KR_NS_VALID   = 1
+       KR_NS_MAX_SCORE = 10 * 1000,
+       KR_NS_TIMEOUT   = KR_NS_MAX_SCORE,
+       KR_NS_UNKNOWN   = 10
 };
 
+/**
+ * NS reputation/QoS tracking.
+ */
+typedef lru_hash(unsigned) kr_nsrep_lru_t;
+
 /**
  * Name server representation.
  * Contains extra information about the name server, e.g. score
@@ -39,6 +46,7 @@ struct kr_nsrep
 {
        unsigned score;                  /**< Server score */
        const knot_dname_t *name;        /**< Server name */
+       kr_nsrep_lru_t *repcache;        /**< Reputation cache pointer */
        union {
                struct sockaddr ip;
                struct sockaddr_in ip4;
@@ -55,8 +63,21 @@ struct kr_nsrep
 
 /**
  * Elect best nameserver/address pair from the nsset.
- * @param  ns    updated NS representation
- * @param  nsset NS set to choose from
- * @return       score, see enum kr_ns_score
+ * @param  ns           updated NS representation
+ * @param  nsset        NS set to choose from
+ * @param  repcache     reputation storage
+ * @return              score, see enum kr_ns_score
+ */
+int kr_nsrep_elect(struct kr_nsrep *ns, map_t *nsset, kr_nsrep_lru_t *repcache);
+
+/**
+ * Update NS quality information.
+ *
+ * @brief Reputation is smoothed over last N measurements.
+ * 
+ * @param  ns           updated NS representation
+ * @param  score        new score (i.e. RTT), see enum kr_ns_score
+ * @param  reputation   reputation storage
+ * @return              0 on success, error code on failure
  */
-int kr_nsrep_elect(struct kr_nsrep *ns, map_t *nsset);
+int kr_nsrep_update(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *repcache);
index 822ae0b9309740c8528c4ced284e7ca838d9763c..cd9d72d6b730204582265a01f3c9d4665338f48c 100644 (file)
 
 #define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), "resl",  fmt)
 
+/** @internal Subtract time (best effort) */
+float time_diff(struct timeval *begin, struct timeval *end)
+{
+       return (end->tv_sec - begin->tv_sec) * 1000 +
+              (end->tv_usec - begin->tv_usec) / 1000.0;
+
+}
+
 /** Invalidate current NS/addr pair. */
 static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
 {
-       uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr);
-       size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr);
-       knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
-       knot_rdata_init(rdata, addr_len, addr, 0);
-       return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
+       if (qry->ns.addr.ip.sa_family != AF_UNSPEC) {
+               uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr);
+               size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr);
+               knot_rdata_t rdata[knot_rdata_array_size(addr_len)];
+               knot_rdata_init(rdata, addr_len, addr, 0);
+               return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
+       } else {
+               return kr_zonecut_del(&qry->zone_cut, qry->ns.name, NULL);
+       }
 }
 
 static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param)
@@ -334,6 +346,8 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
                        DEBUG_MSG("=> ns unreachable, retrying over TCP\n");
                        qry->flags |= QUERY_TCP;
                        return KNOT_STATE_CONSUME; /* Try again */
+               } else {
+                       kr_nsrep_update(&qry->ns, KR_NS_TIMEOUT, qry->ns.repcache);
                }
        } else {
                state = knot_overlay_consume(&request->overlay, packet);
@@ -345,6 +359,11 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
                if (invalidate_ns(rplan, qry) == 0) {
                        qry->flags &= ~QUERY_TCP;
                }
+       /* Track RTT for iterative answers */
+       } else if (!(qry->flags & QUERY_CACHED)) {
+               struct timeval now;
+               gettimeofday(&now, NULL);
+               kr_nsrep_update(&qry->ns, time_diff(&qry->timestamp, &now), qry->ns.repcache);
        }
 
        /* Pop query if resolved. */
@@ -399,7 +418,7 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
         */
        if (qry->flags & QUERY_AWAIT_CUT) {
                struct kr_cache_txn txn;
-               if (kr_cache_txn_begin(&rplan->context->cache, &txn, NAMEDB_RDONLY) != 0) {
+               if (kr_cache_txn_begin(&request->ctx->cache, &txn, NAMEDB_RDONLY) != 0) {
                        kr_zonecut_set_sbelt(&qry->zone_cut);
                } else {
                        kr_zonecut_find_cached(&qry->zone_cut, qry->sname, &txn, qry->timestamp.tv_sec);
@@ -410,8 +429,8 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
 
 ns_election:
        /* Elect best nameserver candidate */
-       kr_nsrep_elect(&qry->ns, &qry->zone_cut.nsset);
-       if (qry->ns.score < KR_NS_VALID) {
+       kr_nsrep_elect(&qry->ns, &qry->zone_cut.nsset, request->ctx->nsrep);
+       if (qry->ns.score > KR_NS_MAX_SCORE) {
                DEBUG_MSG("=> no valid NS left\n");
                knot_overlay_reset(&request->overlay);
                kr_rplan_pop(rplan, qry);
@@ -419,6 +438,7 @@ ns_election:
        } else {
                if (qry->ns.addr.ip.sa_family == AF_UNSPEC) {
                        if (ns_resolve_addr(qry, request) != 0) {
+                               qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
                                goto ns_election; /* Must try different NS */
                        }
                        knot_overlay_reset(&request->overlay);
@@ -435,7 +455,7 @@ ns_election:
        struct sockaddr *addr = &qry->ns.addr.ip;
        inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr), ns_str, sizeof(ns_str));
        knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str));
-       DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str);
+       DEBUG_MSG("=> querying: '%s' score: %u zone cut: '%s' m12n: '%s'\n", ns_str, qry->ns.score, zonecut_str, qname_str);
 #endif
 
        /* Prepare additional query */
@@ -443,6 +463,7 @@ ns_election:
        if (ret != 0) {
                return KNOT_STATE_FAIL;
        }
+       gettimeofday(&qry->timestamp, NULL);
        *dst = &qry->ns.addr.ip;
        *type = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM;
        return state;
index 1b17d8b23be972a277f78cfa92b1bdea57c31c19..e03629bd89c11528ec724d73d8699cc105f0b8fd 100644 (file)
@@ -21,6 +21,7 @@
 #include <libknot/packet/pkt.h>
 
 #include "lib/generic/array.h"
+#include "lib/nsrep.h"
 #include "lib/rplan.h"
 #include "lib/module.h"
 #include "lib/cache.h"
@@ -100,6 +101,7 @@ struct kr_context
 {
        mm_ctx_t *pool;
        struct kr_cache cache;
+       kr_nsrep_lru_t *nsrep;
        module_array_t *modules;
        uint32_t options;
 };