1d83a80eeSchristos
2d83a80eeSchristos /* rrl.c - Response Rate Limiting for NSD.
3d83a80eeSchristos * By W.C.A. Wijngaards
4d83a80eeSchristos * Copyright 2012, NLnet Labs.
5d83a80eeSchristos * BSD, see LICENSE.
6d83a80eeSchristos */
7d83a80eeSchristos #include "config.h"
8d83a80eeSchristos #include <errno.h>
9d83a80eeSchristos #include "rrl.h"
10d83a80eeSchristos #include "util.h"
11d83a80eeSchristos #include "lookup3.h"
12d83a80eeSchristos #include "options.h"
13d83a80eeSchristos
14d83a80eeSchristos #ifdef RATELIMIT
15d83a80eeSchristos
16d83a80eeSchristos #ifdef HAVE_MMAP
17d83a80eeSchristos #include <sys/mman.h>
18d83a80eeSchristos #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
19d83a80eeSchristos #define MAP_ANONYMOUS MAP_ANON
20d83a80eeSchristos #endif
21d83a80eeSchristos #endif /* HAVE_MMAP */
22d83a80eeSchristos
23d83a80eeSchristos
24d83a80eeSchristos /**
25d83a80eeSchristos * The rate limiting data structure bucket, this represents one rate of
26d83a80eeSchristos * packets from a single source.
27d83a80eeSchristos * Smoothed average rates.
28d83a80eeSchristos */
29d83a80eeSchristos struct rrl_bucket {
30d83a80eeSchristos /* the source netmask */
31d83a80eeSchristos uint64_t source;
32d83a80eeSchristos /* rate, in queries per second, which due to rate=r(t)+r(t-1)/2 is
33d83a80eeSchristos * equal to double the queries per second */
34d83a80eeSchristos uint32_t rate;
35d83a80eeSchristos /* the full hash */
36d83a80eeSchristos uint32_t hash;
37d83a80eeSchristos /* counter for queries arrived in this second */
38d83a80eeSchristos uint32_t counter;
39d83a80eeSchristos /* timestamp, which time is the time of the counter, the rate is from
40d83a80eeSchristos * one timestep before that. */
41d83a80eeSchristos int32_t stamp;
42d83a80eeSchristos /* flags for the source mask and type */
43d83a80eeSchristos uint16_t flags;
44d83a80eeSchristos };
45d83a80eeSchristos
46d83a80eeSchristos /* the (global) array of RRL buckets */
47d83a80eeSchristos static struct rrl_bucket* rrl_array = NULL;
48d83a80eeSchristos static size_t rrl_array_size = RRL_BUCKETS;
49d83a80eeSchristos static uint32_t rrl_ratelimit = RRL_LIMIT; /* 2x qps */
50d83a80eeSchristos static uint8_t rrl_slip_ratio = RRL_SLIP;
51d83a80eeSchristos static uint8_t rrl_ipv4_prefixlen = RRL_IPV4_PREFIX_LENGTH;
52d83a80eeSchristos static uint8_t rrl_ipv6_prefixlen = RRL_IPV6_PREFIX_LENGTH;
53d83a80eeSchristos static uint64_t rrl_ipv6_mask; /* max prefixlen 64 */
54d83a80eeSchristos static uint32_t rrl_whitelist_ratelimit = RRL_WLIST_LIMIT; /* 2x qps */
55d83a80eeSchristos
56d83a80eeSchristos /* the array of mmaps for the children (saved between reloads) */
57d83a80eeSchristos static void** rrl_maps = NULL;
58d83a80eeSchristos static size_t rrl_maps_num = 0;
59d83a80eeSchristos
rrl_mmap_init(int numch,size_t numbuck,size_t lm,size_t wlm,size_t sm,size_t plf,size_t pls)60d83a80eeSchristos void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm,
61d83a80eeSchristos size_t plf, size_t pls)
62d83a80eeSchristos {
63d83a80eeSchristos #ifdef HAVE_MMAP
64d83a80eeSchristos size_t i;
65d83a80eeSchristos #endif
66d83a80eeSchristos if(numbuck != 0)
67d83a80eeSchristos rrl_array_size = numbuck;
68d83a80eeSchristos rrl_ratelimit = lm*2;
69d83a80eeSchristos rrl_slip_ratio = sm;
70d83a80eeSchristos rrl_ipv4_prefixlen = plf;
71d83a80eeSchristos rrl_ipv6_prefixlen = pls;
72d83a80eeSchristos if (pls <= 32) {
73d83a80eeSchristos rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (32-pls))) << 32;
74d83a80eeSchristos } else {
75d83a80eeSchristos rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (64-pls))) |
76d83a80eeSchristos (((uint64_t)0xffffffff)<<32);
77d83a80eeSchristos }
78d83a80eeSchristos rrl_whitelist_ratelimit = wlm*2;
79d83a80eeSchristos #ifdef HAVE_MMAP
80d83a80eeSchristos /* allocate the ratelimit hashtable in a memory map so it is
81d83a80eeSchristos * preserved across reforks (every child its own table) */
82d83a80eeSchristos rrl_maps_num = (size_t)numch;
83d83a80eeSchristos rrl_maps = (void**)xmallocarray(rrl_maps_num, sizeof(void*));
84d83a80eeSchristos for(i=0; i<rrl_maps_num; i++) {
85d83a80eeSchristos rrl_maps[i] = mmap(NULL,
86d83a80eeSchristos sizeof(struct rrl_bucket)*rrl_array_size,
87d83a80eeSchristos PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
88d83a80eeSchristos if(rrl_maps[i] == MAP_FAILED) {
89d83a80eeSchristos log_msg(LOG_ERR, "rrl: mmap failed: %s",
90d83a80eeSchristos strerror(errno));
91d83a80eeSchristos exit(1);
92d83a80eeSchristos }
93d83a80eeSchristos memset(rrl_maps[i], 0,
94d83a80eeSchristos sizeof(struct rrl_bucket)*rrl_array_size);
95d83a80eeSchristos }
96d83a80eeSchristos #else
97d83a80eeSchristos (void)numch;
98d83a80eeSchristos rrl_maps_num = 0;
99d83a80eeSchristos rrl_maps = NULL;
100d83a80eeSchristos #endif
101d83a80eeSchristos }
102d83a80eeSchristos
rrl_mmap_deinit(void)103da4c7d9dSchristos void rrl_mmap_deinit(void)
104da4c7d9dSchristos {
105da4c7d9dSchristos #ifdef HAVE_MMAP
106da4c7d9dSchristos size_t i;
107da4c7d9dSchristos for(i=0; i<rrl_maps_num; i++) {
108da4c7d9dSchristos munmap(rrl_maps[i], sizeof(struct rrl_bucket)*rrl_array_size);
109da4c7d9dSchristos rrl_maps[i] = NULL;
110da4c7d9dSchristos }
111da4c7d9dSchristos free(rrl_maps);
112da4c7d9dSchristos rrl_maps = NULL;
113da4c7d9dSchristos #endif
114da4c7d9dSchristos }
115da4c7d9dSchristos
rrl_mmap_deinit_keep_mmap(void)116da4c7d9dSchristos void rrl_mmap_deinit_keep_mmap(void)
117da4c7d9dSchristos {
118da4c7d9dSchristos #ifdef HAVE_MMAP
119da4c7d9dSchristos free(rrl_maps);
120da4c7d9dSchristos rrl_maps = NULL;
121da4c7d9dSchristos #endif
122da4c7d9dSchristos }
123da4c7d9dSchristos
rrl_set_limit(size_t lm,size_t wlm,size_t sm)124d83a80eeSchristos void rrl_set_limit(size_t lm, size_t wlm, size_t sm)
125d83a80eeSchristos {
126d83a80eeSchristos rrl_ratelimit = lm*2;
127d83a80eeSchristos rrl_whitelist_ratelimit = wlm*2;
128d83a80eeSchristos rrl_slip_ratio = sm;
129d83a80eeSchristos }
130d83a80eeSchristos
rrl_init(size_t ch)131d83a80eeSchristos void rrl_init(size_t ch)
132d83a80eeSchristos {
133d83a80eeSchristos if(!rrl_maps || ch >= rrl_maps_num)
134d83a80eeSchristos rrl_array = xalloc_array_zero(sizeof(struct rrl_bucket),
135d83a80eeSchristos rrl_array_size);
136d83a80eeSchristos #ifdef HAVE_MMAP
137d83a80eeSchristos else rrl_array = (struct rrl_bucket*)rrl_maps[ch];
138d83a80eeSchristos #endif
139d83a80eeSchristos }
140d83a80eeSchristos
rrl_deinit(size_t ch)141da4c7d9dSchristos void rrl_deinit(size_t ch)
142da4c7d9dSchristos {
143da4c7d9dSchristos if(!rrl_maps || ch >= rrl_maps_num)
144da4c7d9dSchristos free(rrl_array);
145da4c7d9dSchristos rrl_array = NULL;
146da4c7d9dSchristos }
147da4c7d9dSchristos
148d83a80eeSchristos /** return the source netblock of the query, this is the genuine source
149d83a80eeSchristos * for genuine queries and the target for reflected packets */
rrl_get_source(query_type * query,uint16_t * c2)150d83a80eeSchristos static uint64_t rrl_get_source(query_type* query, uint16_t* c2)
151d83a80eeSchristos {
152d83a80eeSchristos /* note there is an IPv6 subnet, that maps
153d83a80eeSchristos * to the same buckets as IPv4 space, but there is a flag in c2
154d83a80eeSchristos * that makes the hash different */
155d83a80eeSchristos #ifdef INET6
156*811a4a01Schristos if( ((struct sockaddr_in*)&query->client_addr)->sin_family == AF_INET) {
157d83a80eeSchristos *c2 = 0;
158*811a4a01Schristos return ((struct sockaddr_in*)&query->client_addr)->
159d83a80eeSchristos sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen));
160d83a80eeSchristos } else {
161d83a80eeSchristos uint64_t s;
162d83a80eeSchristos *c2 = rrl_ip6;
163*811a4a01Schristos memmove(&s, &((struct sockaddr_in6*)&query->client_addr)->sin6_addr,
164d83a80eeSchristos sizeof(s));
165d83a80eeSchristos return s & rrl_ipv6_mask;
166d83a80eeSchristos }
167d83a80eeSchristos #else
168d83a80eeSchristos *c2 = 0;
169*811a4a01Schristos return query->client_addr.sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen));
170d83a80eeSchristos #endif
171d83a80eeSchristos }
172d83a80eeSchristos
173d83a80eeSchristos /** debug source to string */
rrlsource2str(uint64_t s,uint16_t c2)174d83a80eeSchristos static const char* rrlsource2str(uint64_t s, uint16_t c2)
175d83a80eeSchristos {
176d83a80eeSchristos static char buf[64];
177d83a80eeSchristos struct in_addr a4;
178d83a80eeSchristos #ifdef INET6
179d83a80eeSchristos if(c2) {
180d83a80eeSchristos /* IPv6 */
181d83a80eeSchristos struct in6_addr a6;
182d83a80eeSchristos memset(&a6, 0, sizeof(a6));
183d83a80eeSchristos memmove(&a6, &s, sizeof(s));
184d83a80eeSchristos if(!inet_ntop(AF_INET6, &a6, buf, sizeof(buf)))
185d83a80eeSchristos strlcpy(buf, "[ip6 ntop failed]", sizeof(buf));
186d83a80eeSchristos else {
1873fb62404Schristos static char prefix[5];
188d83a80eeSchristos snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv6_prefixlen);
189d83a80eeSchristos strlcat(buf, &prefix[0], sizeof(buf));
190d83a80eeSchristos }
191d83a80eeSchristos return buf;
192d83a80eeSchristos }
193d83a80eeSchristos #else
194d83a80eeSchristos (void)c2;
195d83a80eeSchristos #endif
196d83a80eeSchristos /* ipv4 */
197d83a80eeSchristos a4.s_addr = (uint32_t)s;
198d83a80eeSchristos if(!inet_ntop(AF_INET, &a4, buf, sizeof(buf)))
199d83a80eeSchristos strlcpy(buf, "[ip4 ntop failed]", sizeof(buf));
200d83a80eeSchristos else {
2013fb62404Schristos static char prefix[5];
202d83a80eeSchristos snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv4_prefixlen);
203d83a80eeSchristos strlcat(buf, &prefix[0], sizeof(buf));
204d83a80eeSchristos }
205d83a80eeSchristos return buf;
206d83a80eeSchristos }
207d83a80eeSchristos
rrlstr2type(const char * s)208d83a80eeSchristos enum rrl_type rrlstr2type(const char* s)
209d83a80eeSchristos {
210d83a80eeSchristos if(strcmp(s, "nxdomain")==0) return rrl_type_nxdomain;
211d83a80eeSchristos else if(strcmp(s, "error")==0) return rrl_type_error;
212d83a80eeSchristos else if(strcmp(s, "referral")==0) return rrl_type_referral;
213d83a80eeSchristos else if(strcmp(s, "any")==0) return rrl_type_any;
214d83a80eeSchristos else if(strcmp(s, "wildcard")==0) return rrl_type_wildcard;
215d83a80eeSchristos else if(strcmp(s, "nodata")==0) return rrl_type_nodata;
216d83a80eeSchristos else if(strcmp(s, "dnskey")==0) return rrl_type_dnskey;
217d83a80eeSchristos else if(strcmp(s, "positive")==0) return rrl_type_positive;
218d83a80eeSchristos else if(strcmp(s, "rrsig")==0) return rrl_type_rrsig;
219d83a80eeSchristos else if(strcmp(s, "all")==0) return rrl_type_all;
220d83a80eeSchristos return 0; /* unknown */
221d83a80eeSchristos }
222d83a80eeSchristos
rrltype2str(enum rrl_type c)223d83a80eeSchristos const char* rrltype2str(enum rrl_type c)
224d83a80eeSchristos {
225d83a80eeSchristos switch(c & 0x0fff) {
226d83a80eeSchristos case rrl_type_nxdomain: return "nxdomain";
227d83a80eeSchristos case rrl_type_error: return "error";
228d83a80eeSchristos case rrl_type_referral: return "referral";
229d83a80eeSchristos case rrl_type_any: return "any";
230d83a80eeSchristos case rrl_type_wildcard: return "wildcard";
231d83a80eeSchristos case rrl_type_nodata: return "nodata";
232d83a80eeSchristos case rrl_type_dnskey: return "dnskey";
233d83a80eeSchristos case rrl_type_positive: return "positive";
234d83a80eeSchristos case rrl_type_rrsig: return "rrsig";
235d83a80eeSchristos case rrl_type_all: return "all";
236d83a80eeSchristos }
237d83a80eeSchristos return "unknown";
238d83a80eeSchristos }
239d83a80eeSchristos
240d83a80eeSchristos /** classify the query in a number of different types, each has separate
241d83a80eeSchristos * ratelimiting, so that positive queries are not impeded by others */
rrl_classify(query_type * query,const uint8_t ** d,size_t * d_len)242d83a80eeSchristos static uint16_t rrl_classify(query_type* query, const uint8_t** d,
243d83a80eeSchristos size_t* d_len)
244d83a80eeSchristos {
245d83a80eeSchristos if(RCODE(query->packet) == RCODE_NXDOMAIN) {
246d83a80eeSchristos if(query->zone && query->zone->apex) {
247d83a80eeSchristos *d = dname_name(domain_dname(query->zone->apex));
248d83a80eeSchristos *d_len = domain_dname(query->zone->apex)->name_size;
249d83a80eeSchristos }
250d83a80eeSchristos return rrl_type_nxdomain;
251d83a80eeSchristos }
252d83a80eeSchristos if(RCODE(query->packet) != RCODE_OK) {
253d83a80eeSchristos if(query->zone && query->zone->apex) {
254d83a80eeSchristos *d = dname_name(domain_dname(query->zone->apex));
255d83a80eeSchristos *d_len = domain_dname(query->zone->apex)->name_size;
256d83a80eeSchristos }
257d83a80eeSchristos return rrl_type_error;
258d83a80eeSchristos }
259d83a80eeSchristos if(query->delegation_domain) {
260d83a80eeSchristos *d = dname_name(domain_dname(query->delegation_domain));
261d83a80eeSchristos *d_len = domain_dname(query->delegation_domain)->name_size;
262d83a80eeSchristos return rrl_type_referral;
263d83a80eeSchristos }
264d83a80eeSchristos if(query->qtype == TYPE_ANY) {
265d83a80eeSchristos if(query->qname) {
266d83a80eeSchristos *d = dname_name(query->qname);
267d83a80eeSchristos *d_len = query->qname->name_size;
268d83a80eeSchristos }
269d83a80eeSchristos return rrl_type_any;
270d83a80eeSchristos }
271d83a80eeSchristos if(query->qtype == TYPE_RRSIG) {
272d83a80eeSchristos if(query->qname) {
273d83a80eeSchristos *d = dname_name(query->qname);
274d83a80eeSchristos *d_len = query->qname->name_size;
275d83a80eeSchristos }
276d83a80eeSchristos return rrl_type_rrsig;
277d83a80eeSchristos }
278d83a80eeSchristos if(query->wildcard_domain) {
279d83a80eeSchristos *d = dname_name(domain_dname(query->wildcard_domain));
280d83a80eeSchristos *d_len = domain_dname(query->wildcard_domain)->name_size;
281d83a80eeSchristos return rrl_type_wildcard;
282d83a80eeSchristos }
283d83a80eeSchristos if(ANCOUNT(query->packet) == 0) {
284d83a80eeSchristos if(query->zone && query->zone->apex) {
285d83a80eeSchristos *d = dname_name(domain_dname(query->zone->apex));
286d83a80eeSchristos *d_len = domain_dname(query->zone->apex)->name_size;
287d83a80eeSchristos }
288d83a80eeSchristos return rrl_type_nodata;
289d83a80eeSchristos }
290d83a80eeSchristos if(query->qtype == TYPE_DNSKEY) {
291d83a80eeSchristos if(query->qname) {
292d83a80eeSchristos *d = dname_name(query->qname);
293d83a80eeSchristos *d_len = query->qname->name_size;
294d83a80eeSchristos }
295d83a80eeSchristos return rrl_type_dnskey;
296d83a80eeSchristos }
297d83a80eeSchristos /* positive */
298d83a80eeSchristos if(query->qname) {
299d83a80eeSchristos *d = dname_name(query->qname);
300d83a80eeSchristos *d_len = query->qname->name_size;
301d83a80eeSchristos }
302d83a80eeSchristos return rrl_type_positive;
303d83a80eeSchristos }
304d83a80eeSchristos
305d83a80eeSchristos /** Examine the query and return hash and source of netblock. */
examine_query(query_type * query,uint32_t * hash,uint64_t * source,uint16_t * flags,uint32_t * lm)306d83a80eeSchristos static void examine_query(query_type* query, uint32_t* hash, uint64_t* source,
307d83a80eeSchristos uint16_t* flags, uint32_t* lm)
308d83a80eeSchristos {
309d83a80eeSchristos /* compile a binary string representing the query */
310d83a80eeSchristos uint16_t c, c2;
311d83a80eeSchristos /* size with 16 bytes to spare */
312d83a80eeSchristos uint8_t buf[MAXDOMAINLEN + sizeof(*source) + sizeof(c) + 16];
313d83a80eeSchristos const uint8_t* dname = NULL; size_t dname_len = 0;
314d83a80eeSchristos uint32_t r = 0x267fcd16;
315d83a80eeSchristos
316d83a80eeSchristos *source = rrl_get_source(query, &c2);
317d83a80eeSchristos c = rrl_classify(query, &dname, &dname_len);
318d83a80eeSchristos if(query->zone && query->zone->opts &&
319d83a80eeSchristos (query->zone->opts->pattern->rrl_whitelist & c))
320d83a80eeSchristos *lm = rrl_whitelist_ratelimit;
321d83a80eeSchristos if(*lm == 0) return;
322d83a80eeSchristos c |= c2;
323d83a80eeSchristos *flags = c;
324d83a80eeSchristos memmove(buf, source, sizeof(*source));
325d83a80eeSchristos memmove(buf+sizeof(*source), &c, sizeof(c));
326d83a80eeSchristos
327d83a80eeSchristos DEBUG(DEBUG_QUERY, 1, (LOG_INFO, "rrl_examine type %s name %s", rrltype2str(c), dname?wiredname2str(dname):"NULL"));
328d83a80eeSchristos
329d83a80eeSchristos /* and hash it */
330d83a80eeSchristos if(dname && dname_len <= MAXDOMAINLEN) {
331d83a80eeSchristos memmove(buf+sizeof(*source)+sizeof(c), dname, dname_len);
332d83a80eeSchristos *hash = hashlittle(buf, sizeof(*source)+sizeof(c)+dname_len, r);
333d83a80eeSchristos } else
334d83a80eeSchristos *hash = hashlittle(buf, sizeof(*source)+sizeof(c), r);
335d83a80eeSchristos }
336d83a80eeSchristos
337d83a80eeSchristos /* age the bucket because elapsed time steps have gone by */
rrl_attenuate_bucket(struct rrl_bucket * b,int32_t elapsed)338d83a80eeSchristos static void rrl_attenuate_bucket(struct rrl_bucket* b, int32_t elapsed)
339d83a80eeSchristos {
340d83a80eeSchristos if(elapsed > 16) {
341d83a80eeSchristos b->rate = 0;
342d83a80eeSchristos } else {
343d83a80eeSchristos /* divide rate /2 for every elapsed time step, because
344d83a80eeSchristos * the counters in the inbetween steps were 0 */
345d83a80eeSchristos /* r(t) = 0 + 0/2 + 0/4 + .. + oldrate/2^dt */
346d83a80eeSchristos b->rate >>= elapsed;
347d83a80eeSchristos /* we know that elapsed >= 2 */
348d83a80eeSchristos b->rate += (b->counter>>(elapsed-1));
349d83a80eeSchristos }
350d83a80eeSchristos }
351d83a80eeSchristos
352d83a80eeSchristos /** log a message about ratelimits */
353d83a80eeSchristos static void
rrl_msg(query_type * query,const char * str)354d83a80eeSchristos rrl_msg(query_type* query, const char* str)
355d83a80eeSchristos {
356d83a80eeSchristos uint16_t c, c2, wl = 0;
357d83a80eeSchristos const uint8_t* d = NULL;
358d83a80eeSchristos size_t d_len;
359d83a80eeSchristos uint64_t s;
360d83a80eeSchristos char address[128];
361d83a80eeSchristos if(verbosity < 1) return;
362*811a4a01Schristos addr2str(&query->client_addr, address, sizeof(address));
363d83a80eeSchristos s = rrl_get_source(query, &c2);
364d83a80eeSchristos c = rrl_classify(query, &d, &d_len) | c2;
365d83a80eeSchristos if(query->zone && query->zone->opts &&
366d83a80eeSchristos (query->zone->opts->pattern->rrl_whitelist & c))
367d83a80eeSchristos wl = 1;
368d83a80eeSchristos log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s query %s %s",
369d83a80eeSchristos str, d?wiredname2str(d):"", rrltype2str(c),
370d83a80eeSchristos wl?"(whitelisted)":"", rrlsource2str(s, c2),
371d83a80eeSchristos address, rrtype_to_string(query->qtype));
372d83a80eeSchristos }
373d83a80eeSchristos
374d83a80eeSchristos /** true if the query used to be blocked by the ratelimit */
375d83a80eeSchristos static int
used_to_block(uint32_t rate,uint32_t counter,uint32_t lm)376d83a80eeSchristos used_to_block(uint32_t rate, uint32_t counter, uint32_t lm)
377d83a80eeSchristos {
378d83a80eeSchristos return rate >= lm || counter+rate/2 >= lm;
379d83a80eeSchristos }
380d83a80eeSchristos
381d83a80eeSchristos /** update the rate in a ratelimit bucket, return actual rate */
rrl_update(query_type * query,uint32_t hash,uint64_t source,uint16_t flags,int32_t now,uint32_t lm)382d83a80eeSchristos uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source,
383d83a80eeSchristos uint16_t flags, int32_t now, uint32_t lm)
384d83a80eeSchristos {
385d83a80eeSchristos struct rrl_bucket* b = &rrl_array[hash % rrl_array_size];
386d83a80eeSchristos
387d83a80eeSchristos DEBUG(DEBUG_QUERY, 1, (LOG_INFO, "source %llx hash %x oldrate %d oldcount %d stamp %d",
388d83a80eeSchristos (long long unsigned)source, hash, b->rate, b->counter, b->stamp));
389d83a80eeSchristos
390d83a80eeSchristos /* check if different source */
391d83a80eeSchristos if(b->source != source || b->flags != flags || b->hash != hash) {
392d83a80eeSchristos /* initialise */
393d83a80eeSchristos /* potentially the wrong limit here, used lower nonwhitelim */
394d83a80eeSchristos if(verbosity >= 1 &&
395d83a80eeSchristos used_to_block(b->rate, b->counter, rrl_ratelimit)) {
396d83a80eeSchristos char address[128];
397*811a4a01Schristos addr2str(&query->client_addr, address, sizeof(address));
398d83a80eeSchristos log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s query %s %s (%s collision)",
399d83a80eeSchristos rrltype2str(b->flags),
400d83a80eeSchristos rrlsource2str(b->source, b->flags),
401d83a80eeSchristos address, rrtype_to_string(query->qtype),
402d83a80eeSchristos (b->hash!=hash?"bucket":"hash"));
403d83a80eeSchristos }
404d83a80eeSchristos b->hash = hash;
405d83a80eeSchristos b->source = source;
406d83a80eeSchristos b->flags = flags;
407d83a80eeSchristos b->counter = 1;
408d83a80eeSchristos b->rate = 0;
409d83a80eeSchristos b->stamp = now;
410d83a80eeSchristos return 1;
411d83a80eeSchristos }
412d83a80eeSchristos /* this is the same source */
413d83a80eeSchristos
414d83a80eeSchristos /* check if old, zero or smooth it */
415d83a80eeSchristos /* circular arith for time */
416d83a80eeSchristos if(now - b->stamp == 1) {
417d83a80eeSchristos /* very busy bucket and time just stepped one step */
418d83a80eeSchristos int oldblock = used_to_block(b->rate, b->counter, lm);
419d83a80eeSchristos b->rate = b->rate/2 + b->counter;
420d83a80eeSchristos if(oldblock && b->rate < lm)
421d83a80eeSchristos rrl_msg(query, "unblock");
422d83a80eeSchristos b->counter = 1;
423d83a80eeSchristos b->stamp = now;
424d83a80eeSchristos } else if(now - b->stamp > 0) {
425d83a80eeSchristos /* older bucket */
426d83a80eeSchristos int olderblock = used_to_block(b->rate, b->counter, lm);
427d83a80eeSchristos rrl_attenuate_bucket(b, now - b->stamp);
428d83a80eeSchristos if(olderblock && b->rate < lm)
429d83a80eeSchristos rrl_msg(query, "unblock");
430d83a80eeSchristos b->counter = 1;
431d83a80eeSchristos b->stamp = now;
432d83a80eeSchristos } else if(now != b->stamp) {
433d83a80eeSchristos /* robust, timestamp from the future */
434d83a80eeSchristos if(used_to_block(b->rate, b->counter, lm))
435d83a80eeSchristos rrl_msg(query, "unblock");
436d83a80eeSchristos b->rate = 0;
437d83a80eeSchristos b->counter = 1;
438d83a80eeSchristos b->stamp = now;
439d83a80eeSchristos } else {
440d83a80eeSchristos /* bucket is from the current timestep, update counter */
441d83a80eeSchristos b->counter ++;
442d83a80eeSchristos
443d83a80eeSchristos /* log what is blocked for operational debugging */
444d83a80eeSchristos if(b->counter + b->rate/2 == lm && b->rate < lm)
445d83a80eeSchristos rrl_msg(query, "block");
446d83a80eeSchristos }
447d83a80eeSchristos
448d83a80eeSchristos /* return max from current rate and projected next-value for rate */
449d83a80eeSchristos /* so that if the rate increases suddenly very high, it is
450d83a80eeSchristos * stopped halfway into the time step */
451d83a80eeSchristos if(b->counter > b->rate/2)
452d83a80eeSchristos return b->counter + b->rate/2;
453d83a80eeSchristos return b->rate;
454d83a80eeSchristos }
455d83a80eeSchristos
rrl_process_query(query_type * query)456d83a80eeSchristos int rrl_process_query(query_type* query)
457d83a80eeSchristos {
458d83a80eeSchristos uint64_t source;
459d83a80eeSchristos uint32_t hash;
460d83a80eeSchristos /* we can use circular arithmetic here, so int32 works after 2038 */
461d83a80eeSchristos int32_t now = (int32_t)time(NULL);
462d83a80eeSchristos uint32_t lm = rrl_ratelimit;
463d83a80eeSchristos uint16_t flags;
464d83a80eeSchristos if(rrl_ratelimit == 0 && rrl_whitelist_ratelimit == 0)
465d83a80eeSchristos return 0;
466d83a80eeSchristos
467d83a80eeSchristos /* examine query */
468d83a80eeSchristos examine_query(query, &hash, &source, &flags, &lm);
469d83a80eeSchristos
470d83a80eeSchristos if(lm == 0)
471d83a80eeSchristos return 0; /* no limit for this */
472d83a80eeSchristos
473d83a80eeSchristos /* update rate */
474d83a80eeSchristos return (rrl_update(query, hash, source, flags, now, lm) >= lm);
475d83a80eeSchristos }
476d83a80eeSchristos
rrl_slip(query_type * query)477d83a80eeSchristos query_state_type rrl_slip(query_type* query)
478d83a80eeSchristos {
479d83a80eeSchristos /* discard number the packets, randomly */
480d83a80eeSchristos #ifdef HAVE_ARC4RANDOM_UNIFORM
481d83a80eeSchristos if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((arc4random_uniform(rrl_slip_ratio)) == 0))) {
482d83a80eeSchristos #elif HAVE_ARC4RANDOM
483d83a80eeSchristos if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((arc4random() % rrl_slip_ratio) == 0))) {
484d83a80eeSchristos #else
485d83a80eeSchristos if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((random() % rrl_slip_ratio) == 0))) {
486d83a80eeSchristos #endif
487d83a80eeSchristos /* set TC on the rest */
488d83a80eeSchristos TC_SET(query->packet);
489d83a80eeSchristos ANCOUNT_SET(query->packet, 0);
490d83a80eeSchristos NSCOUNT_SET(query->packet, 0);
491d83a80eeSchristos ARCOUNT_SET(query->packet, 0);
492d83a80eeSchristos if(query->qname)
493d83a80eeSchristos /* header, type, class, qname */
494d83a80eeSchristos buffer_set_position(query->packet,
495d83a80eeSchristos QHEADERSZ+4+query->qname->name_size);
496d83a80eeSchristos else buffer_set_position(query->packet, QHEADERSZ);
497d83a80eeSchristos return QUERY_PROCESSED;
498d83a80eeSchristos }
499d83a80eeSchristos return QUERY_DISCARDED;
500d83a80eeSchristos }
501d83a80eeSchristos
502d83a80eeSchristos #endif /* RATELIMIT */
503