Commit 4a713957 authored by Marek Vavruša's avatar Marek Vavruša

lib/nsrep: library supports multiple query targets

the selected address is now an array with selection, caller can then send the same query to multiple offered targets

refs #35
parent ccac4151
......@@ -333,7 +333,7 @@ static int qr_task_step(struct qr_task *task, knot_pkt_t *packet)
int sock_type = -1;
struct sockaddr *addr = NULL;
knot_pkt_t *pktbuf = task->pktbuf;
int state = kr_resolve_consume(&task->req, packet);
int state = kr_resolve_consume(&task->req, NULL, packet);
while (state == KNOT_STATE_PRODUCE) {
state = kr_resolve_produce(&task->req, &addr, &sock_type, pktbuf);
if (unlikely(++task->iter_count > KR_ITER_LIMIT)) {
......
......@@ -100,7 +100,7 @@ static int peek(knot_layer_t *ctx, knot_pkt_t *pkt)
if (ctx->state & (KNOT_STATE_FAIL|KNOT_STATE_DONE) || (qry->flags & QUERY_NO_CACHE)) {
return ctx->state; /* Already resolved/failed */
}
if (qry->ns.addr.ip.sa_family != AF_UNSPEC) {
if (qry->ns.addr[0].ip.sa_family != AF_UNSPEC) {
return ctx->state; /* Only lookup before asking a query */
}
if (knot_pkt_qclass(pkt) != KNOT_CLASS_IN) {
......
......@@ -107,7 +107,7 @@ static int peek(knot_layer_t *ctx, knot_pkt_t *pkt)
if (ctx->state & (KNOT_STATE_FAIL|KNOT_STATE_DONE) || (qry->flags & QUERY_NO_CACHE)) {
return ctx->state; /* Already resolved/failed */
}
if (qry->ns.addr.ip.sa_family != AF_UNSPEC) {
if (qry->ns.addr[0].ip.sa_family != AF_UNSPEC) {
return ctx->state; /* Only lookup before asking a query */
}
......
......@@ -37,12 +37,10 @@
} while (0)
/** Update nameserver representation with current name/address pair. */
static void update_nsrep(struct kr_nsrep *ns, const knot_dname_t *name, uint8_t *addr, unsigned score)
static void update_nsrep(struct kr_nsrep *ns, uint8_t *addr, size_t pos)
{
ns->name = name;
ns->score = score;
if (addr == NULL) {
ns->addr.ip.sa_family = AF_UNSPEC;
ns->addr[pos].ip.sa_family = AF_UNSPEC;
return;
}
......@@ -50,16 +48,25 @@ static void update_nsrep(struct kr_nsrep *ns, const knot_dname_t *name, uint8_t
void *addr_val = pack_obj_val(addr);
switch(len) {
case sizeof(struct in_addr):
ADDR_SET(ns->addr.ip4.sin, AF_INET, addr_val, len); break;
ADDR_SET(ns->addr[pos].ip4.sin, AF_INET, addr_val, len); break;
case sizeof(struct in6_addr):
ADDR_SET(ns->addr.ip6.sin6, AF_INET6, addr_val, len); break;
ADDR_SET(ns->addr[pos].ip6.sin6, AF_INET6, addr_val, len); break;
default: assert(0); break;
}
}
static void update_nsrep_set(struct kr_nsrep *ns, const knot_dname_t *name, uint8_t *addr[], unsigned score)
{
ns->name = name;
ns->score = score;
for (size_t i = 0; i < KR_NSREP_MAXADDR; ++i) {
update_nsrep(ns, addr[i], i);
}
}
#undef ADDR_SET
static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsigned score, uint8_t **addr)
static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsigned score, uint8_t *addr[])
{
/* Name server is better candidate if it has address record. */
uint8_t *it = pack_head(*addr_set);
......@@ -72,7 +79,10 @@ static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsign
/* Give v6 a head start */
unsigned favour = (len == sizeof(struct in6_addr)) ? FAVOUR_IPV6 : 0;
if (addr_score < score + favour) {
*addr = it;
/* Shake down previous contenders, last one is always unused */
for (size_t i = KR_NSREP_MAXADDR - 2; i > 0; --i)
addr[i] = addr[i - 1];
addr[0] = it;
score = addr_score;
}
it = pack_obj_next(it);
......@@ -87,7 +97,7 @@ static int eval_nsrep(const char *k, void *v, void *baton)
struct kr_context *ctx = ns->ctx;
unsigned score = KR_NS_MAX_SCORE;
unsigned reputation = 0;
uint8_t *addr = NULL;
uint8_t *addr_choice[KR_NSREP_MAXADDR] = { NULL, };
/* Fetch NS reputation */
if (ctx->cache_rep) {
......@@ -112,7 +122,7 @@ static int eval_nsrep(const char *k, void *v, void *baton)
}
}
} else {
score = eval_addr_set(addr_set, ctx->cache_rtt, score, &addr);
score = eval_addr_set(addr_set, ctx->cache_rtt, score, addr_choice);
}
/* Probabilistic bee foraging strategy (naive).
......@@ -120,7 +130,7 @@ static int eval_nsrep(const char *k, void *v, void *baton)
* at the same time long distance scouts probe other sources (low probability).
* Servers on TIMEOUT (depleted) can be probed by the dice roll only */
if (score < ns->score && (qry->flags & QUERY_NO_THROTTLE || score < KR_NS_TIMEOUT)) {
update_nsrep(ns, (const knot_dname_t *)k, addr, score);
update_nsrep_set(ns, (const knot_dname_t *)k, addr_choice, score);
ns->reputation = reputation;
} else {
/* With 5% chance, probe server with a probability given by its RTT / MAX_RTT */
......@@ -129,7 +139,7 @@ static int eval_nsrep(const char *k, void *v, void *baton)
if (score >= KR_NS_LONG) {
qry->flags |= QUERY_TCP;
}
update_nsrep(ns, (const knot_dname_t *)k, addr, score);
update_nsrep_set(ns, (const knot_dname_t *)k, addr_choice, score);
ns->reputation = reputation;
return 1; /* Stop evaluation */
}
......@@ -140,7 +150,7 @@ static int eval_nsrep(const char *k, void *v, void *baton)
#define ELECT_INIT(ns, ctx_) do { \
(ns)->ctx = (ctx_); \
(ns)->addr.ip.sa_family = AF_UNSPEC; \
(ns)->addr[0].ip.sa_family = AF_UNSPEC; \
(ns)->reputation = 0; \
(ns)->score = KR_NS_MAX_SCORE + 1; \
} while (0)
......@@ -170,23 +180,32 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx)
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);
uint8_t *addr_choice[KR_NSREP_MAXADDR] = { NULL, };
unsigned score = eval_addr_set(addr_set, ctx->cache_rtt, ns->score, addr_choice);
update_nsrep_set(ns, ns->name, addr_choice, score);
return kr_ok();
}
#undef ELECT_INIT
int kr_nsrep_update_rtt(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *cache)
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache)
{
if (!ns || !cache || ns->addr.ip.sa_family == AF_UNSPEC) {
if (!ns || !cache || ns->addr[0].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(cache, addr, addr_len);
const char *addr_in = kr_nsrep_inaddr(ns->addr[0]);
size_t addr_len = kr_nsrep_inaddr_len(ns->addr[0]);
if (addr) { /* Caller provided specific address */
if (addr->sa_family == AF_INET) {
addr_in = (const char *)&((struct sockaddr_in *)addr)->sin_addr;
addr_len = sizeof(struct in_addr);
} else if (addr->sa_family == AF_INET6) {
addr_in = (const char *)&((struct sockaddr_in6 *)addr)->sin6_addr;
addr_len = sizeof(struct in6_addr);
}
}
unsigned *cur = lru_set(cache, addr_in, addr_len);
if (!cur) {
return kr_error(ENOMEM);
}
......
......@@ -52,6 +52,9 @@ enum kr_ns_rep {
*/
typedef lru_hash(unsigned) kr_nsrep_lru_t;
/* Maximum count of addresses probed in one go (last is left empty) */
#define KR_NSREP_MAXADDR 3
/**
* Name server representation.
* Contains extra information about the name server, e.g. score
......@@ -67,7 +70,7 @@ struct kr_nsrep
struct sockaddr ip;
struct sockaddr_in ip4;
struct sockaddr_in6 ip6;
} addr; /**< NS address */
} addr[KR_NSREP_MAXADDR]; /**< NS address(es) */
};
/** @internal Address bytes for given family. */
......@@ -99,16 +102,15 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx);
* @brief Reputation is smoothed over last N measurements.
*
* @param ns updated NS representation
* @param addr chosen address (NULL for first)
* @param score new score (i.e. RTT), see enum kr_ns_score
* @param cache LRU cache
* @return 0 on success, error code on failure
*/
int kr_nsrep_update_rtt(struct kr_nsrep *ns, unsigned score, kr_nsrep_lru_t *cache);
int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, unsigned score, kr_nsrep_lru_t *cache);
/**
* Update NS name quality information.
*
* @brief Reputation is smoothed over last N measurements.
* Update NSSET reputation information.
*
* @param ns updated NS representation
* @param reputation combined reputation flags, see enum kr_ns_rep
......
......@@ -64,9 +64,9 @@ static void randomized_qname_case(knot_dname_t *qname, unsigned secret)
/** Invalidate current NS/addr pair. */
static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
{
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);
if (qry->ns.addr[0].ip.sa_family != AF_UNSPEC) {
uint8_t *addr = kr_nsrep_inaddr(qry->ns.addr[0]);
size_t addr_len = kr_nsrep_inaddr_len(qry->ns.addr[0]);
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);
......@@ -275,7 +275,7 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
return request->state;
}
int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
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;
......@@ -310,14 +310,14 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
/* Resolution failed, invalidate current NS. */
if (request->state == KNOT_STATE_FAIL) {
kr_nsrep_update_rtt(&qry->ns, KR_NS_TIMEOUT, ctx->cache_rtt);
kr_nsrep_update_rtt(&qry->ns, src, KR_NS_TIMEOUT, ctx->cache_rtt);
invalidate_ns(rplan, qry);
qry->flags &= ~QUERY_RESOLVED;
/* Track RTT for iterative answers */
} else if (!(qry->flags & QUERY_CACHED)) {
struct timeval now;
gettimeofday(&now, NULL);
kr_nsrep_update_rtt(&qry->ns, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
kr_nsrep_update_rtt(&qry->ns, src, time_diff(&qry->timestamp, &now), ctx->cache_rtt);
/* Sucessful answer, lift any address resolution requests. */
qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4);
}
......@@ -493,7 +493,7 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
}
/* Resolve address records */
if (qry->ns.addr.ip.sa_family == AF_UNSPEC) {
if (qry->ns.addr[0].ip.sa_family == AF_UNSPEC) {
if (ns_resolve_addr(qry, request) != 0) {
qry->flags &= ~(QUERY_AWAIT_IPV6|QUERY_AWAIT_IPV4|QUERY_TCP);
goto ns_election; /* Must try different NS */
......@@ -511,16 +511,21 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
WITH_DEBUG {
char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN], type_str[16];
knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str));
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));
knot_rrtype_to_string(knot_pkt_qtype(packet), type_str, sizeof(type_str));
DEBUG_MSG("=> querying: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s'\n",
ns_str, qry->ns.score, zonecut_str, qname_str, type_str);
for (size_t i = 0; i < KR_NSREP_MAXADDR; ++i) {
struct sockaddr *addr = &qry->ns.addr[i].ip;
if (addr->sa_family == AF_UNSPEC) {
break;
}
inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr[i]), ns_str, sizeof(ns_str));
DEBUG_MSG("%c> querying: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s'\n",
i == 0 ? '=' : '+', ns_str, qry->ns.score, zonecut_str, qname_str, type_str);
}
}
gettimeofday(&qry->timestamp, NULL);
*dst = &qry->ns.addr.ip;
*dst = &qry->ns.addr[0].ip;
*type = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM;
return request->state;
}
......@@ -543,7 +548,8 @@ int kr_resolve_finish(struct kr_request *request, int state)
}
ITERATE_LAYERS(request, finish);
DEBUG_MSG("finished: %d, mempool: %zu B\n", state, (size_t) mp_total_size(request->pool.ctx));
DEBUG_MSG("finished: %d, queries: %zu, mempool: %zu B\n",
state, list_size(&rplan->resolved), (size_t) mp_total_size(request->pool.ctx));
return KNOT_STATE_DONE;
}
......
......@@ -57,7 +57,7 @@
* int ret = sendrecv(addr, proto, query, resp);
*
* // If I/O fails, make "resp" empty
* state = kr_resolve_consume(&request, resp);
* state = kr_resolve_consume(&request, addr, resp);
* knot_pkt_clear(resp);
* }
* knot_pkt_clear(query);
......@@ -137,10 +137,11 @@ int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pk
* @note If the I/O fails, provide an empty or NULL packet, this will make iterator recognize nameserver failure.
*
* @param request request state (awaiting input)
* @param src [in] packet source address
* @param packet [in] input packet
* @return any state
*/
int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet);
int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, knot_pkt_t *packet);
/**
* Produce either next additional query or finish.
......
......@@ -119,7 +119,7 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
qry->stype = type;
qry->flags = rplan->request->options;
qry->parent = parent;
qry->ns.addr.ip.sa_family = AF_UNSPEC;
qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
gettimeofday(&qry->timestamp, NULL);
add_tail(&rplan->pending, &qry->node);
kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
......
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