]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
itercache: caching layer implemented
authorMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 29 Dec 2014 01:25:26 +0000 (02:25 +0100)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 29 Dec 2014 01:25:26 +0000 (02:25 +0100)
lib/Makefile.am
lib/layer/itercache.c [new file with mode: 0644]
lib/layer/itercache.h [new file with mode: 0644]
lib/resolve.c

index 24bf4a1d3e03bc979a653b2a777bd2d2827b8074..476db0fbca7a01ef7650328f58039d8b3c76fc97 100644 (file)
@@ -5,6 +5,8 @@ lib_LTLIBRARIES = libknotresolve.la
 libknotresolve_la_SOURCES =            \
        layer/iterate.h \
        layer/iterate.c \
+       layer/itercache.h       \
+       layer/itercache.c       \
        layer/static.h  \
        layer/static.c  \
        layer/stats.h   \
diff --git a/lib/layer/itercache.c b/lib/layer/itercache.c
new file mode 100644 (file)
index 0000000..a461a7b
--- /dev/null
@@ -0,0 +1,272 @@
+/* Copyright 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <libknot/descriptor.h>
+#include <libknot/errcode.h>
+#include <libknot/rrset.h>
+#include <libknot/internal/mempool.h>
+#include <libknot/rrtype/rdname.h>
+
+#include "lib/layer/static.h"
+#include "lib/utils.h"
+
+#define DEBUG_MSG(fmt, ...) fprintf(stderr, "[cache] " fmt, ## __VA_ARGS__)
+
+static int begin(knot_layer_t *ctx, void *module_param)
+{
+       ctx->data = module_param;
+       return ctx->state;
+}
+
+static int query_cache_append(knot_pkt_t *answer, namedb_txn_t *txn, knot_rrset_t *cache_rr, uint32_t timestamp)
+{
+       unsigned drift = timestamp;
+
+       /* Query cache and keep drift between RRSet origin and now. */
+       if (kr_cache_query(txn, cache_rr, &drift) != KNOT_EOK) {
+               return KNOT_ENOENT;
+       }
+
+       /* Make RRSet copy. */
+       knot_rrset_t rr_copy;
+       knot_rrset_init(&rr_copy, knot_dname_copy(cache_rr->owner, &answer->mm), cache_rr->type, cache_rr->rclass);
+       int ret = knot_rdataset_copy(&rr_copy.rrs, &cache_rr->rrs, &answer->mm);
+       if (ret != KNOT_EOK) {
+               knot_rrset_clear(&rr_copy, &answer->mm);
+               return ret;
+       }
+
+       /* Adjust the TTL of the records. */
+       for (unsigned i = 0; i < rr_copy.rrs.rr_count; ++i) {
+               knot_rdata_t *rd = knot_rdataset_at(&rr_copy.rrs, i);
+               knot_rdata_set_ttl(rd, knot_rdata_ttl(rd) - drift);
+       }
+
+       /* Write copied RR to the result packet. */
+       ret = knot_pkt_put(answer, KNOT_COMPR_HINT_NONE, &rr_copy, KNOT_PF_FREE);
+       if (ret != KNOT_EOK) {
+               knot_rrset_clear(&rr_copy, &answer->mm);
+               knot_wire_set_tc(answer->wire);
+       }
+
+       return KNOT_EOK;
+}
+
+static int query_cache_zonecut(struct kr_zonecut *cut, namedb_txn_t *txn, knot_rrset_t *cache_rr, uint32_t timestamp)
+{
+       /* Query cache for requested record */
+       if (kr_cache_query(txn, cache_rr, &timestamp) != KNOT_EOK) {
+               return KNOT_ENOENT;
+       }
+
+       switch(cache_rr->type) {
+       case KNOT_RRTYPE_NS:
+               return kr_set_zone_cut(cut, cache_rr->owner, knot_ns_name(&cache_rr->rrs, 0));
+       case KNOT_RRTYPE_A:
+       case KNOT_RRTYPE_AAAA:
+               return kr_rrset_to_addr(&cut->addr, cache_rr);
+       default:
+               return KNOT_ENOENT;
+       }
+}
+
+static int query_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+       assert(pkt && ctx);
+       struct kr_layer_param *param = ctx->data;
+       struct kr_query *cur = kr_rplan_current(param->rplan);
+       if (cur == NULL) {
+               return ctx->state;
+       }
+
+       int ret = KNOT_EOK;
+       knot_rrset_t cache_rr;
+       knot_rrset_init(&cache_rr, cur->sname, cur->stype, cur->sclass);
+       namedb_txn_t *txn = kr_rplan_txn_acquire(param->rplan, NAMEDB_RDONLY);
+       uint32_t timestamp = cur->timestamp.tv_sec;
+
+       /* Check if updating current zone cut. */
+       if (cur != kr_rplan_last(param->rplan)) {
+               ret = query_cache_zonecut(&param->rplan->zone_cut, txn, &cache_rr, timestamp);
+               if (ret == KNOT_EOK) {
+                       kr_rplan_pop(param->rplan, cur);
+                       return KNOT_NS_PROC_DONE;
+               }
+
+               return ctx->state;
+       }
+
+       /* Try to find a CNAME/DNAME chain first. */
+       cache_rr.type = KNOT_RRTYPE_CNAME;
+       ret = query_cache_append(param->answer, txn, &cache_rr, timestamp);
+       if (ret == KNOT_EOK) {
+               /* Terminate if the STYPE was CNAME as well, otherwise follow. */
+               kr_rplan_pop(param->rplan, cur);
+               if (cur->stype != KNOT_RRTYPE_CNAME) {
+                       const knot_dname_t *cname = knot_cname_name(&cache_rr.rrs);
+                       if (kr_rplan_push(param->rplan, cname, cur->sclass, cur->stype) == NULL) {
+                               return KNOT_NS_PROC_FAIL;
+                       }
+               }
+               return KNOT_NS_PROC_DONE;
+       }
+
+       /* Try to find expected record then. */
+       cache_rr.type = cur->stype;
+       ret = query_cache_append(param->answer, txn, &cache_rr, timestamp);
+       if (ret == KNOT_EOK) {
+               kr_rplan_pop(param->rplan, cur);
+               return KNOT_NS_PROC_DONE;
+       }
+
+       /* Not resolved. */
+       return KNOT_NS_PROC_MORE;
+}
+
+/*! \brief Merge-in record if same type and owner. */
+static int merge_cache_rr(knot_rrset_t *cache_rr, const knot_rrset_t *rr, mm_ctx_t *pool)
+{
+       if (rr->type != cache_rr->type || !knot_dname_is_equal(rr->owner, cache_rr->owner)) {
+               return KNOT_EOK; /* Ignore */
+       }
+
+       return knot_rdataset_merge(&cache_rr->rrs, &rr->rrs, pool);
+}
+
+/*! \brief Merge-in records from the same section. */
+static int merge_in_section(knot_rrset_t *cache_rr, const knot_pktsection_t *section, unsigned from, mm_ctx_t *pool)
+{
+       int ret = KNOT_EOK;
+
+       for (unsigned i = from; i < section->count; ++i) {
+               ret = merge_cache_rr(cache_rr, knot_pkt_rr(section, i), pool);
+               if (ret != KNOT_EOK) {
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+/*! \brief Cache direct answer. */
+static int update_cache_answer(knot_pkt_t *pkt, namedb_txn_t *txn, mm_ctx_t *pool, uint32_t timestamp)
+{
+       /* Cache only positive answers. */
+       if (knot_wire_get_rcode(pkt->wire) != KNOT_RCODE_NOERROR) {
+               return KNOT_EOK;
+       }
+
+       /* Cache only direct answer. */
+       knot_rrset_t cache_rr;
+       knot_rrset_init(&cache_rr, (knot_dname_t *)knot_pkt_qname(pkt), knot_pkt_qtype(pkt), knot_pkt_qclass(pkt));
+
+       int ret = merge_in_section(&cache_rr, knot_pkt_section(pkt, KNOT_ANSWER), 0, pool);
+       if (ret != KNOT_EOK) {
+               return ret;
+       }
+
+       /* Cache the merged RRSet (may fail) */
+       (void) kr_cache_insert(txn, &cache_rr, timestamp);
+
+       return ret;
+}
+
+/*! \brief Cache stub nameservers. */
+static int update_cache_authority(knot_pkt_t *pkt, namedb_txn_t *txn, mm_ctx_t *pool, uint32_t timestamp)
+{
+       bool found_ns_rr = false;
+       knot_rrset_t cache_rr;
+
+       /* Take first NS and merge with rest. */
+       const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
+       for (unsigned i = 0; i < ns->count; ++i) {
+               const knot_rrset_t *rr = knot_pkt_rr(ns, i);
+               if (rr->type == KNOT_RRTYPE_NS) {
+                       knot_rrset_init(&cache_rr, (knot_dname_t *)rr->owner, rr->type, rr->rclass);
+                       found_ns_rr = true;
+                       break;
+               }
+       }
+
+       /* Not found any viable NS */
+       if (!found_ns_rr) {
+               return KNOT_EOK;
+       }
+
+       int ret = merge_in_section(&cache_rr, ns, 0, pool);
+       if (ret != KNOT_EOK) {
+               return ret;
+       }
+
+       /* Cache the merged RRSet (may fail) */
+       (void) kr_cache_insert(txn, &cache_rr, timestamp);
+
+       return ret;
+}
+
+static void update_cache_pkt(knot_pkt_t *pkt, namedb_txn_t *txn, mm_ctx_t *pool, uint32_t timestamp)
+{
+       /* If authoritative, cache answer for current query. */
+       if (knot_wire_get_aa(pkt->wire)) {
+               update_cache_answer(pkt, txn, pool, timestamp);
+       } else {
+               /* Cache authority records, but not glue. */
+               update_cache_authority(pkt, txn, pool, timestamp);
+       }
+}
+
+static int update_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+       struct kr_layer_param *param = ctx->data;
+       struct kr_query *last_query = kr_rplan_last(param->rplan);
+
+       /* Don't cache anything if failed / no query. */
+       if (ctx->state == KNOT_NS_PROC_FAIL || last_query == NULL) {
+               return ctx->state;
+       }
+
+       /* Open write transaction */
+       uint32_t timestamp = last_query->timestamp.tv_sec;
+       namedb_txn_t *txn = kr_rplan_txn_acquire(param->rplan, 0);
+       if (txn == NULL) {
+               return ctx->state; /* Couldn't acquire cache, ignore. */
+       }
+
+       /* Create memory pool for merging RRSets. */
+       mm_ctx_t pool;
+       mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
+
+       /* Selectively cache records from the packet. */
+       update_cache_pkt(pkt, txn, &pool, timestamp);
+
+       /* Cleanup. */
+       mp_delete(pool.ctx);
+       return ctx->state;
+}
+
+/*! \brief Module implementation. */
+static const knot_layer_api_t LAYER_ITERCACHE_MODULE = {
+        &begin,
+        NULL,
+        NULL,
+        &update_cache,
+        &query_cache,
+        NULL
+};
+
+const knot_layer_api_t *layer_itercache_module(void)
+{
+       return &LAYER_ITERCACHE_MODULE;
+}
diff --git a/lib/layer/itercache.h b/lib/layer/itercache.h
new file mode 100644 (file)
index 0000000..ec9fb01
--- /dev/null
@@ -0,0 +1,22 @@
+/* Copyright 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+#include "lib/layer.h"
+
+/* Processing module implementation. */
+const knot_layer_api_t *layer_itercache_module(void);
+#define LAYER_ITERCACHE layer_itercache_module()
index 3cdb067b49b6ba241e93956e88de9fda33cb6841..9d2ec4e9e792eb8ce0196c01cf272a84ab66d76e 100644 (file)
@@ -8,6 +8,7 @@
 #include "lib/resolve.h"
 #include "lib/defines.h"
 #include "lib/utils.h"
+#include "lib/layer/itercache.h"
 #include "lib/layer/iterate.h"
 #include "lib/layer/static.h"
 #include "lib/layer/stats.h"
@@ -138,6 +139,7 @@ int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer,
        struct knot_requestor requestor;
        knot_requestor_init(&requestor, ctx->pool);
        knot_requestor_overlay(&requestor, LAYER_STATIC, &param);
+       knot_requestor_overlay(&requestor, LAYER_ITERCACHE, &param);
        knot_requestor_overlay(&requestor, LAYER_ITERATE, &param);
        knot_requestor_overlay(&requestor, LAYER_STATS, &param);