Commit b2127d21 authored by Marek Vavruša's avatar Marek Vavruša

lib/resolve: do not reelect NS after addr resolution

previously if NS had no A/AAAA records, they we’re looked up in subrequests, after that a new NS was reelected (possibly preferring another unknown), now it stays true to the NS of choice and changes only if it is unuseable
parent a03a263e
......@@ -68,7 +68,7 @@ static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsign
size_t len = pack_obj_len(it);
/* Get RTT for this address (if known) */
unsigned *cached = rttcache ? lru_get(rttcache, val, len) : NULL;
unsigned addr_score = (cached) ? *cached : KR_NS_UNKNOWN / 2;
unsigned addr_score = (cached) ? *cached : KR_NS_GLUED;
/* Give v6 a head start */
unsigned favour = (len == sizeof(struct in6_addr)) ? FAVOUR_IPV6 : 0;
if (addr_score < score + favour) {
......@@ -102,10 +102,14 @@ static int eval_nsrep(const char *k, void *v, void *baton)
pack_t *addr_set = (pack_t *)v;
if (addr_set->len == 0) {
score = KR_NS_UNKNOWN;
/* If the server is unknown but has rep record, treat it as timeouted */
if ((reputation & KR_NS_NOIP4) && (reputation & KR_NS_NOIP6)) {
score = KR_NS_TIMEOUT;
reputation = 0; /* Start with clean slate */
/* If the server doesn't have IPv6, give it disadvantage. */
if (reputation & KR_NS_NOIP6) {
score += FAVOUR_IPV6;
/* If the server is unknown but has rep record, treat it as timeouted */
if (reputation & KR_NS_NOIP4) {
score = KR_NS_TIMEOUT;
reputation = 0; /* Start with clean slate */
}
}
} else {
score = eval_addr_set(addr_set, ctx->cache_rtt, score, &addr);
......@@ -131,6 +135,13 @@ static int eval_nsrep(const char *k, void *v, void *baton)
return kr_ok();
}
#define ELECT_INIT(ns, ctx_) do { \
(ns)->ctx = (ctx_); \
(ns)->addr.ip.sa_family = AF_UNSPEC; \
(ns)->reputation = 0; \
(ns)->score = KR_NS_MAX_SCORE + 1; \
} while (0)
int kr_nsrep_elect(struct kr_query *qry, struct kr_context *ctx)
{
if (!qry || !ctx) {
......@@ -138,13 +149,32 @@ int kr_nsrep_elect(struct kr_query *qry, struct kr_context *ctx)
}
struct kr_nsrep *ns = &qry->ns;
ns->ctx = ctx;
ns->addr.ip.sa_family = AF_UNSPEC;
ns->reputation = 0;
ns->score = KR_NS_MAX_SCORE + 1;
ELECT_INIT(ns, ctx);
return map_walk(&qry->zone_cut.nsset, eval_nsrep, qry);
}
int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx)
{
if (!qry || !ctx) {
return kr_error(EINVAL);
}
/* Get address list for this NS */
struct kr_nsrep *ns = &qry->ns;
ELECT_INIT(ns, ctx);
pack_t *addr_set = map_get(&qry->zone_cut.nsset, (const char *)ns->name);
if (!addr_set) {
return kr_error(ENOENT);
}
/* Evaluate addr list */
uint8_t *addr = NULL;
unsigned score = eval_addr_set(addr_set, ctx->cache_rtt, ns->score, &addr);
update_nsrep(ns, ns->name, addr, score);
return kr_ok();
}
#undef ELECT_INIT
int kr_nsrep_update_rtt(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *cache)
{
if (!ns || !cache || ns->addr.ip.sa_family == AF_UNSPEC) {
......@@ -161,8 +191,8 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *cac
if (score > KR_NS_MAX_SCORE) {
score = KR_NS_MAX_SCORE;
}
if (score <= KR_NS_UNKNOWN) {
score = KR_NS_UNKNOWN + 1;
if (score <= KR_NS_GLUED) {
score = KR_NS_GLUED + 1;
}
/* Set initial value or smooth over last two measurements */
if (*cur != 0) {
......
......@@ -33,7 +33,8 @@ struct kr_query;
enum kr_ns_score {
KR_NS_MAX_SCORE = KR_CONN_RTT_MAX,
KR_NS_TIMEOUT = (95 * KR_NS_MAX_SCORE) / 100,
KR_NS_UNKNOWN = 10
KR_NS_UNKNOWN = KR_NS_TIMEOUT / 2,
KR_NS_GLUED = 10
};
/**
......@@ -83,6 +84,14 @@ struct kr_nsrep
*/
int kr_nsrep_elect(struct kr_query *qry, struct kr_context *ctx);
/**
* Elect best nameserver/address pair from the nsset.
* @param qry updated query
* @param ctx resolution context
* @return 0 or an error code
*/
int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx);
/**
* Update NS address RTT information.
*
......
......@@ -455,34 +455,34 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
}
ns_election:
/* Elect best nameserver candidate */
/* 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.
*/
assert(++ns_election_iter < KR_ITER_LIMIT);
kr_nsrep_elect(qry, request->ctx);
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);
return KNOT_STATE_PRODUCE;
if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
kr_nsrep_elect_addr(qry, request->ctx);
} else {
/* Update query flags based on the NS reputation */
if (qry->ns.reputation & KR_NS_NOIP6) {
qry->flags |= QUERY_AWAIT_IPV6;
}
if (qry->ns.reputation & KR_NS_NOIP4) {
qry->flags |= QUERY_AWAIT_IPV4;
}
/* Resolve address records */
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 */
}
kr_nsrep_elect(qry, request->ctx);
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);
return KNOT_STATE_PRODUCE;
} else {
/* Address resolved, clear the flag */
}
}
/* Resolve address records */
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);
return KNOT_STATE_PRODUCE;
} else {
/* Address resolved, clear the flag */
qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
}
#ifndef NDEBUG
......
......@@ -37,6 +37,27 @@ SECTION ADDITIONAL
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
a.gtld-servers.net. IN A
SECTION ANSWER
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
a.gtld-servers.net. IN AAAA
SECTION AUTHORITY
. SOA bla bla 1 2 3 4 5
ENTRY_END
RANGE_END
; a.gtld-servers.net.
......
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