diff options
Diffstat (limited to 'external/unbound/services/cache')
-rw-r--r-- | external/unbound/services/cache/dns.c | 28 | ||||
-rw-r--r-- | external/unbound/services/cache/dns.h | 16 | ||||
-rw-r--r-- | external/unbound/services/cache/infra.c | 139 | ||||
-rw-r--r-- | external/unbound/services/cache/infra.h | 50 | ||||
-rw-r--r-- | external/unbound/services/cache/rrset.c | 10 | ||||
-rw-r--r-- | external/unbound/services/cache/rrset.h | 2 |
6 files changed, 217 insertions, 28 deletions
diff --git a/external/unbound/services/cache/dns.c b/external/unbound/services/cache/dns.c index e14e636db..a8fde9f28 100644 --- a/external/unbound/services/cache/dns.c +++ b/external/unbound/services/cache/dns.c @@ -106,7 +106,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside, + hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, struct reply_info* qrep, struct regional* region) { struct msgreply_entry* e; @@ -188,12 +188,13 @@ msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, { struct lruhash_entry* e; struct query_info k; - hashvalue_t h; + hashvalue_type h; k.qname = qname; k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; + k.local_alias = NULL; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, wr); @@ -361,6 +362,7 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, msg->qinfo.qname_len = qnamelen; msg->qinfo.qtype = qtype; msg->qinfo.qclass = qclass; + msg->qinfo.local_alias = NULL; /* non-packed reply_info, because it needs to grow the array */ msg->rep = (struct reply_info*)regional_alloc_zero(region, sizeof(struct reply_info)-sizeof(struct rrset_ref)); @@ -477,8 +479,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num) return msg; } -/** generate dns_msg from cached message */ -static struct dns_msg* +struct dns_msg* tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, struct regional* region, time_t now, struct regional* scratch) { @@ -523,8 +524,11 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, return NULL; } } - rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, + if(env) + rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, r->rrset_count); + else + rrset_array_unlock(r->ref, r->rrset_count); return msg; } @@ -707,7 +711,7 @@ dns_cache_lookup(struct module_env* env, { struct lruhash_entry* e; struct query_info k; - hashvalue_t h; + hashvalue_type h; time_t now = *env->now; struct ub_packed_rrset_key* rrset; @@ -716,6 +720,7 @@ dns_cache_lookup(struct module_env* env, k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; + k.local_alias = NULL; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(e) { @@ -795,6 +800,12 @@ dns_cache_lookup(struct module_env* env, dname_remove_label(&k.qname, &k.qname_len); h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); + if(!e && k.qtype != LDNS_RR_TYPE_A && + env->cfg->qname_minimisation) { + k.qtype = LDNS_RR_TYPE_A; + h = query_info_hash(&k, flags); + e = slabhash_lookup(env->msg_cache, h, &k, 0); + } if(e) { struct reply_info* data = (struct reply_info*)e->data; struct dns_msg* msg; @@ -810,7 +821,8 @@ dns_cache_lookup(struct module_env* env, } lock_rw_unlock(&e->lock); } - } + k.qtype = qtype; + } /* fill common RR types for ANY response to avoid requery */ if(qtype == LDNS_RR_TYPE_ANY) { @@ -855,7 +867,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf, } else { /* store msg, and rrsets */ struct query_info qinf; - hashvalue_t h; + hashvalue_type h; qinf = *msgqinf; qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len); diff --git a/external/unbound/services/cache/dns.h b/external/unbound/services/cache/dns.h index 69796c2eb..0dfb68874 100644 --- a/external/unbound/services/cache/dns.h +++ b/external/unbound/services/cache/dns.h @@ -106,7 +106,7 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf, * @param region: to allocate into for qmsg. */ void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside, + hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, struct reply_info* qrep, struct regional* region); /** @@ -126,6 +126,20 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct regional* region, struct dns_msg** msg, time_t timenow); +/** + * generate dns_msg from cached message + * @param env: module environment with the DNS cache. NULL if the LRU from cache + * does not need to be touched. + * @param q: query info, contains qname that will make up the dns message. + * @param r: reply info that, together with qname, will make up the dns message. + * @param region: where to allocate dns message. + * @param now: the time now, for check if TTL on cache entry is ok. + * @param scratch: where to allocate temporary data. + * */ +struct dns_msg* tomsg(struct module_env* env, struct query_info* q, + struct reply_info* r, struct regional* region, time_t now, + struct regional* scratch); + /** * Find cached message * @param env: module environment with the DNS cache. diff --git a/external/unbound/services/cache/infra.c b/external/unbound/services/cache/infra.c index c0049d8b6..314c85ef5 100644 --- a/external/unbound/services/cache/infra.c +++ b/external/unbound/services/cache/infra.c @@ -61,6 +61,10 @@ /** ratelimit value for delegation point */ int infra_dp_ratelimit = 0; +/** ratelimit value for client ip addresses, + * in queries per second. */ +int infra_ip_ratelimit = 0; + size_t infra_sizefunc(void* k, void* ATTR_UNUSED(d)) { @@ -244,11 +248,19 @@ infra_create(struct config_file* cfg) } name_tree_init_parents(&infra->domain_limits); } + infra_ip_ratelimit = cfg->ip_ratelimit; + infra->client_ip_rates = slabhash_create(cfg->ratelimit_slabs, + INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, + &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL); + if(!infra->client_ip_rates) { + infra_delete(infra); + return NULL; + } return infra; } /** delete domain_limit entries */ -static void domain_limit_free(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) { if(n) { free(((struct domain_limit_data*)n)->node.name); @@ -264,6 +276,7 @@ infra_delete(struct infra_cache* infra) slabhash_delete(infra->hosts); slabhash_delete(infra->domain_rates); traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); + slabhash_delete(infra->client_ip_rates); free(infra); } @@ -284,31 +297,38 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg) return infra; } -/** calculate the hash value for a host key */ -static hashvalue_t -hash_addr(struct sockaddr_storage* addr, socklen_t addrlen) +/** calculate the hash value for a host key + * set use_port to a non-0 number to use the port in + * the hash calculation; 0 to ignore the port.*/ +static hashvalue_type +hash_addr(struct sockaddr_storage* addr, socklen_t addrlen, + int use_port) { - hashvalue_t h = 0xab; + hashvalue_type h = 0xab; /* select the pieces to hash, some OS have changing data inside */ if(addr_is_ip6(addr, addrlen)) { struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h); - h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); + if(use_port){ + h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); + } h = hashlittle(&in6->sin6_addr, INET6_SIZE, h); } else { struct sockaddr_in* in = (struct sockaddr_in*)addr; h = hashlittle(&in->sin_family, sizeof(in->sin_family), h); - h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); + if(use_port){ + h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); + } h = hashlittle(&in->sin_addr, INET_SIZE, h); } return h; } /** calculate infra hash for a key */ -static hashvalue_t +static hashvalue_type hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name) { - return dname_query_hash(name, hash_addr(addr, addrlen)); + return dname_query_hash(name, hash_addr(addr, addrlen, 1)); } /** lookup version that does not check host ttl (you check it) */ @@ -726,12 +746,36 @@ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, return infra_dp_ratelimit; } +size_t ip_rate_sizefunc(void* k, void* ATTR_UNUSED(d)) +{ + struct ip_rate_key* key = (struct ip_rate_key*)k; + return sizeof(*key) + sizeof(struct ip_rate_data) + + lock_get_mem(&key->entry.lock); +} + +int ip_rate_compfunc(void* key1, void* key2) +{ + struct ip_rate_key* k1 = (struct ip_rate_key*)key1; + struct ip_rate_key* k2 = (struct ip_rate_key*)key2; + return sockaddr_cmp_addr(&k1->addr, k1->addrlen, + &k2->addr, k2->addrlen); +} + +void ip_rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) +{ + struct ip_rate_key* key = (struct ip_rate_key*)k; + if(!key) + return; + lock_rw_destroy(&key->entry.lock); + free(key); +} + /** find data item in array, for write access, caller unlocks */ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, uint8_t* name, size_t namelen, int wr) { struct rate_key key; - hashvalue_t h = dname_query_hash(name, 0xab); + hashvalue_type h = dname_query_hash(name, 0xab); memset(&key, 0, sizeof(key)); key.name = name; key.namelen = namelen; @@ -739,11 +783,25 @@ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, return slabhash_lookup(infra->domain_rates, h, &key, wr); } +/** find data item in array for ip addresses */ +struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra, + struct comm_reply* repinfo, int wr) +{ + struct ip_rate_key key; + hashvalue_type h = hash_addr(&(repinfo->addr), + repinfo->addrlen, 0); + memset(&key, 0, sizeof(key)); + key.addr = repinfo->addr; + key.addrlen = repinfo->addrlen; + key.entry.hash = h; + return slabhash_lookup(infra->client_ip_rates, h, &key, wr); +} + /** create rate data item for name, number 1 in now */ static void infra_create_ratedata(struct infra_cache* infra, uint8_t* name, size_t namelen, time_t timenow) { - hashvalue_t h = dname_query_hash(name, 0xab); + hashvalue_type h = dname_query_hash(name, 0xab); struct rate_key* k = (struct rate_key*)calloc(1, sizeof(*k)); struct rate_data* d = (struct rate_data*)calloc(1, sizeof(*d)); if(!k || !d) { @@ -767,6 +825,30 @@ static void infra_create_ratedata(struct infra_cache* infra, slabhash_insert(infra->domain_rates, h, &k->entry, d, NULL); } +/** create rate data item for ip address */ +static void infra_ip_create_ratedata(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow) +{ + hashvalue_type h = hash_addr(&(repinfo->addr), + repinfo->addrlen, 0); + struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); + struct ip_rate_data* d = (struct ip_rate_data*)calloc(1, sizeof(*d)); + if(!k || !d) { + free(k); + free(d); + return; /* alloc failure */ + } + k->addr = repinfo->addr; + k->addrlen = repinfo->addrlen; + lock_rw_init(&k->entry.lock); + k->entry.hash = h; + k->entry.key = k; + k->entry.data = d; + d->qps[0] = 1; + d->timestamp[0] = timenow; + slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); +} + /** find the second and return its rate counter, if none, remove oldest */ static int* infra_rate_find_second(void* data, time_t t) { @@ -875,6 +957,41 @@ infra_get_mem(struct infra_cache* infra) { size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts); if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates); + if(infra->client_ip_rates) s += slabhash_get_mem(infra->client_ip_rates); /* ignore domain_limits because walk through tree is big */ return s; } + +int infra_ip_ratelimit_inc(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow) +{ + int max; + struct lruhash_entry* entry; + + /* not enabled */ + if(!infra_ip_ratelimit) { + return 1; + } + /* find or insert ratedata */ + entry = infra_find_ip_ratedata(infra, repinfo, 1); + if(entry) { + int premax = infra_rate_max(entry->data, timenow); + int* cur = infra_rate_find_second(entry->data, timenow); + (*cur)++; + max = infra_rate_max(entry->data, timenow); + lock_rw_unlock(&entry->lock); + + if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) { + char client_ip[128]; + addr_to_str((struct sockaddr_storage *)&repinfo->addr, + repinfo->addrlen, client_ip, sizeof(client_ip)); + verbose(VERB_OPS, "ratelimit exceeded %s %d", client_ip, + infra_ip_ratelimit); + } + return (max <= infra_ip_ratelimit); + } + + /* create */ + infra_ip_create_ratedata(infra, repinfo, timenow); + return 1; +} diff --git a/external/unbound/services/cache/infra.h b/external/unbound/services/cache/infra.h index fc7abb7c4..6f9471a39 100644 --- a/external/unbound/services/cache/infra.h +++ b/external/unbound/services/cache/infra.h @@ -36,7 +36,10 @@ /** * \file * - * This file contains the infrastructure cache. + * This file contains the infrastructure cache, as well as rate limiting. + * Note that there are two sorts of rate-limiting here: + * - Pre-cache, per-query rate limiting (query ratelimits) + * - Post-cache, per-domain name rate limiting (infra-ratelimits) */ #ifndef SERVICES_CACHE_INFRA_H @@ -44,6 +47,8 @@ #include "util/storage/lruhash.h" #include "util/storage/dnstree.h" #include "util/rtt.h" +#include "util/netevent.h" +#include "util/data/msgreply.h" struct slabhash; struct config_file; @@ -112,7 +117,9 @@ struct infra_cache { /** hash table with query rates per name: rate_key, rate_data */ struct slabhash* domain_rates; /** ratelimit settings for domains, struct domain_limit_data */ - rbtree_t domain_limits; + rbtree_type domain_limits; + /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ + struct slabhash* client_ip_rates; }; /** ratelimit, unless overridden by domain_limits, 0 is off */ @@ -142,6 +149,21 @@ struct rate_key { size_t namelen; }; +/** ip ratelimit, 0 is off */ +extern int infra_ip_ratelimit; + +/** + * key for ip_ratelimit lookups, a source IP. + */ +struct ip_rate_key { + /** lruhash key entry */ + struct lruhash_entry entry; + /** client ip information */ + struct sockaddr_storage addr; + /** length of address */ + socklen_t addrlen; +}; + /** number of seconds to track qps rate */ #define RATE_WINDOW 2 @@ -160,6 +182,8 @@ struct rate_data { time_t timestamp[RATE_WINDOW]; }; +#define ip_rate_data rate_data + /** infra host cache default hash lookup size */ #define INFRA_HOST_STARTSIZE 32 /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ @@ -381,6 +405,16 @@ int infra_rate_max(void* data, time_t now); int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, size_t namelen); +/** Update query ratelimit hash and decide + * whether or not a query should be dropped. + * @param infra: infra cache + * @param repinfo: information about client + * @param timenow: what time it is now. + * @return 1 if it could be incremented. 0 if the increment overshot the + * ratelimit and the query should be dropped. */ +int infra_ip_ratelimit_inc(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow); + /** * Get memory used by the infra cache. * @param infra: infrastructure cache. @@ -413,4 +447,16 @@ void rate_delkeyfunc(void* k, void* arg); /** delete data */ void rate_deldatafunc(void* d, void* arg); +/* calculate size for the client ip hashtable */ +size_t ip_rate_sizefunc(void* k, void* d); + +/* compare two addresses */ +int ip_rate_compfunc(void* key1, void* key2); + +/* delete key, and destroy the lock */ +void ip_rate_delkeyfunc(void* d, void* arg); + +/* delete data */ +#define ip_rate_deldatafunc rate_deldatafunc + #endif /* SERVICES_CACHE_INFRA_H */ diff --git a/external/unbound/services/cache/rrset.c b/external/unbound/services/cache/rrset.c index 2f6a1b506..7e5732b76 100644 --- a/external/unbound/services/cache/rrset.c +++ b/external/unbound/services/cache/rrset.c @@ -91,7 +91,7 @@ struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r, void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_t hash, rrset_id_t id) + hashvalue_type hash, rrset_id_type id) { struct lruhash* table = slabhash_gettable(&r->table, hash); /* @@ -186,7 +186,7 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, { struct lruhash_entry* e; struct ub_packed_rrset_key* k = ref->key; - hashvalue_t h = k->entry.hash; + hashvalue_type h = k->entry.hash; uint16_t rrset_type = ntohs(k->rk.type); int equal = 0; log_assert(ref->id != 0 && k->id != 0); @@ -303,10 +303,10 @@ void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, struct rrset_ref* ref, size_t count) { - hashvalue_t* h; + hashvalue_type* h; size_t i; - if(count > RR_COUNT_MAX || !(h = (hashvalue_t*)regional_alloc(scratch, - sizeof(hashvalue_t)*count))) { + if(count > RR_COUNT_MAX || !(h = (hashvalue_type*)regional_alloc( + scratch, sizeof(hashvalue_type)*count))) { log_warn("rrset LRU: memory allocation failed"); h = NULL; } else /* store hash values */ diff --git a/external/unbound/services/cache/rrset.h b/external/unbound/services/cache/rrset.h index 98e44a4e5..d5439ef08 100644 --- a/external/unbound/services/cache/rrset.h +++ b/external/unbound/services/cache/rrset.h @@ -102,7 +102,7 @@ struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r, * @param id: used to check that the item is unchanged and not deleted. */ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_t hash, rrset_id_t id); + hashvalue_type hash, rrset_id_type id); /** * Update an rrset in the rrset cache. Stores the information for later use. |