Commit ac55abb4 authored by Vladimír Čunát's avatar Vladimír Čunát

lib/resolve: even better support for forwarder choice

- sort the list instead of just picking the best one
- prefer unknown RTTs to probe them
- verbose output of the choice

Fixes https://gitlab.labs.nic.cz/knot/resolver/issues/125
Fixes https://gitlab.labs.nic.cz/knot/resolver/issues/208
parent 2ee19a2e
...@@ -4,6 +4,7 @@ Knot Resolver 1.3.2 (2017-07-xx) ...@@ -4,6 +4,7 @@ Knot Resolver 1.3.2 (2017-07-xx)
Bugfixes Bugfixes
-------- --------
- daemon: check existence of config file even if rundir isn't specified - daemon: check existence of config file even if rundir isn't specified
- policy.FORWARD and STUB: use RTT tracking to choose servers (#125, #208)
Knot Resolver 1.3.1 (2017-06-23) Knot Resolver 1.3.1 (2017-06-23)
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h>
#include "lib/nsrep.h" #include "lib/nsrep.h"
#include "lib/rplan.h" #include "lib/rplan.h"
#include "lib/resolve.h" #include "lib/resolve.h"
...@@ -336,10 +338,12 @@ int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src) ...@@ -336,10 +338,12 @@ int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src)
return kr_ok(); return kr_ok();
} }
int kr_nsrep_elect_min_rtt(struct kr_query *qry) int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache)
{ {
/* TODO - sorting instead of searching minimum? */ if (!ns || !cache) {
struct kr_nsrep *ns = &qry->ns; assert(false);
return kr_error(EINVAL);
}
if (ns->addr[0].ip.sa_family == AF_UNSPEC) { if (ns->addr[0].ip.sa_family == AF_UNSPEC) {
return kr_error(EINVAL); return kr_error(EINVAL);
...@@ -350,41 +354,52 @@ int kr_nsrep_elect_min_rtt(struct kr_query *qry) ...@@ -350,41 +354,52 @@ int kr_nsrep_elect_min_rtt(struct kr_query *qry)
return kr_ok(); return kr_ok();
} }
const struct kr_context *ctx = ns->ctx; /* Compute the scores. Unfortunately there's no space for scores
if (!ctx) { * along the addresses. */
return kr_ok(); unsigned scores[KR_NSREP_MAXADDR];
int i;
for (i = 0; i < KR_NSREP_MAXADDR; ++i) {
const struct sockaddr *sa = &ns->addr[i].ip;
if (sa->sa_family == AF_UNSPEC) {
break;
}
unsigned *score = lru_get_try(cache, kr_inaddr(sa),
kr_family_len(sa->sa_family));
if (!score) {
scores[i] = 1; /* prefer unknown to probe RTT */
} else if ((kr_rand_uint(100) < 10)
&& (kr_rand_uint(KR_NS_MAX_SCORE) >= *score)) {
/* some probability to bump bad ones up for re-probe */
scores[i] = 1;
} else {
scores[i] = *score;
}
WITH_VERBOSE {
char sa_str[INET6_ADDRSTRLEN];
inet_ntop(sa->sa_family, kr_inaddr(sa), sa_str, sizeof(sa_str));
kr_log_verbose("[ ][nsre] score %d for %s;\t cached RTT: %d\n",
scores[i], sa_str, score ? *score : -1);
}
} }
const struct sockaddr *sock = (const struct sockaddr *)(&ns->addr[0]); /* Select-sort the addresses. */
unsigned *score = lru_get_try(ctx->cache_rtt, kr_inaddr(sock), const int count = i;
kr_family_len(sock->sa_family)); for (i = 0; i < count - 1; ++i) {
unsigned minimal_score = (score) ? *score : KR_NS_MAX_SCORE + 1; /* find min from i onwards */
int i = 1; int min_i = i;
sock = (const struct sockaddr *)(&ns->addr[i]); for (int j = i + 1; j < count; ++j) {
do { if (scores[j] < scores[min_i]) {
score = lru_get_try(ctx->cache_rtt, kr_inaddr(sock), min_i = j;
kr_family_len(sock->sa_family));
if (score) {
if (*score < minimal_score) {
union inaddr temp_addr = ns->addr[0];
ns->addr[0] = ns->addr[i];
ns->addr[i] = temp_addr;
ns->score = *score;
ns->reputation = 0;
minimal_score = ns->score;
} else if ((kr_rand_uint(100) < 10) &&
(kr_rand_uint(KR_NS_MAX_SCORE) >= *score)) {
/* long distance probe */
union inaddr temp_addr = ns->addr[0];
ns->addr[0] = ns->addr[i];
ns->addr[i] = temp_addr;
ns->score = *score;
ns->reputation = 0;
break;
} }
} }
sock = (const struct sockaddr *)(&ns->addr[++i]); /* swap the indices */
} while ((i < KR_NSREP_MAXADDR) && (sock->sa_family != AF_UNSPEC)); if (min_i != i) {
SWAP(scores[min_i], scores[i]);
SWAP(ns->addr[min_i], ns->addr[i]);
}
}
ns->score = scores[0];
ns->reputation = 0;
return kr_ok(); return kr_ok();
} }
...@@ -145,10 +145,13 @@ int kr_nsrep_update_rep(struct kr_nsrep *ns, unsigned reputation, kr_nsrep_lru_t ...@@ -145,10 +145,13 @@ int kr_nsrep_update_rep(struct kr_nsrep *ns, unsigned reputation, kr_nsrep_lru_t
int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src); int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src);
/** /**
* Elect address with minimal rtt within current query nsrep list * Sort addresses in the query nsrep list
* @note ns reputation is zeroed * @param ns updated kr_nsrep
* @param qry updated query * @param cache RTT cache
* @return 0 or an error code * @return 0 or an error code
* @note ns reputation is zeroed, as KR_NS_NOIP{4,6} flags are useless
* in STUB/FORWARD mode.
*/ */
KR_EXPORT KR_EXPORT
int kr_nsrep_elect_min_rtt(struct kr_query *qry); int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache);
...@@ -1378,11 +1378,11 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t ...@@ -1378,11 +1378,11 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
return KR_STATE_FAIL; return KR_STATE_FAIL;
} }
const bool retry = (qry->flags & (QUERY_TCP|QUERY_STUB|QUERY_BADCOOKIE_AGAIN)); const bool retry = (qry->flags & (QUERY_TCP|QUERY_BADCOOKIE_AGAIN));
if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) { if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
kr_nsrep_elect_addr(qry, request->ctx); kr_nsrep_elect_addr(qry, request->ctx);
} else if (qry->flags & QUERY_FORWARD) { } else if (qry->flags & (QUERY_FORWARD|QUERY_STUB)) {
kr_nsrep_elect_min_rtt(qry); kr_nsrep_sort(&qry->ns, request->ctx->cache_rtt);
} else if (!qry->ns.name || !retry) { /* Keep NS when requerying/stub/badcookie. */ } else if (!qry->ns.name || !retry) { /* Keep NS when requerying/stub/badcookie. */
/* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */ /* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */
if (qry->sname[0] == '\0' && qry->stype == KNOT_RRTYPE_DNSKEY) { if (qry->sname[0] == '\0' && qry->stype == KNOT_RRTYPE_DNSKEY) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment