X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=xdp.c;h=ad6ab82db97feedc53f88f9f9f3f067d65bcbb7f;hb=80e92801d22e22493f9f0505a8ed06e6ca95716d;hp=95ab7df9743b85e16d701f79463d989c69b0832c;hpb=04df12ec0dbd79134619a437e88c43e6e256b8d5;p=flowspec-xdp diff --git a/xdp.c b/xdp.c index 95ab7df..ad6ab82 100644 --- a/xdp.c +++ b/xdp.c @@ -177,11 +177,23 @@ struct { } \ } +// Rate limits are done in a static-sized leaky bucket with a decimal counter +// Bucket size is always exactly (1 << RATE_BUCKET_INTEGER_BITS) +#define RATE_BUCKET_DECIMAL_BITS 8 +#define RATE_BUCKET_INTEGER_BITS 4 + +#define RATE_BUCKET_BITS (RATE_BUCKET_DECIMAL_BITS + RATE_BUCKET_INTEGER_BITS) +#define RATE_TIME_MASK ((1ULL << (64 - RATE_BUCKET_BITS)) - 1) + +// Time going backwards 10ms+ or forward 32sec+ implies we should consider it +// an overflow, or at least stale enough that we should reset the entry. +#define RATE_MIN_TIME_OFFSET -10000000LL +#define RATE_MAX_TIME_OFFSET 32000000000LL + #ifdef RATE_CNT struct ratelimit { struct bpf_spin_lock lock; - int64_t sent_rate; - int64_t sent_time; + uint64_t sent_time; }; struct { __uint(type, BPF_MAP_TYPE_ARRAY); @@ -210,8 +222,7 @@ struct { #define CREATE_PERSRC_LOOKUP(IPV, IP_TYPE) \ struct persrc_rate##IPV##_entry { \ - int64_t sent_rate; \ - int64_t sent_time; \ + uint64_t sent_time; \ IP_TYPE srcip; \ }; \ \ @@ -226,7 +237,7 @@ struct persrc_rate##IPV##_ptr { \ }; \ \ __attribute__((always_inline)) \ -static inline struct persrc_rate##IPV##_ptr get_v##IPV##_persrc_ratelimit(IP_TYPE key, void *map, size_t map_limit) { \ +static inline struct persrc_rate##IPV##_ptr get_v##IPV##_persrc_ratelimit(IP_TYPE key, void *map, size_t map_limit, int64_t cur_time_masked) { \ struct persrc_rate##IPV##_ptr res = { .rate = NULL, .lock = NULL }; \ uint64_t hash = siphash(&key, sizeof(key), COMPILE_TIME_RAND); \ \ @@ -241,20 +252,25 @@ static inline struct persrc_rate##IPV##_ptr get_v##IPV##_persrc_ratelimit(IP_TYP bpf_spin_lock(&buckets->lock); \ \ int min_sent_idx = 0; \ - int64_t min_sent_time = INT64_MAX; \ + uint64_t min_sent_time = UINT64_MAX; \ for (int i = 0; i < SRC_HASH_BUCKET_COUNT; i++) { \ if (first_bucket[i].srcip == key) { \ res.rate = &first_bucket[i]; \ res.lock = &buckets->lock; \ return res; \ - } else if (min_sent_time > first_bucket[i].sent_time) { \ - min_sent_time = first_bucket[i].sent_time; \ + } \ + int64_t time_offset = ((int64_t)cur_time_masked) - (first_bucket[i].sent_time & RATE_TIME_MASK); \ + if (time_offset < RATE_MIN_TIME_OFFSET || time_offset > RATE_MAX_TIME_OFFSET) { \ + min_sent_idx = i; \ + break; \ + } \ + if ((first_bucket[i].sent_time & RATE_TIME_MASK) < min_sent_time) { \ + min_sent_time = first_bucket[i].sent_time & RATE_TIME_MASK; \ min_sent_idx = i; \ } \ } \ res.rate = &first_bucket[min_sent_idx]; \ res.rate->srcip = key; \ - res.rate->sent_rate = 0; \ res.rate->sent_time = 0; \ res.lock = &buckets->lock; \ return res; \