aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/services/cache
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/services/cache')
-rw-r--r--external/unbound/services/cache/dns.c28
-rw-r--r--external/unbound/services/cache/dns.h16
-rw-r--r--external/unbound/services/cache/infra.c139
-rw-r--r--external/unbound/services/cache/infra.h50
-rw-r--r--external/unbound/services/cache/rrset.c10
-rw-r--r--external/unbound/services/cache/rrset.h2
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.