From: sthen Date: Thu, 20 Sep 2018 23:14:36 +0000 (+0000) Subject: import unbound 1.8.0, tested by myself and benno@ X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=7bc20e6db500d9f6533bfbedc9830b23d8985022;p=openbsd import unbound 1.8.0, tested by myself and benno@ --- diff --git a/usr.sbin/unbound/ipsecmod/ipsecmod.c b/usr.sbin/unbound/ipsecmod/ipsecmod.c index 3e4ee6a5350..c8400c6333e 100644 --- a/usr.sbin/unbound/ipsecmod/ipsecmod.c +++ b/usr.sbin/unbound/ipsecmod/ipsecmod.c @@ -341,6 +341,8 @@ ipsecmod_handle_query(struct module_qstate* qstate, qstate->env->cfg->ipsecmod_max_ttl; qstate->return_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC( qstate->return_msg->rep->ttl); + qstate->return_msg->rep->serve_expired_ttl = qstate->return_msg->rep->ttl + + qstate->env->cfg->serve_expired_ttl; } } } diff --git a/usr.sbin/unbound/respip/respip.c b/usr.sbin/unbound/respip/respip.c index 2e9313f271b..135c45fe53c 100644 --- a/usr.sbin/unbound/respip/respip.c +++ b/usr.sbin/unbound/respip/respip.c @@ -611,8 +611,9 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region, * EDNS0 OPT RR in the additional section appended on sending it out), * so the total number of RRsets is an_numrrsets. */ new_rep = construct_reply_info_base(region, rep->flags, - rep->qdcount, rep->ttl, rep->prefetch_ttl, an_numrrsets, - 0, 0, an_numrrsets, sec_status_insecure); + rep->qdcount, rep->ttl, rep->prefetch_ttl, + rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, + sec_status_insecure); if(!new_rep) return NULL; if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) diff --git a/usr.sbin/unbound/testcode/fake_event.c b/usr.sbin/unbound/testcode/fake_event.c index 80e3685c09e..777ed7355ed 100644 --- a/usr.sbin/unbound/testcode/fake_event.c +++ b/usr.sbin/unbound/testcode/fake_event.c @@ -374,8 +374,11 @@ answer_callback_from_entry(struct replay_runtime* runtime, c.fd = -1; c.buffer = sldns_buffer_new(runtime->bufsize); c.type = comm_udp; - if(pend->transport == transport_tcp) + if(pend->transport == transport_tcp) { c.type = comm_tcp; + c.tcp_timeout_msec = 30000; + c.tcp_keepalive = runtime->tcp_seen_keepalive; + } fill_buffer_with_reply(c.buffer, entry, pend->pkt, pend->pkt_len, pend->tcp_pkt_counter); repinfo.c = &c; @@ -423,6 +426,8 @@ answer_check_it(struct replay_runtime* runtime) else runtime->answer_list = ans->next; if(!ans->next) runtime->answer_last = prev; + if(ans->repinfo.c->tcp_keepalive) + runtime->tcp_seen_keepalive = 1; delete_replay_answer(ans); return; } else { @@ -452,9 +457,12 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) repinfo.c->fd = -1; repinfo.c->ev = (struct internal_event*)runtime; repinfo.c->buffer = sldns_buffer_new(runtime->bufsize); - if(todo->match->match_transport == transport_tcp) + if(todo->match->match_transport == transport_tcp) { repinfo.c->type = comm_tcp; - else repinfo.c->type = comm_udp; + repinfo.c->tcp_timeout_msec = 30000; + repinfo.c->tcp_keepalive = runtime->tcp_seen_keepalive; + } else + repinfo.c->type = comm_udp; fill_buffer_with_reply(repinfo.c->buffer, todo->match, NULL, 0, 0); log_info("testbound: incoming QUERY"); log_pkt("query pkt", todo->match->reply_list->reply_pkt, @@ -488,8 +496,11 @@ fake_pending_callback(struct replay_runtime* runtime, cb = p->callback; c.buffer = sldns_buffer_new(runtime->bufsize); c.type = comm_udp; - if(p->transport == transport_tcp) + if(p->transport == transport_tcp) { c.type = comm_tcp; + c.tcp_timeout_msec = 30000; + c.tcp_keepalive = runtime->tcp_seen_keepalive; + } if(todo->evt_type == repevt_back_reply && todo->match) { fill_buffer_with_reply(c.buffer, todo->match, p->pkt, p->pkt_len, p->tcp_pkt_counter); @@ -856,6 +867,8 @@ run_scenario(struct replay_runtime* runtime) struct listen_dnsport* listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports), size_t bufsize, int ATTR_UNUSED(tcp_accept_count), + int ATTR_UNUSED(tcp_idle_timeout), + struct tcl_list* ATTR_UNUSED(tcp_conn_limit), void* ATTR_UNUSED(sslctx), struct dt_env* ATTR_UNUSED(dtenv), comm_point_callback_type* cb, void* cb_arg) { diff --git a/usr.sbin/unbound/testcode/replay.h b/usr.sbin/unbound/testcode/replay.h index 81f0a2c275b..0cce0b49010 100644 --- a/usr.sbin/unbound/testcode/replay.h +++ b/usr.sbin/unbound/testcode/replay.h @@ -303,6 +303,9 @@ struct replay_runtime { /** the current time in microseconds */ struct timeval now_tv; + /** has TCP connection seen a keepalive? */ + int tcp_seen_keepalive; + /** signal handler callback */ void (*sig_cb)(int, void*); /** signal handler user arg */ diff --git a/usr.sbin/unbound/testcode/streamtcp.1 b/usr.sbin/unbound/testcode/streamtcp.1 index 7c738d9d278..526c8e1699d 100644 --- a/usr.sbin/unbound/testcode/streamtcp.1 +++ b/usr.sbin/unbound/testcode/streamtcp.1 @@ -12,6 +12,8 @@ .RB [ \-unsh ] .RB [ \-f .IR ipaddr[@port] ] +.RB [ \-d +.IR secs ] .I name .I type .I class @@ -50,6 +52,10 @@ Print program usage. .TP .B \-f \fIipaddr[@port] Specify the server to send the queries to. If not specified localhost (127.0.0.1) is used. +.TP +.B \-d \fIsecs +Delay after the connection before sending query. This tests the timeout +on the other side, eg. if shorter the connection is closed. .SH "EXAMPLES" .LP Some examples of use. diff --git a/usr.sbin/unbound/testcode/streamtcp.c b/usr.sbin/unbound/testcode/streamtcp.c index 0a636395fd3..497e3d2888f 100644 --- a/usr.sbin/unbound/testcode/streamtcp.c +++ b/usr.sbin/unbound/testcode/streamtcp.c @@ -44,6 +44,8 @@ #include #endif #include +#include +#include #include "util/locks.h" #include "util/log.h" #include "util/net_help.h" @@ -71,6 +73,7 @@ static void usage(char* argv[]) printf("-f server what ipaddr@portnr to send the queries to\n"); printf("-u use UDP. No retries are attempted.\n"); printf("-n do not wait for an answer.\n"); + printf("-d secs delay after connection before sending query\n"); printf("-s use ssl\n"); printf("-h this help text\n"); exit(1); @@ -275,7 +278,8 @@ static int get_random(void) /** send the TCP queries and print answers */ static void -send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs) +send_em(const char* svr, int udp, int usessl, int noanswer, int delay, + int num, char** qs) { sldns_buffer* buf = sldns_buffer_new(65553); int fd = open_svr(svr, udp); @@ -310,6 +314,13 @@ send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs) } } for(i=0; itype == comm_udp) + return 1; + + /* To respond with a Keepalive option, the client connection + * must have received one message with a TCP Keepalive EDNS option, + * and that option must have 0 length data. Subsequent messages + * sent on that connection will have a TCP Keepalive option. + */ + if(c->tcp_keepalive || + edns_opt_list_find(edns_in->opt_list, LDNS_EDNS_KEEPALIVE)) { + int keepalive = c->tcp_timeout_msec / 100; + uint8_t data[2]; + data[0] = (uint8_t)((keepalive >> 8) & 0xff); + data[1] = (uint8_t)(keepalive & 0xff); + if(!edns_opt_list_append(&edns_out->opt_list, LDNS_EDNS_KEEPALIVE, + sizeof(data), data, region)) + return 0; + c->tcp_keepalive = 1; + } + return 1; +} + +int apply_edns_options(struct edns_data* edns_out, struct edns_data* edns_in, + struct config_file* cfg, struct comm_point* c, struct regional* region) +{ + if(cfg->do_tcp_keepalive && + !edns_keepalive(edns_out, edns_in, c, region)) + return 0; + + return 1; +} diff --git a/usr.sbin/unbound/util/edns.h b/usr.sbin/unbound/util/edns.h new file mode 100644 index 00000000000..a4ee7def634 --- /dev/null +++ b/usr.sbin/unbound/util/edns.h @@ -0,0 +1,62 @@ +/* + * util/edns.h - handle base EDNS options. + * + * Copyright (c) 2018, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains functions for base EDNS options. + */ + +#ifndef UTIL_EDNS_H +#define UTIL_EDNS_H + +struct edns_data; +struct config_file; +struct comm_point; +struct regional; + +/** + * Apply common EDNS options. + * + * @param edns_out: initialised edns information with outbound edns. + * @param edns_in: initialised edns information with received edns. + * @param cfg: configuration. + * @param c: comm channel. + * @param region: the region to allocate the edns options in. + */ +int apply_edns_options(struct edns_data* edns_out, struct edns_data* edns_in, + struct config_file* cfg, struct comm_point* c, struct regional* region); + +#endif diff --git a/usr.sbin/unbound/util/rtt.c b/usr.sbin/unbound/util/rtt.c index 5d86f13378e..f51576fb57f 100644 --- a/usr.sbin/unbound/util/rtt.c +++ b/usr.sbin/unbound/util/rtt.c @@ -41,6 +41,7 @@ */ #include "config.h" #include "util/rtt.h" +#include "iterator/iterator.h" /* overwritten by config: infra_cache_min_rtt: */ int RTT_MIN_TIMEOUT = 50; @@ -61,7 +62,7 @@ void rtt_init(struct rtt_info* rtt) { rtt->srtt = 0; - rtt->rttvar = 94; + rtt->rttvar = UNKNOWN_SERVER_NICENESS/4; rtt->rto = calc_rto(rtt); /* default value from the book is 0 + 4*0.75 = 3 seconds */ /* first RTO is 0 + 4*0.094 = 0.376 seconds */ diff --git a/usr.sbin/unbound/util/tcp_conn_limit.c b/usr.sbin/unbound/util/tcp_conn_limit.c new file mode 100644 index 00000000000..d7d86a5407b --- /dev/null +++ b/usr.sbin/unbound/util/tcp_conn_limit.c @@ -0,0 +1,194 @@ +/* + * daemon/tcp_conn_limit.c - client TCP connection limit storage for the server. + * + * Copyright (c) 2018, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file helps the server discard excess TCP connections. + */ +#include "config.h" +#include "util/regional.h" +#include "util/log.h" +#include "util/config_file.h" +#include "util/net_help.h" +#include "util/tcp_conn_limit.h" +#include "services/localzone.h" +#include "sldns/str2wire.h" + +struct tcl_list* +tcl_list_create(void) +{ + struct tcl_list* tcl = (struct tcl_list*)calloc(1, + sizeof(struct tcl_list)); + if(!tcl) + return NULL; + tcl->region = regional_create(); + if(!tcl->region) { + tcl_list_delete(tcl); + return NULL; + } + return tcl; +} + +static void +tcl_list_free_node(rbnode_type* node, void* ATTR_UNUSED(arg)) +{ + struct tcl_addr* n = (struct tcl_addr*) node; + lock_quick_destroy(&n->lock); +#ifdef THREADS_DISABLED + (void)n; +#endif +} + +void +tcl_list_delete(struct tcl_list* tcl) +{ + if(!tcl) + return; + traverse_postorder(&tcl->tree, tcl_list_free_node, NULL); + regional_destroy(tcl->region); + free(tcl); +} + +/** insert new address into tcl_list structure */ +static struct tcl_addr* +tcl_list_insert(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen, int net, uint32_t limit, + int complain_duplicates) +{ + struct tcl_addr* node = regional_alloc_zero(tcl->region, + sizeof(struct tcl_addr)); + if(!node) + return NULL; + lock_quick_init(&node->lock); + node->limit = limit; + if(!addr_tree_insert(&tcl->tree, &node->node, addr, addrlen, net)) { + if(complain_duplicates) + verbose(VERB_QUERY, "duplicate tcl address ignored."); + } + return node; +} + +/** apply tcl_list string */ +static int +tcl_list_str_cfg(struct tcl_list* tcl, const char* str, const char* s2, + int complain_duplicates) +{ + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + uint32_t limit; + if(atoi(s2) < 0) { + log_err("bad connection limit %s", s2); + return 0; + } + limit = (uint32_t)atoi(s2); + if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) { + log_err("cannot parse connection limit netblock: %s", str); + return 0; + } + if(!tcl_list_insert(tcl, &addr, addrlen, net, limit, + complain_duplicates)) { + log_err("out of memory"); + return 0; + } + return 1; +} + +/** read tcl_list config */ +static int +read_tcl_list(struct tcl_list* tcl, struct config_file* cfg) +{ + struct config_str2list* p; + for(p = cfg->tcp_connection_limits; p; p = p->next) { + log_assert(p->str && p->str2); + if(!tcl_list_str_cfg(tcl, p->str, p->str2, 1)) + return 0; + } + return 1; +} + +int +tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg) +{ + regional_free_all(tcl->region); + addr_tree_init(&tcl->tree); + if(!read_tcl_list(tcl, cfg)) + return 0; + addr_tree_init_parents(&tcl->tree); + return 1; +} + +int +tcl_new_connection(struct tcl_addr* tcl) +{ + if(tcl) { + int res = 1; + lock_quick_lock(&tcl->lock); + if(tcl->count >= tcl->limit) + res = 0; + else + tcl->count++; + lock_quick_unlock(&tcl->lock); + return res; + } + return 1; +} + +void +tcl_close_connection(struct tcl_addr* tcl) +{ + if(tcl) { + lock_quick_lock(&tcl->lock); + log_assert(tcl->count > 0); + tcl->count--; + lock_quick_unlock(&tcl->lock); + } +} + +struct tcl_addr* +tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen) +{ + return (struct tcl_addr*)addr_tree_lookup(&tcl->tree, + addr, addrlen); +} + +size_t +tcl_list_get_mem(struct tcl_list* tcl) +{ + if(!tcl) return 0; + return sizeof(*tcl) + regional_get_mem(tcl->region); +} diff --git a/usr.sbin/unbound/util/tcp_conn_limit.h b/usr.sbin/unbound/util/tcp_conn_limit.h new file mode 100644 index 00000000000..4fb71a32803 --- /dev/null +++ b/usr.sbin/unbound/util/tcp_conn_limit.h @@ -0,0 +1,130 @@ +/* + * daemon/tcp_conn_limit.h - client TCP connection limit storage for the server. + * + * Copyright (c) 2018, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file keeps track of the limit on the number of TCP connections + * each client makes the server. + */ + +#ifndef DAEMON_TCP_CONN_LIMIT_H +#define DAEMON_TCP_CONN_LIMIT_H +#include "util/storage/dnstree.h" +#include "util/locks.h" +struct config_file; +struct regional; + +/** + * TCP connection limit storage structure + */ +struct tcl_list { + /** regional for allocation */ + struct regional* region; + /** + * Tree of the addresses that are TCP connection limited. + * contents of type tcl_addr. + */ + rbtree_type tree; +}; + +/** + * + * An address span with connection limit information + */ +struct tcl_addr { + /** node in address tree */ + struct addr_tree_node node; + /** lock on structure data */ + lock_quick_type lock; + /** connection limit on this netblock */ + uint32_t limit; + /** current connection count on this netblock */ + uint32_t count; +}; + +/** + * Create TCP connection limit structure + * @return new structure or NULL on error. + */ +struct tcl_list* tcl_list_create(void); + +/** + * Delete TCP connection limit structure. + * @param tcl: to delete. + */ +void tcl_list_delete(struct tcl_list* tcl); + +/** + * Process TCP connection limit config. + * @param tcl: where to store. + * @param cfg: config options. + * @return 0 on error. + */ +int tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg); + +/** + * Increment TCP connection count if found, provided the + * count was below the limit. + * @param tcl: structure for tcl storage, or NULL. + * @return: 0 if limit reached, 1 if tcl was NULL or limit not reached. + */ +int tcl_new_connection(struct tcl_addr* tcl); + +/** + * Decrement TCP connection count if found. + * @param tcl: structure for tcl storage, or NULL. + */ +void tcl_close_connection(struct tcl_addr* tcl); + +/** + * Lookup address to see its TCP connection limit structure + * @param tcl: structure for address storage. + * @param addr: address to check + * @param addrlen: length of addr. + * @return: tcl structure from this address. + */ +struct tcl_addr* +tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen); + +/** + * Get memory used by TCP connection limit structure. + * @param tcl: structure for address storage. + * @return bytes in use. + */ +size_t tcl_list_get_mem(struct tcl_list* tcl); + +#endif /* DAEMON_TCP_CONN_LIMIT_H */ diff --git a/usr.sbin/unbound/validator/val_kcache.c b/usr.sbin/unbound/validator/val_kcache.c index 22070cc6a90..e0b88b6df81 100644 --- a/usr.sbin/unbound/validator/val_kcache.c +++ b/usr.sbin/unbound/validator/val_kcache.c @@ -89,7 +89,7 @@ key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey, if(key_entry_isbad(k) && qstate->errinf && qstate->env->cfg->val_log_level >= 2) { /* on malloc failure there is simply no reason string */ - key_entry_set_reason(k, errinf_to_str(qstate)); + key_entry_set_reason(k, errinf_to_str_bogus(qstate)); } key_entry_hash(k); slabhash_insert(kcache->slab, k->entry.hash, &k->entry,