diff options
Diffstat (limited to 'contrib/unbound/services/cache/infra.c')
| -rw-r--r-- | contrib/unbound/services/cache/infra.c | 395 |
1 files changed, 315 insertions, 80 deletions
diff --git a/contrib/unbound/services/cache/infra.c b/contrib/unbound/services/cache/infra.c index 252e1e288b35..cf999422d002 100644 --- a/contrib/unbound/services/cache/infra.c +++ b/contrib/unbound/services/cache/infra.c @@ -52,14 +52,6 @@ #include "util/config_file.h" #include "iterator/iterator.h" -/** Timeout when only a single probe query per IP is allowed. */ -#define PROBE_MAXRTO 12000 /* in msec */ - -/** number of timeouts for a type when the domain can be blocked ; - * even if another type has completely rtt maxed it, the different type - * can do this number of packets (until those all timeout too) */ -#define TIMEOUT_COUNT_MAX 3 - /** ratelimit value for delegation point */ int infra_dp_ratelimit = 0; @@ -67,6 +59,25 @@ int infra_dp_ratelimit = 0; * in queries per second. */ int infra_ip_ratelimit = 0; +/** ratelimit value for client ip addresses, + * in queries per second. + * For clients with a valid DNS Cookie. */ +int infra_ip_ratelimit_cookie = 0; + +/** Minus 1000 because that is outside of the RTTBAND, so + * blacklisted servers stay blacklisted if this is chosen. + * If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT, + * infra-cache-max-rtt) change it to just above the RTT_BAND. */ +int +still_useful_timeout() +{ + return + USEFUL_SERVER_TOP_TIMEOUT < 1000 || + USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND + ?RTT_BAND + 1 + :USEFUL_SERVER_TOP_TIMEOUT - 1000; +} + size_t infra_sizefunc(void* k, void* ATTR_UNUSED(d)) { @@ -150,7 +161,7 @@ rate_deldatafunc(void* d, void* ATTR_UNUSED(arg)) /** find or create element in domainlimit tree */ static struct domain_limit_data* domain_limit_findcreate( - struct infra_cache* infra, char* name) + struct rbtree_type* domain_limits, char* name) { uint8_t* nm; int labs; @@ -166,8 +177,8 @@ static struct domain_limit_data* domain_limit_findcreate( labs = dname_count_labels(nm); /* can we find it? */ - d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits, - nm, nmlen, labs, LDNS_RR_CLASS_IN); + d = (struct domain_limit_data*)name_tree_find(domain_limits, nm, + nmlen, labs, LDNS_RR_CLASS_IN); if(d) { free(nm); return d; @@ -186,8 +197,8 @@ static struct domain_limit_data* domain_limit_findcreate( d->node.dclass = LDNS_RR_CLASS_IN; d->lim = -1; d->below = -1; - if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen, - labs, LDNS_RR_CLASS_IN)) { + if(!name_tree_insert(domain_limits, &d->node, nm, nmlen, labs, + LDNS_RR_CLASS_IN)) { log_err("duplicate element in domainlimit tree"); free(nm); free(d); @@ -197,19 +208,19 @@ static struct domain_limit_data* domain_limit_findcreate( } /** insert rate limit configuration into lookup tree */ -static int infra_ratelimit_cfg_insert(struct infra_cache* infra, +static int infra_ratelimit_cfg_insert(struct rbtree_type* domain_limits, struct config_file* cfg) { struct config_str2list* p; struct domain_limit_data* d; for(p = cfg->ratelimit_for_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); + d = domain_limit_findcreate(domain_limits, p->str); if(!d) return 0; d->lim = atoi(p->str2); } for(p = cfg->ratelimit_below_domain; p; p = p->next) { - d = domain_limit_findcreate(infra, p->str); + d = domain_limit_findcreate(domain_limits, p->str); if(!d) return 0; d->below = atoi(p->str2); @@ -217,15 +228,117 @@ static int infra_ratelimit_cfg_insert(struct infra_cache* infra, return 1; } -/** setup domain limits tree (0 on failure) */ -static int -setup_domain_limits(struct infra_cache* infra, struct config_file* cfg) +int +setup_domain_limits(struct rbtree_type* domain_limits, struct config_file* cfg) { - name_tree_init(&infra->domain_limits); - if(!infra_ratelimit_cfg_insert(infra, cfg)) { + name_tree_init(domain_limits); + if(!infra_ratelimit_cfg_insert(domain_limits, cfg)) { return 0; } - name_tree_init_parents(&infra->domain_limits); + name_tree_init_parents(domain_limits); + return 1; +} + +/** find or create element in wait limit netblock tree */ +static struct wait_limit_netblock_info* +wait_limit_netblock_findcreate(struct rbtree_type* tree, char* str) +{ + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + struct wait_limit_netblock_info* d; + + if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) { + log_err("cannot parse wait limit netblock '%s'", str); + return 0; + } + + /* can we find it? */ + d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr, + addrlen, net); + if(d) + return d; + + /* create it */ + d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d)); + if(!d) + return NULL; + d->limit = -1; + if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) { + log_err("duplicate element in domainlimit tree"); + free(d); + return NULL; + } + return d; +} + + +/** insert wait limit information into lookup tree */ +static int +infra_wait_limit_netblock_insert(rbtree_type* wait_limits_netblock, + rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg) +{ + struct config_str2list* p; + struct wait_limit_netblock_info* d; + for(p = cfg->wait_limit_netblock; p; p = p->next) { + d = wait_limit_netblock_findcreate(wait_limits_netblock, + p->str); + if(!d) + return 0; + d->limit = atoi(p->str2); + } + for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) { + d = wait_limit_netblock_findcreate(wait_limits_cookie_netblock, + p->str); + if(!d) + return 0; + d->limit = atoi(p->str2); + } + return 1; +} + +/** Add a default wait limit netblock */ +static int +wait_limit_netblock_default(struct rbtree_type* tree, char* str, int limit) +{ + struct wait_limit_netblock_info* d; + d = wait_limit_netblock_findcreate(tree, str); + if(!d) + return 0; + d->limit = limit; + return 1; +} + +int +setup_wait_limits(rbtree_type* wait_limits_netblock, + rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg) +{ + addr_tree_init(wait_limits_netblock); + addr_tree_init(wait_limits_cookie_netblock); + + /* Insert defaults */ + /* The loopback address is separated from the rest of the network. */ + /* wait-limit-netblock: 127.0.0.0/8 -1 */ + if(!wait_limit_netblock_default(wait_limits_netblock, "127.0.0.0/8", + -1)) + return 0; + /* wait-limit-netblock: ::1/128 -1 */ + if(!wait_limit_netblock_default(wait_limits_netblock, "::1/128", -1)) + return 0; + /* wait-limit-cookie-netblock: 127.0.0.0/8 -1 */ + if(!wait_limit_netblock_default(wait_limits_cookie_netblock, + "127.0.0.0/8", -1)) + return 0; + /* wait-limit-cookie-netblock: ::1/128 -1 */ + if(!wait_limit_netblock_default(wait_limits_cookie_netblock, + "::1/128", -1)) + return 0; + + if(!infra_wait_limit_netblock_insert(wait_limits_netblock, + wait_limits_cookie_netblock, cfg)) + return 0; + addr_tree_init_parents(wait_limits_netblock); + addr_tree_init_parents(wait_limits_cookie_netblock); return 1; } @@ -258,11 +371,17 @@ infra_create(struct config_file* cfg) return NULL; } /* insert config data into ratelimits */ - if(!setup_domain_limits(infra, cfg)) { + if(!setup_domain_limits(&infra->domain_limits, cfg)) { + infra_delete(infra); + return NULL; + } + if(!setup_wait_limits(&infra->wait_limits_netblock, + &infra->wait_limits_cookie_netblock, cfg)) { infra_delete(infra); return NULL; } infra_ip_ratelimit = cfg->ip_ratelimit; + infra_ip_ratelimit_cookie = cfg->ip_ratelimit_cookie; infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL); @@ -282,6 +401,29 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) } } +void +domain_limits_free(struct rbtree_type* domain_limits) +{ + if(!domain_limits) + return; + traverse_postorder(domain_limits, domain_limit_free, NULL); +} + +/** delete wait_limit_netblock_info entries */ +static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + free(n); +} + +void +wait_limits_free(struct rbtree_type* wait_limits_tree) +{ + if(!wait_limits_tree) + return; + traverse_postorder(wait_limits_tree, wait_limit_netblock_del, + NULL); +} + void infra_delete(struct infra_cache* infra) { @@ -289,8 +431,10 @@ infra_delete(struct infra_cache* infra) return; slabhash_delete(infra->hosts); slabhash_delete(infra->domain_rates); - traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); + domain_limits_free(&infra->domain_limits); slabhash_delete(infra->client_ip_rates); + wait_limits_free(&infra->wait_limits_netblock); + wait_limits_free(&infra->wait_limits_cookie_netblock); free(infra); } @@ -304,6 +448,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg) infra->infra_keep_probing = cfg->infra_keep_probing; infra_dp_ratelimit = cfg->ratelimit; infra_ip_ratelimit = cfg->ip_ratelimit; + infra_ip_ratelimit_cookie = cfg->ip_ratelimit_cookie; maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ sizeof(struct infra_data)+INFRA_BYTES_NAME); /* divide cachesize by slabs and multiply by slabs, because if the @@ -320,7 +465,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg) /* reapply domain limits */ traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); - if(!setup_domain_limits(infra, cfg)) { + if(!setup_domain_limits(&infra->domain_limits, cfg)) { infra_delete(infra); return NULL; } @@ -562,7 +707,7 @@ infra_update_tcp_works(struct infra_cache* infra, if(data->rtt.rto >= RTT_MAX_TIMEOUT) /* do not disqualify this server altogether, it is better * than nothing */ - data->rtt.rto = RTT_MAX_TIMEOUT-1000; + data->rtt.rto = still_useful_timeout(); lock_rw_unlock(&e->lock); } @@ -702,7 +847,7 @@ infra_get_lame_rtt(struct infra_cache* infra, && infra->infra_keep_probing) { /* single probe, keep probing */ if(*rtt >= USEFUL_SERVER_TOP_TIMEOUT) - *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; + *rtt = still_useful_timeout(); } else if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay && rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) { /* single probe for this domain, and we are not probing */ @@ -710,26 +855,23 @@ infra_get_lame_rtt(struct infra_cache* infra, if(qtype == LDNS_RR_TYPE_A) { if(host->timeout_A >= TIMEOUT_COUNT_MAX) *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; + else *rtt = still_useful_timeout(); } else if(qtype == LDNS_RR_TYPE_AAAA) { if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX) *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; + else *rtt = still_useful_timeout(); } else { if(host->timeout_other >= TIMEOUT_COUNT_MAX) *rtt = USEFUL_SERVER_TOP_TIMEOUT; - else *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; + else *rtt = still_useful_timeout(); } } + /* expired entry */ if(timenow > host->ttl) { - /* expired entry */ /* see if this can be a re-probe of an unresponsive server */ - /* minus 1000 because that is outside of the RTTBAND, so - * blacklisted servers stay blacklisted if this is chosen */ - if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT || - infra->infra_keep_probing) { + if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) { lock_rw_unlock(&e->lock); - *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000; + *rtt = still_useful_timeout(); *lame = 0; *dnsseclame = 0; *reclame = 0; @@ -834,14 +976,13 @@ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, /** find data item in array for ip addresses */ static struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, int wr) + struct sockaddr_storage* addr, socklen_t addrlen, int wr) { struct ip_rate_key key; - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); + hashvalue_type h = hash_addr(addr, addrlen, 0); memset(&key, 0, sizeof(key)); - key.addr = repinfo->addr; - key.addrlen = repinfo->addrlen; + key.addr = *addr; + key.addrlen = addrlen; key.entry.hash = h; return slabhash_lookup(infra->client_ip_rates, h, &key, wr); } @@ -876,10 +1017,10 @@ static void infra_create_ratedata(struct infra_cache* infra, /** create rate data item for ip address */ static void infra_ip_create_ratedata(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow) + struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow, + int mesh_wait) { - hashvalue_type h = hash_addr(&(repinfo->addr), - repinfo->addrlen, 0); + hashvalue_type h = hash_addr(addr, 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) { @@ -887,14 +1028,15 @@ static void infra_ip_create_ratedata(struct infra_cache* infra, free(d); return; /* alloc failure */ } - k->addr = repinfo->addr; - k->addrlen = repinfo->addrlen; + k->addr = *addr; + k->addrlen = 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; + d->mesh_wait = mesh_wait; slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); } @@ -978,15 +1120,16 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, lock_rw_unlock(&entry->lock); if(premax <= lim && max > lim) { - char buf[257], qnm[257], ts[12], cs[12], ip[128]; + char buf[LDNS_MAX_DOMAINLEN], qnm[LDNS_MAX_DOMAINLEN]; + char ts[12], cs[12], ip[128]; dname_str(name, buf); dname_str(qinfo->qname, qnm); sldns_wire2str_type_buf(qinfo->qtype, ts, sizeof(ts)); sldns_wire2str_class_buf(qinfo->qclass, cs, sizeof(cs)); ip[0]=0; if(replylist) { - addr_to_str((struct sockaddr_storage *)&replylist->addr, - replylist->addrlen, ip, sizeof(ip)); + addr_to_str((struct sockaddr_storage *)&replylist->remote_addr, + replylist->remote_addrlen, ip, sizeof(ip)); verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s from %s", buf, lim, qnm, cs, ts, ip); } else { verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s", buf, lim, qnm, cs, ts); @@ -1040,7 +1183,7 @@ int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, max = infra_rate_max(entry->data, timenow, backoff); lock_rw_unlock(&entry->lock); - return (max >= lim); + return (max > lim); } size_t @@ -1053,9 +1196,50 @@ infra_get_mem(struct infra_cache* infra) return s; } +/* Returns 1 if the limit has not been exceeded, 0 otherwise. */ +static int +check_ip_ratelimit(struct sockaddr_storage* addr, socklen_t addrlen, + struct sldns_buffer* buffer, int premax, int max, int has_cookie) +{ + int limit; + + if(has_cookie) limit = infra_ip_ratelimit_cookie; + else limit = infra_ip_ratelimit; + + /* Disabled */ + if(limit == 0) return 1; + + if(premax <= limit && max > limit) { + char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12]; + addr_to_str(addr, addrlen, client_ip, sizeof(client_ip)); + qnm[0]=0; + if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE && + LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) { + (void)sldns_wire2str_rrquestion_buf( + sldns_buffer_at(buffer, LDNS_HEADER_SIZE), + sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE, + qnm, sizeof(qnm)); + if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n') + qnm[strlen(qnm)-1] = 0; /*remove newline*/ + if(strchr(qnm, '\t')) + *strchr(qnm, '\t') = ' '; + if(strchr(qnm, '\t')) + *strchr(qnm, '\t') = ' '; + verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s %s", + client_ip, limit, + has_cookie?"(cookie)":"", qnm); + } else { + verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s (no query name)", + client_ip, limit, + has_cookie?"(cookie)":""); + } + } + return (max <= limit); +} + int infra_ip_ratelimit_inc(struct infra_cache* infra, - struct comm_reply* repinfo, time_t timenow, int backoff, - struct sldns_buffer* buffer) + struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow, + int has_cookie, int backoff, struct sldns_buffer* buffer) { int max; struct lruhash_entry* entry; @@ -1065,42 +1249,93 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra, return 1; } /* find or insert ratedata */ - entry = infra_find_ip_ratedata(infra, repinfo, 1); + entry = infra_find_ip_ratedata(infra, addr, addrlen, 1); if(entry) { int premax = infra_rate_max(entry->data, timenow, backoff); int* cur = infra_rate_give_second(entry->data, timenow); (*cur)++; max = infra_rate_max(entry->data, timenow, backoff); lock_rw_unlock(&entry->lock); - - if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) { - char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12]; - addr_to_str((struct sockaddr_storage *)&repinfo->addr, - repinfo->addrlen, client_ip, sizeof(client_ip)); - qnm[0]=0; - if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE && - LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) { - (void)sldns_wire2str_rrquestion_buf( - sldns_buffer_at(buffer, LDNS_HEADER_SIZE), - sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE, - qnm, sizeof(qnm)); - if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n') - qnm[strlen(qnm)-1] = 0; /*remove newline*/ - if(strchr(qnm, '\t')) - *strchr(qnm, '\t') = ' '; - if(strchr(qnm, '\t')) - *strchr(qnm, '\t') = ' '; - verbose(VERB_OPS, "ip_ratelimit exceeded %s %d %s", - client_ip, infra_ip_ratelimit, qnm); - } else { - verbose(VERB_OPS, "ip_ratelimit exceeded %s %d (no query name)", - client_ip, infra_ip_ratelimit); - } - } - return (max <= infra_ip_ratelimit); + return check_ip_ratelimit(addr, addrlen, buffer, premax, max, + has_cookie); } /* create */ - infra_ip_create_ratedata(infra, repinfo, timenow); + infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0); return 1; } + +int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, + int cookie_valid, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return 1; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 0); + if(entry) { + rbtree_type* tree; + struct wait_limit_netblock_info* w; + struct rate_data* d = (struct rate_data*)entry->data; + int mesh_wait = d->mesh_wait; + lock_rw_unlock(&entry->lock); + + /* have the wait amount, check how much is allowed */ + if(cookie_valid) + tree = &infra->wait_limits_cookie_netblock; + else tree = &infra->wait_limits_netblock; + w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree, + &rep->client_addr, rep->client_addrlen); + if(w) { + if(w->limit != -1 && mesh_wait > w->limit) + return 0; + } else { + /* if there is no IP netblock specific information, + * use the configured value. */ + if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie: + cfg->wait_limit)) + return 0; + } + } + return 1; +} + +void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, + time_t timenow, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + /* Find it */ + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + d->mesh_wait++; + lock_rw_unlock(&entry->lock); + return; + } + + /* Create it */ + infra_ip_create_ratedata(infra, &rep->client_addr, + rep->client_addrlen, timenow, 1); +} + +void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, + struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + if(d->mesh_wait > 0) + d->mesh_wait--; + lock_rw_unlock(&entry->lock); + } +} |
