#include <netinet/in.h>
#include <netdb.h>
+#include <arpa/inet.h>
+
#include "lib/nsrep.h"
#include "lib/rplan.h"
#include "lib/resolve.h"
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? */
- struct kr_nsrep *ns = &qry->ns;
+ if (!ns || !cache) {
+ assert(false);
+ return kr_error(EINVAL);
+ }
if (ns->addr[0].ip.sa_family == AF_UNSPEC) {
return kr_error(EINVAL);
return kr_ok();
}
- const struct kr_context *ctx = ns->ctx;
- if (!ctx) {
- return kr_ok();
+ /* Compute the scores. Unfortunately there's no space for scores
+ * along the addresses. */
+ 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]);
- unsigned *score = lru_get_try(ctx->cache_rtt, kr_inaddr(sock),
- kr_family_len(sock->sa_family));
- unsigned minimal_score = (score) ? *score : KR_NS_MAX_SCORE + 1;
- int i = 1;
- sock = (const struct sockaddr *)(&ns->addr[i]);
- do {
- score = lru_get_try(ctx->cache_rtt, kr_inaddr(sock),
- 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;
+ /* Select-sort the addresses. */
+ const int count = i;
+ for (i = 0; i < count - 1; ++i) {
+ /* find min from i onwards */
+ int min_i = i;
+ for (int j = i + 1; j < count; ++j) {
+ if (scores[j] < scores[min_i]) {
+ min_i = j;
}
}
- sock = (const struct sockaddr *)(&ns->addr[++i]);
- } while ((i < KR_NSREP_MAXADDR) && (sock->sa_family != AF_UNSPEC));
+ /* swap the indices */
+ 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();
}
int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src);
/**
- * Elect address with minimal rtt within current query nsrep list
- * @note ns reputation is zeroed
- * @param qry updated query
+ * Sort addresses in the query nsrep list
+ * @param ns updated kr_nsrep
+ * @param cache RTT cache
* @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
-int kr_nsrep_elect_min_rtt(struct kr_query *qry);
+int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache);
+
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)) {
kr_nsrep_elect_addr(qry, request->ctx);
- } else if (qry->flags & QUERY_FORWARD) {
- kr_nsrep_elect_min_rtt(qry);
+ } else if (qry->flags & (QUERY_FORWARD|QUERY_STUB)) {
+ kr_nsrep_sort(&qry->ns, request->ctx->cache_rtt);
} 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. */
if (qry->sname[0] == '\0' && qry->stype == KNOT_RRTYPE_DNSKEY) {