]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
use LRU for packet cache instead of persistent cache marek/lru-cache-packets
authorMarek Vavruša <mvavrusa@cloudflare.com>
Sat, 12 May 2018 04:06:21 +0000 (21:06 -0700)
committerMarek Vavruša <mvavrusa@cloudflare.com>
Wed, 16 May 2018 04:41:49 +0000 (21:41 -0700)
The primary motivation is that packet cache is shared with records,
but it isn't useful for improving performance of popular domain lookups.

Negative answers (with DNSSEC) are typically around 1kB, so it doesn't
take many queries to force a full cache flush, including useful record data.

This changes the packet cache to use a fixed-size LRU, which is more resilient
to random prefix attacks, but it isn't persistent or shared between processes.

lib/cache/api.c
lib/cache/api.h
lib/cache/entry_pkt.c
lib/cache/impl.h
lib/defines.h

index 63e205740b1ca4614d2828a79de8e2613cf7083b..c9645379ef1a9225dd3d68c5ee8d1ef76c46d4c3 100644 (file)
@@ -129,6 +129,11 @@ int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct k
        /* Check cache ABI version */
        kr_cache_make_checkpoint(cache);
        (void) assert_right_version(cache);
+       /* Create LRU cache for packets */
+       cache->pkt_cache = lru_create_impl(KR_CACHE_DEFAULT_LRU_SIZE, mm, NULL);
+       if (cache->pkt_cache == NULL) {
+               return kr_error(ENOMEM);
+       }
        return 0;
 }
 
@@ -140,6 +145,8 @@ void kr_cache_close(struct kr_cache *cache)
        if (cache_isvalid(cache)) {
                cache_op(cache, close);
                cache->db = NULL;
+               lru_free_impl(cache->pkt_cache);
+               cache->pkt_cache = NULL;
        }
 }
 
@@ -379,11 +386,16 @@ static int cache_peek_real(kr_layer_t *ctx, knot_pkt_t *pkt)
        const uint8_t lowest_rank = get_lowest_rank(req, qry);
 
        /** 1. find the name or the closest (available) zone, not considering wildcards
-        *  1a. exact name+type match (can be negative answer in insecure zones)
+        *  1a. exact name+type match in packet cache (LRU)
+        *  1b. exact name+type match (can be negative answer in insecure zones)
         */
        knot_db_val_t key = key_exact_type_maypkt(k, qry->stype);
        knot_db_val_t val = { NULL, 0 };
-       ret = cache_op(cache, read, &key, &val, 1);
+       ret = find_from_pkt_cache(req, &key, &val, lowest_rank);
+       if (ret != 0) {
+               ret = cache_op(cache, read, &key, &val, 1);
+       }
+
        if (!ret) {
                /* found an entry: test conditions, materialize into pkt, etc. */
                ret = found_exact_hit(ctx, pkt, val, lowest_rank);
index 35ac4ba75728091cd60001c36cdcdf6bf4aac282..531976f0b6b6ca24ba25d0d519a4d59d5bf21ad9 100644 (file)
 #include <libknot/consts.h>
 #include <libknot/rrset.h>
 #include <sys/time.h>
+
 #include "lib/cache/cdb_api.h"
 #include "lib/defines.h"
+#include "lib/generic/lru.h"
 #include "contrib/ucw/config.h" /*uint*/
 
 /** When knot_pkt is passed from cache without ->wire, this is the ->size. */
@@ -52,6 +54,8 @@ struct kr_cache
        /* A pair of stamps for detection of real-time shifts during runtime. */
        struct timeval checkpoint_walltime; /**< Wall time on the last check-point. */
        uint64_t checkpoint_monotime; /**< Monotonic milliseconds on the last check-point. */
+
+       struct lru *pkt_cache;  /**< LRU cache for packets */
 };
 
 /**
index 972bae62fcf962cd5e0013fe99e2655105d000d4..0ae6bc64d10b6ba24707a94387f0b6d2f1f12052 100644 (file)
@@ -139,17 +139,20 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry,
 
        /* For now we stash the full packet byte-exactly as it came from upstream. */
        const uint16_t pkt_size = pkt->size;
-       knot_db_val_t val_new_entry = {
-               .data = NULL,
-               .len = offsetof(struct entry_h, data) + sizeof(pkt_size) + pkt->size,
-       };
+       const size_t entry_len = offsetof(struct entry_h, data) + sizeof(pkt_size) + pkt_size;
+
        /* Prepare raw memory for the new entry and fill it. */
        struct kr_cache *cache = &req->ctx->cache;
-       ret = entry_h_splice(&val_new_entry, rank, key, k->type, pkt_type,
-                               owner, qry, cache, qry->timestamp.tv_sec);
-       if (ret) return; /* some aren't really errors */
-       assert(val_new_entry.data);
-       struct entry_h *eh = val_new_entry.data;
+       if (cache->pkt_cache == NULL) {
+               return;
+       }
+
+       struct entry_h *eh = lru_get_impl(cache->pkt_cache, key.data, (uint)key.len, (uint)entry_len, true, NULL);
+       if (eh == NULL) {
+               return;
+       }
+
+       memset(eh, 0, offsetof(struct entry_h, data));
        eh->time = qry->timestamp.tv_sec;
        eh->ttl  = MAX(MIN(packet_ttl(pkt, is_negative), cache->ttl_max), cache->ttl_min);
        eh->rank = rank;
@@ -162,10 +165,38 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry,
                auto_free char *type_str = kr_rrtype_text(pkt_type),
                        *owner_str = kr_dname_text(owner);
                VERBOSE_MSG(qry, "=> stashed packet: rank 0%.2o, TTL %d, "
-                               "%s %s (%d B)\n",
+                               "%s %s (%zu B)\n",
                                eh->rank, eh->ttl,
-                               type_str, owner_str, (int)val_new_entry.len);
+                               type_str, owner_str, entry_len);
+       }
+}
+
+int find_from_pkt_cache(struct kr_request *req, knot_db_val_t *key, knot_db_val_t *val, uint8_t lowest_rank)
+{
+       struct kr_query *qry = req->current_query;
+       struct kr_cache *cache = &req->ctx->cache;
+       if (cache->pkt_cache == NULL) {
+               return kr_error(EINVAL);
+       }
+
+       /* Only check if key-value exists in LRU, don't insert */
+       struct entry_h *eh = lru_get_impl(cache->pkt_cache, key->data, (uint)key->len, 0, false, NULL);
+       if (eh == NULL) {
+               return kr_error(ENOENT);
+       }
+
+       int32_t new_ttl = get_new_ttl(eh, qry, qry->sname, qry->stype, qry->timestamp.tv_sec);
+       if (new_ttl < 0 || eh->rank < lowest_rank) {
+               return kr_error(ENOENT);
        }
+
+       /* Calculate value size */
+       uint16_t pkt_len;
+       memcpy(&pkt_len, eh->data, sizeof(pkt_len));
+
+       val->data = eh;
+       val->len = offsetof(struct entry_h, data) + sizeof(pkt_len) + pkt_len;
+       return kr_ok();
 }
 
 
index 9b0003430b4c5c38a5ac5d9dfa9c6034d5b56256..ca19c18d9bd60ddf7c7b92a16ff0ba0185a0a8c7 100644 (file)
@@ -137,6 +137,9 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry,
 int answer_from_pkt(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
                const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl);
 
+/** Try to find answer from packet cache.
+ */
+int find_from_pkt_cache(struct kr_request *req, knot_db_val_t *key, knot_db_val_t *val, uint8_t lowest_rank);
 
 /** Record is expiring if it has less than 1% TTL (or less than 5s) */
 static inline bool is_expiring(uint32_t orig_ttl, uint32_t new_ttl)
index 6595588376a651e93b40b80ce4f4bacf05040cec..c7e659afbb36be16a0c678f3cd44032b84608a3c 100644 (file)
@@ -74,6 +74,9 @@ static inline int KR_COLD kr_error(int x) {
 #define KR_EDNS_PAYLOAD 4096 /* Default UDP payload (max unfragmented UDP is 1452B) */
 #define KR_CACHE_DEFAULT_TTL_MIN (5) /* avoid bursts of queries */
 #define KR_CACHE_DEFAULT_TTL_MAX (6 * 24 * 3600) /* 6 days, like the root NS TTL */
+#ifndef KR_CACHE_DEFAULT_LRU_SIZE
+#define KR_CACHE_DEFAULT_LRU_SIZE 65536 /**< LRU packet cache size. */
+#endif
 
 #define KR_DNAME_STR_MAXLEN (KNOT_DNAME_TXT_MAXLEN + 1)
 #define KR_RRTYPE_STR_MAXLEN (16 + 1)