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

lib/nsrep: persistent NS RTT/reputation tracking

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
parent f7517362
......@@ -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();
}
......@@ -18,18 +18,25 @@
#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);
......@@ -28,14 +28,26 @@
#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;
......
......@@ -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;
};
......
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