From 16734a0e042c89b502a9f4a4626693b53c0e346c Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Tue, 23 Sep 2008 14:07:02 +0000 Subject: [PATCH] flush data from cache. git-svn-id: file:///svn/unbound/trunk@1268 be551aaa-1e26-0410-a405-d3ace91eadb9 --- daemon/remote.c | 185 +++++++++++++++++++++++++++++++++++++ doc/Changelog | 1 + doc/control_proto_spec.txt | 6 +- doc/plan | 2 +- doc/unbound-control.8.in | 16 ++++ services/cache/rrset.c | 14 +++ services/cache/rrset.h | 12 +++ smallapp/unbound-control.c | 6 ++ 8 files changed, 238 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 16ca5c2c1..cc7082a20 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -59,6 +59,9 @@ #include "util/storage/slabhash.h" #include "util/fptr_wlist.h" #include "util/data/dname.h" +#include "validator/validator.h" +#include "validator/val_kcache.h" +#include "validator/val_kentry.h" #ifdef HAVE_SYS_TYPES_H # include @@ -982,6 +985,182 @@ do_lookup(SSL* ssl, struct worker* worker, char* arg) free(nm); } +/** flush a type */ +static void +do_flush_type(SSL* ssl, struct worker* worker, char* arg) +{ + uint8_t* nm; + int nmlabs; + size_t nmlen; + char* arg2; + uint16_t t; + if(!find_arg2(ssl, arg, &arg2)) + return; + if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) + return; + t = ldns_get_rr_type_by_name(arg2); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + t, LDNS_RR_CLASS_IN, 0); + + free(nm); + send_ok(ssl); +} + +/** + * Local info for deletion functions + */ +struct del_info { + /** worker */ + struct worker* worker; + /** name to delete */ + uint8_t* name; + /** length */ + size_t len; + /** labels */ + int labs; + /** time to invalidate to */ + uint32_t expired; + /** number of rrsets removed */ + size_t num_rrsets; + /** number of key entries removed */ + size_t num_keys; +}; + +/** callback to delete rrsets in a zone */ +static void +zone_del_rrset(struct lruhash_entry* e, void* arg) +{ + /* entry is locked */ + struct del_info* inf = (struct del_info*)arg; + struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key; + if(dname_subdomain_c(k->rk.dname, inf->name)) { + struct packed_rrset_data* d = + (struct packed_rrset_data*)e->data; + d->ttl = inf->expired; + inf->num_rrsets++; + } +} + +/** callback to delete keys in zone */ +static void +zone_del_kcache(struct lruhash_entry* e, void* arg) +{ + /* entry is locked */ + struct del_info* inf = (struct del_info*)arg; + struct key_entry_key* k = (struct key_entry_key*)e->key; + if(dname_subdomain_c(k->name, inf->name)) { + struct key_entry_data* d = (struct key_entry_data*)e->data; + d->ttl = inf->expired; + inf->num_keys++; + } +} + +/** traverse a lruhash */ +static void +lruhash_traverse(struct lruhash* h, int wr, + void (*func)(struct lruhash_entry*, void*), void* arg) +{ + size_t i; + struct lruhash_entry* e; + + lock_quick_lock(&h->lock); + for(i=0; isize; i++) { + lock_quick_lock(&h->array[i].lock); + for(e = h->array[i].overflow_list; e; e = e->overflow_next) { + if(wr) { + lock_rw_wrlock(&e->lock); + } else { + lock_rw_rdlock(&e->lock); + } + (*func)(e, arg); + lock_rw_unlock(&e->lock); + } + lock_quick_unlock(&h->array[i].lock); + } + lock_quick_unlock(&h->lock); +} + +/** traverse a slabhash */ +static void +slabhash_traverse(struct slabhash* sh, int wr, + void (*func)(struct lruhash_entry*, void*), void* arg) +{ + size_t i; + for(i=0; isize; i++) + lruhash_traverse(sh->array[i], wr, func, arg); +} + +/** remove all rrsets and keys from zone from cache */ +static void +do_flush_zone(SSL* ssl, struct worker* worker, char* arg) +{ + uint8_t* nm; + int nmlabs; + size_t nmlen; + struct del_info inf; + int idx; + if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) + return; + /* delete all RRs and key entries from zone */ + /* what we do is to set them all expired */ + inf.worker = worker; + inf.name = nm; + inf.len = nmlen; + inf.labs = nmlabs; + inf.expired = *worker->env.now; + inf.expired -= 3; /* handle 3 seconds skew between threads */ + inf.num_rrsets = 0; + inf.num_keys = 0; + slabhash_traverse(&worker->env.rrset_cache->table, 1, + &zone_del_rrset, &inf); + + /* and validator cache */ + idx = modstack_find(&worker->daemon->mods, "validator"); + if(idx != -1) { + struct val_env* ve = (struct val_env*)worker->env.modinfo[idx]; + slabhash_traverse(ve->kcache->slab, 1, &zone_del_kcache, &inf); + } + + free(nm); + + (void)ssl_printf(ssl, "ok removed %u rrsets and %u key entries\n", + (unsigned)inf.num_rrsets, (unsigned)inf.num_keys); +} + +/** remove name rrset from cache */ +static void +do_flush_name(SSL* ssl, struct worker* worker, char* arg) +{ + uint8_t* nm; + int nmlabs; + size_t nmlen; + if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) + return; + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_AAAA, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_CNAME, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_DNAME, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_MX, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_PTR, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_SRV, LDNS_RR_CLASS_IN, 0); + rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, + LDNS_RR_TYPE_NAPTR, LDNS_RR_CLASS_IN, 0); + + free(nm); + send_ok(ssl); +} + /** execute a remote control command */ static void execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) @@ -1010,6 +1189,12 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) if(load_cache(ssl, rc->worker)) send_ok(ssl); } else if(strncmp(p, "lookup", 6) == 0) { do_lookup(ssl, rc->worker, skipwhite(p+6)); + } else if(strncmp(p, "flush_zone", 10) == 0) { + do_flush_zone(ssl, rc->worker, skipwhite(p+10)); + } else if(strncmp(p, "flush_type", 10) == 0) { + do_flush_type(ssl, rc->worker, skipwhite(p+10)); + } else if(strncmp(p, "flush", 5) == 0) { + do_flush_name(ssl, rc->worker, skipwhite(p+5)); } else { (void)ssl_printf(ssl, "error unknown command '%s'\n", p); } diff --git a/doc/Changelog b/doc/Changelog index 0bf4454ab..bf2791dc4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ 23 September 2008: Wouter - Msg cache is loaded. A cache load enables cache responses. + - unbound-control flush [name], flush_type and flush_zone. 22 September 2008: Wouter - dump_cache and load_cache statements in unbound-control. diff --git a/doc/control_proto_spec.txt b/doc/control_proto_spec.txt index 9b3012326..f6d330e1a 100644 --- a/doc/control_proto_spec.txt +++ b/doc/control_proto_spec.txt @@ -37,10 +37,10 @@ load_cache client sends cache contents (like from dump_cache), which is stored in the cache. end of data indicated with a line with 'EOF' on it. The data is sent after the query line. -flush_name +flush flushes some information regarding the name from the cache. - removes the A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR types. - Does not remote other types. + removes the A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV, NAPTR types. + Does not remove other types. flush_type removes rrtype entry from the cache. flush_zone diff --git a/doc/plan b/doc/plan index bde157128..2a7f3b180 100644 --- a/doc/plan +++ b/doc/plan @@ -56,7 +56,7 @@ not stats on SIGUSR1. perhaps also see which slow auth servers cause >1sec value + remote control to add/remove localinfo, redirects. + remote control to load/store cache contents + remote control to start, stop, reload. -* remote control to flush names or domains (all under a name) from the ++ remote control to flush names or domains (all under a name) from the cache. Include NSes. And the A, AAAA for its NSes. + remote control to see delegation; what servers would be used to get data for a name. diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index aa92b2792..ffe017b31 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -95,6 +95,22 @@ in old or wrong data returned to clients. .B lookup \fIname Print to stdout the name servers that would be used to look up the name specified. +.TP +.B flush \fIname +Remove the name from the cache. Removes the types +A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV and NAPTR. +Because that is fast to do. Other record types can be removed using +.B flush_type +or +.B flush_zone\fR. +.TP +.B flush_type \fIname\fR \fItype +Remove the name, type information from the cache. +.TP +.B flush_zone \fIname +Remove all information at or below the name from the cache. +The rrsets and key entries are removed so that new lookups will be performed. +This needs to walk and inspect the entire cache, and is a slow operation. .SH "EXIT CODE" The unbound-control program exits with status code 1 on error, 0 on success. .SH "SET UP" diff --git a/services/cache/rrset.c b/services/cache/rrset.c index e9270551a..c9856c96c 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -371,3 +371,17 @@ rrset_check_sec_status(struct rrset_cache* r, } lock_rw_unlock(&e->lock); } + +void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, + uint16_t type, uint16_t dclass, uint32_t flags) +{ + struct ub_packed_rrset_key key; + key.entry.key = &key; + key.rk.dname = nm; + key.rk.dname_len = nmlen; + key.rk.rrset_class = htons(dclass); + key.rk.type = htons(type); + key.rk.flags = flags; + key.entry.hash = rrset_key_hash(&key.rk); + slabhash_remove(&r->table, key.entry.hash, &key); +} diff --git a/services/cache/rrset.h b/services/cache/rrset.h index 02241d4bd..e414107d2 100644 --- a/services/cache/rrset.h +++ b/services/cache/rrset.h @@ -209,6 +209,18 @@ void rrset_update_sec_status(struct rrset_cache* r, void rrset_check_sec_status(struct rrset_cache* r, struct ub_packed_rrset_key* rrset, uint32_t now); +/** + * Remove an rrset from the cache, by name and type and flags + * @param r: rrset cache + * @param nm: name of rrset + * @param nmlen: length of name + * @param type: type of rrset + * @param dclass: class of rrset, host order + * @param flags: flags of rrset, host order + */ +void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, + uint16_t type, uint16_t dclass, uint32_t flags); + /** mark rrset to be deleted, set id=0 */ void rrset_markdel(void* key); diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index a513db0c6..050a6d5b5 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -71,6 +71,12 @@ usage() printf(" dump_cache print cache to stdout\n"); printf(" load_cache load cache from stdin\n"); printf(" lookup [name] print nameservers for name\n"); + printf(" flush [name] flushes common types for name from cache\n"); + printf(" types: A, AAAA, MX, PTR, NS,\n"); + printf(" SOA, CNAME, DNAME, SRV, NAPTR\n"); + printf(" flush_type [name] [type] flush name, type from cache\n"); + printf(" flush_zone [name] flush everything at or under name\n"); + printf(" from rr and dnssec caches\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE in source package for details.\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); -- 2.47.3