Commit ac8707ad authored by Daniel Salzman's avatar Daniel Salzman

mod-rrl: remove seeding (obsoleted by Siphash), tiny refactoring

parent a3318fdb
......@@ -19,6 +19,7 @@
#include "knot/modules/rrl/functions.h"
#include "contrib/sockaddr.h"
#include "dnssec/error.h"
#include "dnssec/random.h"
/* Hopscotch defines. */
......@@ -31,6 +32,8 @@
/* Defaults */
#define RRL_SSTART 2 /* 1/Nth of the rate for slow start */
#define RRL_PSIZE_LARGE 1024
#define RRL_CAPACITY 4 /* Window size in seconds */
#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
/* Classification */
enum {
......@@ -152,14 +155,10 @@ static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls, rrl_req_t *req,
}
static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage *a,
rrl_req_t *p, const knot_dname_t *z, uint32_t seed)
rrl_req_t *req, const knot_dname_t *name)
{
if (!dst || !p || !a || maxlen == 0) {
return KNOT_EINVAL;
}
/* Class */
uint8_t cls = rrl_clsid(p);
uint8_t cls = rrl_clsid(req);
*dst = cls;
int blklen = sizeof(cls);
......@@ -181,7 +180,7 @@ static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage
/* Name */
uint16_t *len_pos = (uint16_t *)(dst + blklen);
blklen += sizeof(uint16_t);
int ret = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z);
int ret = rrl_clsname(dst + blklen, maxlen - blklen, cls, req, name);
if (ret < 0) {
return ret;
}
......@@ -189,17 +188,11 @@ static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage
memcpy(len_pos, &len, sizeof(len));
blklen += len;
/* Seed. */
if (blklen + sizeof(seed) > maxlen) {
return KNOT_ESPACE;
}
memcpy(dst + blklen, (void *)&seed, sizeof(seed));
blklen += sizeof(seed);
return blklen;
}
static int bucket_free(rrl_item_t *b, uint32_t now) {
static int bucket_free(rrl_item_t *b, uint32_t now)
{
return b->cls == CLS_NULL || (b->time + 1 < now);
}
......@@ -295,46 +288,20 @@ static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss,
addr_str, rrl_clsstr(cls), what);
}
rrl_table_t *rrl_create(size_t size)
{
if (size == 0) {
return NULL;
}
const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
rrl_table_t *t = calloc(1, tbl_len);
if (!t) {
return NULL;
}
(void)dnssec_random_buffer((uint8_t *)&t->key, sizeof(t->key));
t->size = size;
rrl_reseed(t);
return t;
}
uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate)
static void rrl_lock(rrl_table_t *t, int lk_id)
{
if (!rrl) {
return 0;
}
uint32_t old = rrl->rate;
rrl->rate = rate;
return old;
assert(lk_id > -1);
pthread_mutex_lock(t->lk + lk_id);
}
uint32_t rrl_rate(rrl_table_t *rrl)
static void rrl_unlock(rrl_table_t *t, int lk_id)
{
return rrl ? rrl->rate : 0;
assert(lk_id > -1);
pthread_mutex_unlock(t->lk + lk_id);
}
int rrl_setlocks(rrl_table_t *rrl, unsigned granularity)
static int rrl_setlocks(rrl_table_t *rrl, uint32_t granularity)
{
if (!rrl) {
return KNOT_EINVAL;
}
assert(!rrl->lk); /* Cannot change while locks are used. */
assert(granularity <= rrl->size / 10); /* Due to int. division err. */
......@@ -370,11 +337,40 @@ int rrl_setlocks(rrl_table_t *rrl, unsigned granularity)
return KNOT_EOK;
}
rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p,
const knot_dname_t *zone, uint32_t stamp, int *lock)
rrl_table_t *rrl_create(size_t size, uint32_t rate)
{
if (size == 0) {
return NULL;
}
const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
rrl_table_t *t = calloc(1, tbl_len);
if (!t) {
return NULL;
}
t->size = size;
t->rate = rate;
if (dnssec_random_buffer((uint8_t *)&t->key, sizeof(t->key)) != DNSSEC_EOK) {
free(t);
return NULL;
}
if (rrl_setlocks(t, RRL_LOCK_GRANULARITY) != KNOT_EOK) {
free(t);
return NULL;
}
return t;
}
/*! \brief Get bucket for current combination of parameters. */
static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a,
rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp,
int *lock)
{
char buf[RRL_CLSBLK_MAXLEN];
int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed);
int len = rrl_classify(buf, sizeof(buf), a, req, zone);
if (len < 0) {
return NULL;
}
......@@ -512,7 +508,7 @@ bool rrl_slip_roll(int n_slip)
return (roll < threshold);
}
int rrl_destroy(rrl_table_t *rrl)
void rrl_destroy(rrl_table_t *rrl)
{
if (rrl) {
if (rrl->lk_count > 0) {
......@@ -525,46 +521,4 @@ int rrl_destroy(rrl_table_t *rrl)
}
free(rrl);
return KNOT_EOK;
}
int rrl_reseed(rrl_table_t *rrl)
{
/* Lock entire table. */
if (rrl->lk_count > 0) {
pthread_mutex_lock(&rrl->ll);
for (unsigned i = 0; i < rrl->lk_count; ++i) {
rrl_lock(rrl, i);
}
}
memset(rrl->arr, 0, rrl->size * sizeof(rrl_item_t));
rrl->seed = dnssec_random_uint32_t();
if (rrl->lk_count > 0) {
for (unsigned i = 0; i < rrl->lk_count; ++i) {
rrl_unlock(rrl, i);
}
pthread_mutex_unlock(&rrl->ll);
}
return KNOT_EOK;
}
int rrl_lock(rrl_table_t *t, int lk_id)
{
assert(lk_id > -1);
if (pthread_mutex_lock(t->lk + lk_id) != 0) {
return KNOT_ERROR;
}
return KNOT_EOK;
}
int rrl_unlock(rrl_table_t *t, int lk_id)
{
assert(lk_id > -1);
if (pthread_mutex_unlock(t->lk + lk_id)!= 0) {
return KNOT_ERROR;
}
return KNOT_EOK;
}
......@@ -26,8 +26,6 @@
/* Defaults */
#define RRL_SLIP_MAX 100
#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
#define RRL_CAPACITY 4 /* Window size in seconds */
/*!
* \brief RRL hash bucket.
......@@ -57,7 +55,6 @@ typedef struct {
typedef struct {
SIPHASH_KEY key; /* Siphash key. */
uint32_t rate; /* Configured RRL limit. */
uint32_t seed; /* Pseudorandom seed for hashing. */
pthread_mutex_t ll;
pthread_mutex_t *lk; /* Table locks. */
unsigned lk_count; /* Table lock count (granularity). */
......@@ -84,50 +81,10 @@ typedef struct {
/*!
* \brief Create a RRL table.
* \param size Fixed hashtable size (reasonable large prime is recommended).
* \param rate Rate (in pkts/sec).
* \return created table or NULL.
*/
rrl_table_t *rrl_create(size_t size);
/*!
* \brief Get RRL table default rate.
* \param rrl RRL table.
* \return rate
*/
uint32_t rrl_rate(rrl_table_t *rrl);
/*!
* \brief Set RRL table default rate.
*
* \note When changing the rate, it is NOT applied to all buckets immediately.
*
* \param rrl RRL table.
* \param rate New rate (in pkts/sec).
* \return old rate
*/
uint32_t rrl_setrate(rrl_table_t *rrl, uint32_t rate);
/*!
* \brief Set N distributed locks for the RRL table.
*
* \param rrl RRL table.
* \param granularity Number of created locks.
* \retval KNOT_EOK
* \retval KNOT_EINVAL
*/
int rrl_setlocks(rrl_table_t *rrl, unsigned granularity);
/*!
* \brief Get bucket for current combination of parameters.
* \param t RRL table.
* \param a Source address.
* \param p RRL request.
* \param zone Relate zone name.
* \param stamp Timestamp (current time).
* \param lock Held lock.
* \return assigned bucket
*/
rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p,
const knot_dname_t *zone, uint32_t stamp, int *lock);
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.
......@@ -153,31 +110,5 @@ bool rrl_slip_roll(int n_slip);
/*!
* \brief Destroy RRL table.
* \param rrl RRL table.
* \return KNOT_EOK
*/
int rrl_destroy(rrl_table_t *rrl);
/*!
* \brief Reseed RRL table secret.
* \param rrl RRL table.
* \return KNOT_EOK
*/
int rrl_reseed(rrl_table_t *rrl);
/*!
* \brief Lock specified element lock.
* \param rrl RRL table.
* \param lk_id Specified lock.
* \retval KNOT_EOK
* \retval KNOT_ERROR
*/
int rrl_lock(rrl_table_t *rrl, int lk_id);
/*!
* \brief Unlock specified element lock.
* \param rrl RRL table.
* \param lk_id Specified lock.
* \retval KNOT_EOK
* \retval KNOT_ERROR
*/
int rrl_unlock(rrl_table_t *rrl, int lk_id);
void rrl_destroy(rrl_table_t *rrl);
......@@ -189,37 +189,23 @@ int rrl_load(knotd_mod_t *mod)
}
// Create table.
knotd_conf_t conf = knotd_conf_mod(mod, MOD_TBL_SIZE);
ctx->rrl = rrl_create(conf.single.integer);
knotd_conf_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT);
knotd_conf_t size = knotd_conf_mod(mod, MOD_TBL_SIZE);
ctx->rrl = rrl_create(size.single.integer, rate.single.integer);
if (ctx->rrl == NULL) {
ctx_free(ctx);
return KNOT_ENOMEM;
}
// Set locks.
int ret = rrl_setlocks(ctx->rrl, RRL_LOCK_GRANULARITY);
if (ret != KNOT_EOK) {
ctx_free(ctx);
return ret;
}
// Set rate limit.
conf = knotd_conf_mod(mod, MOD_RATE_LIMIT);
ret = rrl_setrate(ctx->rrl, conf.single.integer);
if (ret != KNOT_EOK) {
ctx_free(ctx);
return ret;
}
// Get slip.
conf = knotd_conf_mod(mod, MOD_SLIP);
knotd_conf_t conf = knotd_conf_mod(mod, MOD_SLIP);
ctx->slip = conf.single.integer;
// Get whitelist.
ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST);
// Set up statistics counters.
ret = knotd_mod_stats_add(mod, "slipped", 1, NULL);
int ret = knotd_mod_stats_add(mod, "slipped", 1, NULL);
if (ret != KNOT_EOK) {
ctx_free(ctx);
return ret;
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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
......@@ -20,14 +20,13 @@
#include "dnssec/random.h"
#include "libknot/libknot.h"
#include "contrib/sockaddr.h"
#include "knot/modules/rrl/functions.h"
#include "knot/modules/rrl/functions.c"
/* Enable time-dependent tests. */
//#define ENABLE_TIMED_TESTS
#define RRL_SIZE 196613
#define RRL_THREADS 8
#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */
#define RRL_LOCKS 64
/* Disabled as default as it depends on random input.
* Table may be consistent even if some collision occur (and they may occur).
......@@ -91,11 +90,7 @@ static void rrl_hopscotch(struct runnable_data* rd)
int main(int argc, char *argv[])
{
#ifdef ENABLE_TIMED_TESTS
plan(10);
#else
plan(5);
#endif
plan_lazy();
dnssec_crypto_init();
......@@ -126,18 +121,10 @@ int main(int argc, char *argv[])
rq.flags = 0;
/* 1. create rrl table */
rrl_table_t *rrl = rrl_create(RRL_SIZE);
const uint32_t rate = 10;
rrl_table_t *rrl = rrl_create(RRL_SIZE, rate);
ok(rrl != NULL, "rrl: create");
/* 2. set rate limit */
uint32_t rate = 10;
rrl_setrate(rrl, rate);
is_int(rate, rrl_rate(rrl), "rrl: setrate");
/* 3. setlocks */
ret = rrl_setlocks(rrl, RRL_LOCKS);
is_int(KNOT_EOK, ret, "rrl: setlocks");
/* 4. N unlimited requests. */
knot_dname_t *zone = knot_dname_from_str_alloc("rrl.");
......@@ -163,34 +150,13 @@ int main(int argc, char *argv[])
/* 6. limited IPv6 request */
ret = rrl_query(rrl, &addr6, &rq, zone, NULL);
is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv6 request");
#endif
/* 7. invalid values. */
ret = 0;
rrl_create(0); // NULL
ret += rrl_setrate(0, 0); // 0
ret += rrl_rate(0); // 0
ret += rrl_setlocks(0,0); // -1
ret += rrl_query(0, 0, 0, 0, NULL); // -1
ret += rrl_query(rrl, 0, 0, 0, NULL); // -1
ret += rrl_query(rrl, (void*)0x1, 0, 0, NULL); // -1
ret += rrl_destroy(0); // -1
is_int(-88, ret, "rrl: not crashed while executing functions on NULL context");
#ifdef ENABLE_TIMED_TESTS
/* 8. hopscotch test */
struct runnable_data rd = {
1, rrl, &addr, &rq, zone
};
rrl_hopscotch(&rd);
ok(rd.passed, "rrl: hashtable is ~ consistent");
/* 9. reseed */
is_int(0, rrl_reseed(rrl), "rrl: reseed");
/* 10. hopscotch after reseed. */
rrl_hopscotch(&rd);
ok(rd.passed, "rrl: hashtable is ~ consistent");
#endif
knot_dname_free(&zone, NULL);
......
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