Commit 4d3b5010 authored by Mark Karpilovskij's avatar Mark Karpilovskij Committed by Daniel Salzman

mod-rrl: improve code readability

parent 1c80ae4e
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> /* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -155,7 +155,7 @@ static int rrl_clsname(uint8_t *dst, size_t maxlen, uint8_t cls, rrl_req_t *req, ...@@ -155,7 +155,7 @@ static int rrl_clsname(uint8_t *dst, size_t maxlen, uint8_t cls, rrl_req_t *req,
return knot_dname_to_wire(dst, name, maxlen); return knot_dname_to_wire(dst, name, maxlen);
} }
static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_storage *a, static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_storage *remote,
rrl_req_t *req, const knot_dname_t *name) rrl_req_t *req, const knot_dname_t *name)
{ {
/* Class */ /* Class */
...@@ -164,19 +164,19 @@ static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_stora ...@@ -164,19 +164,19 @@ static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_stora
int blklen = sizeof(cls); int blklen = sizeof(cls);
/* Address (in network byteorder, adjust masks). */ /* Address (in network byteorder, adjust masks). */
uint64_t nb = 0; uint64_t netblk = 0;
if (a->ss_family == AF_INET6) { if (remote->ss_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)a; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)remote;
nb = *((uint64_t *)(&ipv6->sin6_addr)) & RRL_V6_PREFIX; netblk = *((uint64_t *)(&ipv6->sin6_addr)) & RRL_V6_PREFIX;
} else { } else {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)a; struct sockaddr_in *ipv4 = (struct sockaddr_in *)remote;
nb = ((uint32_t)ipv4->sin_addr.s_addr) & RRL_V4_PREFIX; netblk = ((uint32_t)ipv4->sin_addr.s_addr) & RRL_V4_PREFIX;
} }
if (blklen + sizeof(nb) > maxlen) { if (blklen + sizeof(netblk) > maxlen) {
return KNOT_ESPACE; return KNOT_ESPACE;
} }
memcpy(dst + blklen, (void *)&nb, sizeof(nb)); memcpy(dst + blklen, (void *)&netblk, sizeof(netblk));
blklen += sizeof(nb); blklen += sizeof(netblk);
/* Name */ /* Name */
uint16_t *len_pos = (uint16_t *)(dst + blklen); uint16_t *len_pos = (uint16_t *)(dst + blklen);
...@@ -204,19 +204,19 @@ static int bucket_match(rrl_item_t *b, rrl_item_t *m) ...@@ -204,19 +204,19 @@ static int bucket_match(rrl_item_t *b, rrl_item_t *m)
b->qname == m->qname; b->qname == m->qname;
} }
static int find_free(rrl_table_t *t, unsigned i, uint32_t now) static int find_free(rrl_table_t *tbl, unsigned i, uint32_t now)
{ {
rrl_item_t *np = t->arr + t->size; rrl_item_t *np = tbl->arr + tbl->size;
rrl_item_t *b = NULL; rrl_item_t *b = NULL;
for (b = t->arr + i; b != np; ++b) { for (b = tbl->arr + i; b != np; ++b) {
if (bucket_free(b, now)) { if (bucket_free(b, now)) {
return b - (t->arr + i); return b - (tbl->arr + i);
} }
} }
np = t->arr + i; np = tbl->arr + i;
for (b = t->arr; b != np; ++b) { for (b = tbl->arr; b != np; ++b) {
if (bucket_free(b, now)) { if (bucket_free(b, now)) {
return (b - t->arr) + (t->size - i); return (b - tbl->arr) + (tbl->size - i);
} }
} }
...@@ -224,15 +224,15 @@ static int find_free(rrl_table_t *t, unsigned i, uint32_t now) ...@@ -224,15 +224,15 @@ static int find_free(rrl_table_t *t, unsigned i, uint32_t now)
return i; return i;
} }
static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m) static inline unsigned find_match(rrl_table_t *tbl, uint32_t id, rrl_item_t *m)
{ {
unsigned f = 0; unsigned f = 0;
unsigned d = 0; unsigned d = 0;
unsigned match = t->arr[id].hop; unsigned match = tbl->arr[id].hop;
while (match != 0) { while (match != 0) {
d = __builtin_ctz(match); d = __builtin_ctz(match);
f = (id + d) % t->size; f = (id + d) % tbl->size;
if (bucket_match(t->arr + f, m)) { if (bucket_match(tbl->arr + f, m)) {
return d; return d;
} else { } else {
match &= ~(1<<d); /* clear potential match */ match &= ~(1<<d); /* clear potential match */
...@@ -242,21 +242,21 @@ static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m) ...@@ -242,21 +242,21 @@ static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m)
return HOP_LEN + 1; return HOP_LEN + 1;
} }
static inline unsigned reduce_dist(rrl_table_t *t, unsigned id, unsigned d, unsigned *f) static inline unsigned reduce_dist(rrl_table_t *tbl, unsigned id, unsigned d, unsigned *f)
{ {
unsigned rd = HOP_LEN - 1; unsigned rd = HOP_LEN - 1;
while (rd > 0) { while (rd > 0) {
unsigned s = (t->size + *f - rd) % t->size; /* bucket to be vacated */ unsigned s = (tbl->size + *f - rd) % tbl->size; /* bucket to be vacated */
if (t->arr[s].hop != 0) { if (tbl->arr[s].hop != 0) {
unsigned o = __builtin_ctz(t->arr[s].hop); /* offset of first valid bucket */ unsigned o = __builtin_ctz(tbl->arr[s].hop); /* offset of first valid bucket */
if (o < rd) { /* only offsets in <s, f> are interesting */ if (o < rd) { /* only offsets in <s, f> are interesting */
unsigned e = (s + o) % t->size; /* this item will be displaced to [f] */ unsigned e = (s + o) % tbl->size; /* this item will be displaced to [f] */
unsigned keep_hop = t->arr[*f].hop; /* unpredictable padding */ unsigned keep_hop = tbl->arr[*f].hop; /* unpredictable padding */
memcpy(t->arr + *f, t->arr + e, sizeof(rrl_item_t)); memcpy(tbl->arr + *f, tbl->arr + e, sizeof(rrl_item_t));
t->arr[*f].hop = keep_hop; tbl->arr[*f].hop = keep_hop;
t->arr[e].cls = CLS_NULL; tbl->arr[e].cls = CLS_NULL;
t->arr[s].hop &= ~(1<<o); tbl->arr[s].hop &= ~(1<<o);
t->arr[s].hop |= 1<<rd; tbl->arr[s].hop |= 1<<rd;
*f = e; *f = e;
return d - (rd - o); return d - (rd - o);
} }
...@@ -289,49 +289,49 @@ static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss, ...@@ -289,49 +289,49 @@ static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss,
addr_str, rrl_clsstr(cls), what); addr_str, rrl_clsstr(cls), what);
} }
static void rrl_lock(rrl_table_t *t, int lk_id) static void rrl_lock(rrl_table_t *tbl, int lk_id)
{ {
assert(lk_id > -1); assert(lk_id > -1);
pthread_mutex_lock(t->lk + lk_id); pthread_mutex_lock(tbl->lk + lk_id);
} }
static void rrl_unlock(rrl_table_t *t, int lk_id) static void rrl_unlock(rrl_table_t *tbl, int lk_id)
{ {
assert(lk_id > -1); assert(lk_id > -1);
pthread_mutex_unlock(t->lk + lk_id); pthread_mutex_unlock(tbl->lk + lk_id);
} }
static int rrl_setlocks(rrl_table_t *rrl, uint32_t granularity) static int rrl_setlocks(rrl_table_t *tbl, uint32_t granularity)
{ {
assert(!rrl->lk); /* Cannot change while locks are used. */ assert(!tbl->lk); /* Cannot change while locks are used. */
assert(granularity <= rrl->size / 10); /* Due to int. division err. */ assert(granularity <= tbl->size / 10); /* Due to int. division err. */
if (pthread_mutex_init(&rrl->ll, NULL) < 0) { if (pthread_mutex_init(&tbl->ll, NULL) < 0) {
return KNOT_ENOMEM; return KNOT_ENOMEM;
} }
/* Alloc new locks. */ /* Alloc new locks. */
rrl->lk = malloc(granularity * sizeof(pthread_mutex_t)); tbl->lk = malloc(granularity * sizeof(pthread_mutex_t));
if (!rrl->lk) { if (!tbl->lk) {
return KNOT_ENOMEM; return KNOT_ENOMEM;
} }
memset(rrl->lk, 0, granularity * sizeof(pthread_mutex_t)); memset(tbl->lk, 0, granularity * sizeof(pthread_mutex_t));
/* Initialize. */ /* Initialize. */
for (size_t i = 0; i < granularity; ++i) { for (size_t i = 0; i < granularity; ++i) {
if (pthread_mutex_init(rrl->lk + i, NULL) < 0) { if (pthread_mutex_init(tbl->lk + i, NULL) < 0) {
break; break;
} }
++rrl->lk_count; ++tbl->lk_count;
} }
/* Incomplete initialization */ /* Incomplete initialization */
if (rrl->lk_count != granularity) { if (tbl->lk_count != granularity) {
for (size_t i = 0; i < rrl->lk_count; ++i) { for (size_t i = 0; i < tbl->lk_count; ++i) {
pthread_mutex_destroy(rrl->lk + i); pthread_mutex_destroy(tbl->lk + i);
} }
free(rrl->lk); free(tbl->lk);
rrl->lk_count = 0; tbl->lk_count = 0;
return KNOT_ERROR; return KNOT_ERROR;
} }
...@@ -345,41 +345,41 @@ rrl_table_t *rrl_create(size_t size, uint32_t rate) ...@@ -345,41 +345,41 @@ rrl_table_t *rrl_create(size_t size, uint32_t rate)
} }
const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t); const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
rrl_table_t *t = calloc(1, tbl_len); rrl_table_t *tbl = calloc(1, tbl_len);
if (!t) { if (!tbl) {
return NULL; return NULL;
} }
t->size = size; tbl->size = size;
t->rate = rate; tbl->rate = rate;
if (dnssec_random_buffer((uint8_t *)&t->key, sizeof(t->key)) != DNSSEC_EOK) { if (dnssec_random_buffer((uint8_t *)&tbl->key, sizeof(tbl->key)) != DNSSEC_EOK) {
free(t); free(tbl);
return NULL; return NULL;
} }
if (rrl_setlocks(t, RRL_LOCK_GRANULARITY) != KNOT_EOK) { if (rrl_setlocks(tbl, RRL_LOCK_GRANULARITY) != KNOT_EOK) {
free(t); free(tbl);
return NULL; return NULL;
} }
return t; return tbl;
} }
/*! \brief Get bucket for current combination of parameters. */ /*! \brief Get bucket for current combination of parameters. */
static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, static rrl_item_t *rrl_hash(rrl_table_t *tbl, const struct sockaddr_storage *remote,
rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp, rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp,
int *lock) int *lock)
{ {
uint8_t buf[RRL_CLSBLK_MAXLEN]; uint8_t buf[RRL_CLSBLK_MAXLEN];
int len = rrl_classify(buf, sizeof(buf), a, req, zone); int len = rrl_classify(buf, sizeof(buf), remote, req, zone);
if (len < 0) { if (len < 0) {
return NULL; return NULL;
} }
uint32_t id = SipHash24(&t->key, buf, len) % t->size; uint32_t id = SipHash24(&tbl->key, buf, len) % tbl->size;
/* Lock for lookup. */ /* Lock for lookup. */
pthread_mutex_lock(&t->ll); pthread_mutex_lock(&tbl->ll);
/* Find an exact match in <id, id + HOP_LEN). */ /* Find an exact match in <id, id + HOP_LEN). */
uint8_t *qname = buf + sizeof(uint8_t) + sizeof(uint64_t); uint8_t *qname = buf + sizeof(uint8_t) + sizeof(uint64_t);
...@@ -388,33 +388,33 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, ...@@ -388,33 +388,33 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a,
rrl_item_t match = { rrl_item_t match = {
.hop = 0, .hop = 0,
.netblk = netblk, .netblk = netblk,
.ntok = t->rate * RRL_CAPACITY, .ntok = tbl->rate * RRL_CAPACITY,
.cls = buf[0], .cls = buf[0],
.flags = RRL_BF_NULL, .flags = RRL_BF_NULL,
.qname = SipHash24(&t->key, qname + 1, qname[0]), .qname = SipHash24(&tbl->key, qname + 1, qname[0]),
.time = stamp .time = stamp
}; };
unsigned d = find_match(t, id, &match); unsigned d = find_match(tbl, id, &match);
if (d > HOP_LEN) { /* not an exact match, find free element [f] */ if (d > HOP_LEN) { /* not an exact match, find free element [f] */
d = find_free(t, id, stamp); d = find_free(tbl, id, stamp);
} }
/* Reduce distance to fit <id, id + HOP_LEN) */ /* Reduce distance to fit <id, id + HOP_LEN) */
unsigned f = (id + d) % t->size; unsigned f = (id + d) % tbl->size;
while (d >= HOP_LEN) { while (d >= HOP_LEN) {
d = reduce_dist(t, id, d, &f); d = reduce_dist(tbl, id, d, &f);
} }
/* Assign granular lock and unlock lookup. */ /* Assign granular lock and unlock lookup. */
*lock = f % t->lk_count; *lock = f % tbl->lk_count;
rrl_lock(t, *lock); rrl_lock(tbl, *lock);
pthread_mutex_unlock(&t->ll); pthread_mutex_unlock(&tbl->ll);
/* found free elm 'k' which is in <id, id + HOP_LEN) */ /* found free elm 'k' which is in <id, id + HOP_LEN) */
t->arr[id].hop |= (1 << d); tbl->arr[id].hop |= (1 << d);
rrl_item_t *b = t->arr + f; rrl_item_t *b = tbl->arr + f;
assert(f == (id+d) % t->size); assert(f == (id+d) % tbl->size);
/* Inspect bucket state. */ /* Inspect bucket state. */
unsigned hop = b->hop; unsigned hop = b->hop;
...@@ -427,7 +427,7 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, ...@@ -427,7 +427,7 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a,
if (!(b->flags & RRL_BF_SSTART)) { if (!(b->flags & RRL_BF_SSTART)) {
memcpy(b, &match, sizeof(rrl_item_t)); memcpy(b, &match, sizeof(rrl_item_t));
b->hop = hop; b->hop = hop;
b->ntok = t->rate + t->rate / RRL_SSTART; b->ntok = tbl->rate + tbl->rate / RRL_SSTART;
b->flags |= RRL_BF_SSTART; b->flags |= RRL_BF_SSTART;
} }
} }
...@@ -435,10 +435,10 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, ...@@ -435,10 +435,10 @@ static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a,
return b; return b;
} }
int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
const knot_dname_t *zone, knotd_mod_t *mod) rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod)
{ {
if (!rrl || !req || !a) { if (!rrl || !req || !remote) {
return KNOT_EINVAL; return KNOT_EINVAL;
} }
...@@ -446,8 +446,8 @@ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req ...@@ -446,8 +446,8 @@ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req
int ret = KNOT_EOK; int ret = KNOT_EOK;
int lock = -1; int lock = -1;
uint32_t now = time_now().tv_sec; uint32_t now = time_now().tv_sec;
rrl_item_t *b = rrl_hash(rrl, a, req, zone, now, &lock); rrl_item_t *bucket = rrl_hash(rrl, remote, req, zone, now, &lock);
if (!b) { if (!bucket) {
if (lock > -1) { if (lock > -1) {
rrl_unlock(rrl, lock); rrl_unlock(rrl, lock);
} }
...@@ -455,41 +455,41 @@ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req ...@@ -455,41 +455,41 @@ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req
} }
/* Calculate rate for dT */ /* Calculate rate for dT */
uint32_t dt = now - b->time; uint32_t dt = now - bucket->time;
if (dt > RRL_CAPACITY) { if (dt > RRL_CAPACITY) {
dt = RRL_CAPACITY; dt = RRL_CAPACITY;
} }
/* Visit bucket. */ /* Visit bucket. */
b->time = now; bucket->time = now;
if (dt > 0) { /* Window moved. */ if (dt > 0) { /* Window moved. */
/* Check state change. */ /* Check state change. */
if ((b->ntok > 0 || dt > 1) && (b->flags & RRL_BF_ELIMIT)) { if ((bucket->ntok > 0 || dt > 1) && (bucket->flags & RRL_BF_ELIMIT)) {
b->flags &= ~RRL_BF_ELIMIT; bucket->flags &= ~RRL_BF_ELIMIT;
rrl_log_state(mod, a, b->flags, b->cls); rrl_log_state(mod, remote, bucket->flags, bucket->cls);
} }
/* Add new tokens. */ /* Add new tokens. */
uint32_t dn = rrl->rate * dt; uint32_t dn = rrl->rate * dt;
if (b->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */ if (bucket->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */
b->flags &= ~RRL_BF_SSTART; bucket->flags &= ~RRL_BF_SSTART;
} }
b->ntok += dn; bucket->ntok += dn;
if (b->ntok > RRL_CAPACITY * rrl->rate) { if (bucket->ntok > RRL_CAPACITY * rrl->rate) {
b->ntok = RRL_CAPACITY * rrl->rate; bucket->ntok = RRL_CAPACITY * rrl->rate;
} }
} }
/* Last item taken. */ /* Last item taken. */
if (b->ntok == 1 && !(b->flags & RRL_BF_ELIMIT)) { if (bucket->ntok == 1 && !(bucket->flags & RRL_BF_ELIMIT)) {
b->flags |= RRL_BF_ELIMIT; bucket->flags |= RRL_BF_ELIMIT;
rrl_log_state(mod, a, b->flags, b->cls); rrl_log_state(mod, remote, bucket->flags, bucket->cls);
} }
/* Decay current bucket. */ /* Decay current bucket. */
if (b->ntok > 0) { if (bucket->ntok > 0) {
--b->ntok; --bucket->ntok;
} else if (b->ntok == 0) { } else if (bucket->ntok == 0) {
ret = KNOT_ELIMIT; ret = KNOT_ELIMIT;
} }
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> /* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -90,15 +90,15 @@ rrl_table_t *rrl_create(size_t size, uint32_t rate); ...@@ -90,15 +90,15 @@ rrl_table_t *rrl_create(size_t size, uint32_t rate);
* \brief Query the RRL table for accept or deny, when the rate limit is reached. * \brief Query the RRL table for accept or deny, when the rate limit is reached.
* *
* \param rrl RRL table. * \param rrl RRL table.
* \param a Source address. * \param remote Source address.
* \param req RRL request (containing resp., flags and question). * \param req RRL request (containing resp., flags and question).
* \param zone Zone name related to the response (or NULL). * \param zone Zone name related to the response (or NULL).
* \param mod Query module (needed for logging). * \param mod Query module (needed for logging).
* \retval KNOT_EOK if passed. * \retval KNOT_EOK if passed.
* \retval KNOT_ELIMIT when the limit is reached. * \retval KNOT_ELIMIT when the limit is reached.
*/ */
int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
const knot_dname_t *zone, knotd_mod_t *mod); rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod);
/*! /*!
* \brief Roll a dice whether answer slips or not. * \brief Roll a dice whether answer slips or not.
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> /* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -164,17 +164,16 @@ int rrl_load(knotd_mod_t *mod) ...@@ -164,17 +164,16 @@ int rrl_load(knotd_mod_t *mod)
} }
// Create table. // Create table.
knotd_conf_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT); uint32_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT).single.integer;
knotd_conf_t size = knotd_conf_mod(mod, MOD_TBL_SIZE); size_t size = knotd_conf_mod(mod, MOD_TBL_SIZE).single.integer;
ctx->rrl = rrl_create(size.single.integer, rate.single.integer); ctx->rrl = rrl_create(size, rate);
if (ctx->rrl == NULL) { if (ctx->rrl == NULL) {
ctx_free(ctx); ctx_free(ctx);
return KNOT_ENOMEM; return KNOT_ENOMEM;
} }
// Get slip. // Get slip.
knotd_conf_t conf = knotd_conf_mod(mod, MOD_SLIP); ctx->slip = knotd_conf_mod(mod, MOD_SLIP).single.integer;
ctx->slip = conf.single.integer;
// Get whitelist. // Get whitelist.
ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST); ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment