/**
* The rate limiting data structure bucket, this represents one rate of
* packets from a single source.
* Smoothed average rates.
*/
struct rrl_bucket {
/* the source netmask */
uint64_t source;
/* rate, in queries per second, which due to rate=r(t)+r(t-1)/2 is
* equal to double the queries per second */
uint32_t rate;
/* the full hash */
uint32_t hash;
/* counter for queries arrived in this second */
uint32_t counter;
/* timestamp, which time is the time of the counter, the rate is from
* one timestep before that. */
int32_t stamp;
/* flags for the source mask and type */
uint16_t flags;
};
/** return the source netblock of the query, this is the genuine source
* for genuine queries and the target for reflected packets */
static uint64_t rrl_get_source(query_type* query, uint16_t* c2)
{
/* note there is an IPv6 subnet, that maps
* to the same buckets as IPv4 space, but there is a flag in c2
* that makes the hash different */
#ifdef INET6
if( ((struct sockaddr_in*)&query->client_addr)->sin_family == AF_INET) {
*c2 = 0;
return ((struct sockaddr_in*)&query->client_addr)->
sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen));
} else {
uint64_t s;
*c2 = rrl_ip6;
memmove(&s, &((struct sockaddr_in6*)&query->client_addr)->sin6_addr,
sizeof(s));
return s & rrl_ipv6_mask;
}
#else
*c2 = 0;
return query->client_addr.sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen));
#endif
}
const char* rrltype2str(enum rrl_type c)
{
switch(c & 0x0fff) {
case rrl_type_nxdomain: return "nxdomain";
case rrl_type_error: return "error";
case rrl_type_referral: return "referral";
case rrl_type_any: return "any";
case rrl_type_wildcard: return "wildcard";
case rrl_type_nodata: return "nodata";
case rrl_type_dnskey: return "dnskey";
case rrl_type_positive: return "positive";
case rrl_type_rrsig: return "rrsig";
case rrl_type_all: return "all";
}
return "unknown";
}
/* age the bucket because elapsed time steps have gone by */
static void rrl_attenuate_bucket(struct rrl_bucket* b, int32_t elapsed)
{
if(elapsed > 16) {
b->rate = 0;
} else {
/* divide rate /2 for every elapsed time step, because
* the counters in the inbetween steps were 0 */
/* r(t) = 0 + 0/2 + 0/4 + .. + oldrate/2^dt */
b->rate >>= elapsed;
/* we know that elapsed >= 2 */
b->rate += (b->counter>>(elapsed-1));
}
}
/** true if the query used to be blocked by the ratelimit */
static int
used_to_block(uint32_t rate, uint32_t counter, uint32_t lm)
{
return rate >= lm || counter+rate/2 >= lm;
}
/** update the rate in a ratelimit bucket, return actual rate */
uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source,
uint16_t flags, int32_t now, uint32_t lm)
{
struct rrl_bucket* b = &rrl_array[hash % rrl_array_size];
/* check if old, zero or smooth it */
/* circular arith for time */
if(now - b->stamp == 1) {
/* very busy bucket and time just stepped one step */
int oldblock = used_to_block(b->rate, b->counter, lm);
b->rate = b->rate/2 + b->counter;
if(oldblock && b->rate < lm)
rrl_msg(query, "unblock");
b->counter = 1;
b->stamp = now;
} else if(now - b->stamp > 0) {
/* older bucket */
int olderblock = used_to_block(b->rate, b->counter, lm);
rrl_attenuate_bucket(b, now - b->stamp);
if(olderblock && b->rate < lm)
rrl_msg(query, "unblock");
b->counter = 1;
b->stamp = now;
} else if(now != b->stamp) {
/* robust, timestamp from the future */
if(used_to_block(b->rate, b->counter, lm))
rrl_msg(query, "unblock");
b->rate = 0;
b->counter = 1;
b->stamp = now;
} else {
/* bucket is from the current timestep, update counter */
b->counter ++;
/* log what is blocked for operational debugging */
if(b->counter + b->rate/2 == lm && b->rate < lm)
rrl_msg(query, "block");
}
/* return max from current rate and projected next-value for rate */
/* so that if the rate increases suddenly very high, it is
* stopped halfway into the time step */
if(b->counter > b->rate/2)
return b->counter + b->rate/2;
return b->rate;
}
int rrl_process_query(query_type* query)
{
uint64_t source;
uint32_t hash;
/* we can use circular arithmetic here, so int32 works after 2038 */
int32_t now = (int32_t)time(NULL);
uint32_t lm = rrl_ratelimit;
uint16_t flags;
if(rrl_ratelimit == 0 && rrl_whitelist_ratelimit == 0)
return 0;