From: Vladimír Čunát Date: Mon, 15 Jan 2018 16:22:01 +0000 (+0100) Subject: new serve_stale module X-Git-Tag: v2.0.0~3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c766411a8868b57cb8a9f3c1536b32b5204ffcd3;p=thirdparty%2Fknot-resolver.git new serve_stale module Decision function is separated out. --- diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 68d0dd268..8fdd010ac 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -97,6 +97,7 @@ struct kr_qflags { _Bool NONAUTH : 1; _Bool FORWARD : 1; _Bool DNS64_MARK : 1; + _Bool CACHE_TRIED : 1; }; typedef struct { knot_rrset_t **at; @@ -185,6 +186,9 @@ struct kr_cache { struct timeval last_clear_walltime; uint64_t last_clear_monotime; }; + +typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type, + const struct kr_query *qry); struct knot_rrset { knot_dname_t *_owner; uint16_t type; @@ -219,6 +223,7 @@ struct kr_query { uint32_t uid; struct kr_query *cname_parent; struct kr_request *request; + kr_stale_cb stale_cb; }; struct kr_context { struct kr_qflags options; @@ -280,6 +285,7 @@ struct sockaddr *kr_straddr_socket(const char *, int); int kr_ranked_rrarray_add(ranked_rr_array_t *, const knot_rrset_t *, uint8_t, _Bool, uint32_t, knot_mm_t *); void kr_qflags_set(struct kr_qflags *, struct kr_qflags); void kr_qflags_clear(struct kr_qflags *, struct kr_qflags); +void kr_query_set_stale_cb(struct kr_query *, kr_stale_cb); int kr_zonecut_add(struct kr_zonecut *, const knot_dname_t *, const knot_rdata_t *); void kr_zonecut_set(struct kr_zonecut *, const knot_dname_t *); uint64_t kr_now(); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index ff673a517..59fafce76 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -65,6 +65,11 @@ typedef void (*trace_callback_f)(struct kr_request *); struct kr_cache EOF +printf " +typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type, + const struct kr_query *qry); +" + genResType() { echo "$1" | ./scripts/gen-cdefs.sh libkres types } @@ -147,6 +152,7 @@ EOF kr_ranked_rrarray_add kr_qflags_set kr_qflags_clear + kr_query_set_stale_cb kr_zonecut_add kr_zonecut_set kr_now diff --git a/doc/modules.rst b/doc/modules.rst index 5e01e981b..f109b3c88 100644 --- a/doc/modules.rst +++ b/doc/modules.rst @@ -28,5 +28,6 @@ Knot DNS Resolver modules .. include:: ../modules/ta_signal_query/README.rst .. include:: ../modules/ta_sentinel/README.rst .. include:: ../modules/priming/README.rst +.. include:: ../modules/serve_stale/README.rst .. include:: ../modules/detect_time_skew/README.rst .. include:: ../modules/detect_time_jump/README.rst diff --git a/lib/cache/api.c b/lib/cache/api.c index b2937bc33..6860dd52e 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -224,12 +224,11 @@ int32_t get_new_ttl(const struct entry_h *entry, const struct kr_query *qry, diff = 0; } int32_t res = entry->ttl - diff; - if (res < 0 && owner && false/*qry->flags.SERVE_STALE*/) { + if (res < 0 && owner && qry->stale_cb) { /* Stale-serving decision. FIXME: modularize or make configurable, etc. */ - if (res + 3600 * 24 > 0) { - VERBOSE_MSG(qry, "stale TTL accepted: %d -> 1\n", (int)res); - return 1; - } + int res_stale = qry->stale_cb(res, owner, type, qry); + if (res_stale >= 0) + return res_stale; } return res; } @@ -315,6 +314,7 @@ int cache_peek(kr_layer_t *ctx, knot_pkt_t *pkt) struct kr_query *qry = req->current_query; if (ctx->state & (KR_STATE_FAIL|KR_STATE_DONE) || qry->flags.NO_CACHE + || (qry->flags.CACHE_TRIED && !qry->stale_cb) || qry->stype == KNOT_RRTYPE_RRSIG /* LATER: some other behavior for this STYPE? */ || qry->sclass != KNOT_CLASS_IN) { return ctx->state; /* Already resolved/failed or already tried, etc. */ @@ -337,7 +337,7 @@ static int cache_peek_real(kr_layer_t *ctx, knot_pkt_t *pkt) /* ATM cache only peeks for qry->sname and that would be useless * to repeat on every iteration, so disable it from now on. * LATER(optim.): assist with more precise QNAME minimization. */ - qry->flags.NO_CACHE = true; + qry->flags.CACHE_TRIED = true; struct key k_storage, *k = &k_storage; if (qry->stype == KNOT_RRTYPE_NSEC) { diff --git a/lib/rplan.c b/lib/rplan.c index e18d96a13..a21e05a76 100644 --- a/lib/rplan.c +++ b/lib/rplan.c @@ -27,6 +27,11 @@ #define QUERY_PROVIDES(q, name, cls, type) \ ((q)->sclass == (cls) && (q)->stype == type && knot_dname_is_equal((q)->sname, name)) +void kr_query_set_stale_cb(struct kr_query *qry, kr_stale_cb cb) +{ + qry->stale_cb = cb; +} + inline static unsigned char chars_or(const unsigned char a, const unsigned char b) { return a | b; diff --git a/lib/rplan.h b/lib/rplan.h index 52b3e242c..2d3810c60 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -61,6 +61,7 @@ struct kr_qflags { * TODO: utilize this also outside cache. */ bool FORWARD : 1; /**< Forward all queries to upstream; validate answers. */ bool DNS64_MARK : 1; /**< Internal mark for dns64 module. */ + bool CACHE_TRIED : 1; /**< Internal to cache module. */ }; /** Combine flags together. This means set union for simple flags. */ @@ -71,6 +72,17 @@ void kr_qflags_set(struct kr_qflags *fl1, struct kr_qflags fl2); KR_EXPORT void kr_qflags_clear(struct kr_qflags *fl1, struct kr_qflags fl2); +/** Callback for serve-stale decisions. + * @param ttl the expired TTL (i.e. it's < 0) + * @return the adjusted TTL (typically 1) or < 0. + */ +typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type, + const struct kr_query *qry); +/** Trivial wrapper to set kr_query::stale_cb. + * For some unknown reason, direct setting via lua doesn't work. */ +KR_EXPORT +void kr_query_set_stale_cb(struct kr_query *qry, kr_stale_cb cb); + /** * Single query representation. */ @@ -97,6 +109,7 @@ struct kr_query { /** Pointer to the query that originated this one because of following a CNAME (or NULL). */ struct kr_query *cname_parent; struct kr_request *request; /**< Parent resolution request. */ + kr_stale_cb stale_cb; /**< See the type */ }; /** @cond internal Array of queries. */ diff --git a/modules/modules.mk b/modules/modules.mk index ea63491c6..864058f67 100644 --- a/modules/modules.mk +++ b/modules/modules.mk @@ -36,6 +36,7 @@ modules_TARGETS += etcd \ version \ ta_signal_query \ priming \ + serve_stale \ detect_time_skew \ detect_time_jump endif diff --git a/modules/serve_stale/README.rst b/modules/serve_stale/README.rst new file mode 100644 index 000000000..74ee09be5 --- /dev/null +++ b/modules/serve_stale/README.rst @@ -0,0 +1,21 @@ +.. _mod-serve_stale: + +Serve stale +----------- + +Demo module that allows using timed-out records in case kresd is +unable to contact upstream servers. + +By default it allows stale-ness by up to one day, +after roughly four seconds trying to contact the servers. +It's quite configurable/flexible; see the beginning of the module source for details. +See also the RFC draft_ (not fully followed). + +Running +^^^^^^^ +.. code-block:: lua + + modules = { 'serve_stale < cache' } + +.. _draft: https://tools.ietf.org/html/draft-ietf-dnsop-serve-stale-00 + diff --git a/modules/serve_stale/serve_stale.lua b/modules/serve_stale/serve_stale.lua new file mode 100644 index 000000000..c0527666e --- /dev/null +++ b/modules/serve_stale/serve_stale.lua @@ -0,0 +1,42 @@ +local M = {} -- the module + +local ffi = require('ffi') + +-- Beware that the timeout is only considered at certain points in time; +-- approximately at multiples of KR_CONN_RTT_MAX. +M.timeout = 3*sec + +M.callback = ffi.cast("kr_stale_cb", + function (ttl) --, name, type, qry) + --log('[ ][stal] => called back with TTL: ' .. tostring(ttl)) + if ttl + 3600 * 24 > 0 then -- at most one day stale + return 1 + else + return -1 + end + end) + +M.layer = { + produce = function (state, req) + req = kres.request_t(req) + local qry = req:current() + -- Don't do anything for priming, prefetching, etc. + -- TODO: not all cases detected ATM. + if qry.flags.NO_CACHE then return state end + + local now = ffi.C.kr_now() + local deadline = qry.creation_time_mono + M.timeout + if now > deadline then + --log('[ ][stal] => deadline has passed') + ffi.C.kr_query_set_stale_cb(qry, M.callback) + -- TODO: probably start the same request that doesn't stale-serve, + -- but first we need some detection of non-interactive / internal requests. + -- resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass) + end + + return state + end, +} + +return M + diff --git a/modules/serve_stale/serve_stale.mk b/modules/serve_stale/serve_stale.mk new file mode 100644 index 000000000..46c6f6402 --- /dev/null +++ b/modules/serve_stale/serve_stale.mk @@ -0,0 +1,2 @@ +serve_stale_SOURCES := serve_stale.lua +$(call make_lua_module,serve_stale)