Commit 55d0a3fd authored by Marek Vavrusa's avatar Marek Vavrusa

Ported RRL to the new query processing API.

parent 7aaa0b28
......@@ -33,6 +33,7 @@
#include "libknot/dname.h"
#include "libknot/binary.h"
#include "libknot/edns.h"
#include "knot/server/rrl.h"
#include "knot/conf/conf.h"
#include "knot/conf/libknotd_la-cf-parse.h" /* Automake generated header. */
......@@ -652,7 +653,7 @@ system:
SET_SIZE(new_config->rrl_size, $3.i, "rate-limit-size");
}
| system RATE_LIMIT_SLIP NUM ';' {
SET_INT(new_config->rrl_slip, $3.i, "rate-limit-slip");
SET_NUM(new_config->rrl_slip, $3.i, 1, RRL_SLIP_MAX, "rate-limit-slip");
}
| system TRANSFERS NUM ';' {
SET_INT(new_config->xfers, $3.i, "transfers");
......
......@@ -482,6 +482,18 @@ int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
return ret;
}
bool rrl_slip_roll(int n_slip)
{
/* Now n_slip means every Nth answer slips.
* That represents a chance of 1/N that answer slips.
* Therefore, on average, from 100 answers 100/N will slip. */
int threshold = RRL_SLIP_MAX / n_slip;
int roll = knot_random_uint16_t() % RRL_SLIP_MAX;
int ret = (roll < threshold);
printf("slip chance %d < %d roll result = %d\n", roll, threshold, ret);
return ret;
}
int rrl_destroy(rrl_table_t *rrl)
{
if (rrl) {
......
......@@ -34,6 +34,7 @@
#include "libknot/zone/zone.h"
/* Defaults */
#define RRL_SLIP_MAX 100
#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
/*!
......@@ -142,6 +143,13 @@ rrl_item_t* rrl_hash(rrl_table_t *t, const sockaddr_t *a, rrl_req_t *p,
int rrl_query(rrl_table_t *rrl, const sockaddr_t *a, rrl_req_t *req,
const knot_zone_t *zone);
/*!
* \brief Roll a dice whether answer slips or not.
* \param n_slip Number represents every Nth answer that is slipped.
* \return true or false
*/
bool rrl_slip_roll(int n_slip);
/*!
* \brief Destroy RRL table.
* \param rrl RRL table.
......
......@@ -481,7 +481,7 @@ int server_refresh(server_t *server)
if (zd && zd->xfr_in.timer) {
evsched_cancel(sch, zd->xfr_in.timer);
evsched_schedule(sch, zd->xfr_in.timer,
knot_random_int() % 500 + count/2);
knot_random_uint16_t() % 500 + count/2);
/* Cumulative delay. */
++count;
}
......
......@@ -60,7 +60,7 @@ typedef struct tcp_worker_t {
/*! \brief Calculate TCP throttle time (random). */
static inline int tcp_throttle() {
return TCP_THROTTLE_LO + (knot_random_int() % TCP_THROTTLE_HI);
return TCP_THROTTLE_LO + (knot_random_uint16_t() % TCP_THROTTLE_HI);
}
/*! \brief Sweep TCP connection. */
......
......@@ -105,33 +105,6 @@ static inline void udp_pps_begin() {}
static inline void udp_pps_sample(unsigned n, unsigned thr_id) {}
#endif
/*! \brief RRL reject procedure. */
static size_t udp_rrl_reject(const knot_nameserver_t *ns,
const knot_pkt_t *packet,
uint8_t* resp, size_t rlen,
uint8_t rcode, unsigned *slip)
{
int n_slip = conf()->rrl_slip; /* Check SLIP. */
if (n_slip > 0 && n_slip == ++*slip) {
knot_ns_error_response_from_query(ns, packet, rcode, resp, &rlen);
switch(rcode) { /* Do not set TC=1 to some RCODEs. */
case KNOT_RCODE_FORMERR:
case KNOT_RCODE_REFUSED:
case KNOT_RCODE_SERVFAIL:
case KNOT_RCODE_NOTIMPL:
break;
default:
knot_wire_set_tc(resp); /* Set TC=1 */
break;
}
*slip = 0; /* Restart SLIP interval. */
return rlen;
}
return 0; /* Discard response. */
}
int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
struct iovec *rx, struct iovec *tx)
{
......@@ -142,6 +115,14 @@ int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
dbg_net("udp: received %zd bytes from '%s@%d'.\n", rx->iov_len,
strfrom, sockaddr_portnum(addr));
#endif
/* Rate limit is applied? */
server_t *server = (server_t *)query_ctx->ns->data;
if (knot_unlikely(server->rrl != NULL) && server->rrl->rate > 0) {
query_ctx->flags |= NS_QUERY_RATELIMIT;
} else {
query_ctx->flags &= ~NS_QUERY_RATELIMIT;
}
/* Create query processing parameter. */
struct ns_proc_query_param param;
......@@ -173,28 +154,7 @@ int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
/* Reset context. */
ns_proc_finish(query_ctx);
return KNOT_EOK;
/*! \todo Move RRL to IN processing. */
// rrl_table_t *rrl = ans->srv->rrl;
// /* Process RRL. */
// if (knot_unlikely(rrl != NULL) && rrl->rate > 0) {
// rrl_req_t rrl_rq;
// memset(&rrl_rq, 0, sizeof(rrl_req_t));
// rrl_rq.w = tx->iov_base; /* Wire */
// rrl_rq.query = query;
// rcu_read_lock();
// rrl_rq.flags = query->flags;
// if (rrl_query(rrl, addr, &rrl_rq, query->zone) != KNOT_EOK) {
// tx->iov_len = udp_rrl_reject(ns, query, tx->iov_base,
// KNOT_WIRE_MAX_PKTSIZE,
// knot_wire_get_rcode(query->wire),
// &ans->slip);
// }
// rcu_read_unlock();
// }
}
/* Check for sendmmsg syscall. */
......
......@@ -63,7 +63,6 @@ static inline int knot_random_buffer(void *dest, size_t size)
return buffer; \
}
_knot_register_random_type(int);
_knot_register_random_type(uint16_t);
_knot_register_random_type(uint32_t);
......
......@@ -185,121 +185,6 @@ int knot_ns_parse_packet(knot_pkt_t *packet, knot_packet_type_t *type)
return KNOT_RCODE_NOERROR;
}
/*----------------------------------------------------------------------------*/
static void knot_ns_error_response(const knot_nameserver_t *ns,
uint16_t query_id, uint8_t *flags1_query,
uint8_t rcode, uint8_t *response_wire,
size_t *rsize)
{
memcpy(response_wire, ns->err_response->wire, ns->err_response->size);
// copy only the ID of the query
knot_wire_set_id(response_wire, query_id);
if (flags1_query != NULL) {
if (knot_wire_flags_get_rd(*flags1_query) != 0) {
knot_wire_set_rd(response_wire);
}
knot_wire_set_opcode(response_wire,
knot_wire_flags_get_opcode(*flags1_query));
}
// set the RCODE
knot_wire_set_rcode(response_wire, rcode);
*rsize = ns->err_response->size;
}
/*----------------------------------------------------------------------------*/
int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver,
const uint8_t *query, size_t size,
uint8_t rcode,
uint8_t *response_wire, size_t *rsize)
{
if (size < 2) {
// ignore packet
return KNOT_EFEWDATA;
}
uint16_t pkt_id = knot_wire_get_id(query);
uint8_t *flags1_ptr = NULL;
uint8_t flags1;
if (size > KNOT_WIRE_OFFSET_FLAGS1) {
flags1 = knot_wire_get_flags1(query);
flags1_ptr = &flags1;
}
knot_ns_error_response(nameserver, pkt_id, flags1_ptr,
rcode, response_wire, rsize);
return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
const knot_pkt_t *query,
uint8_t rcode, uint8_t *response_wire,
size_t *rsize)
{
if (query->parsed < 2) {
// ignore packet
return KNOT_EFEWDATA;
}
if (query->parsed < KNOT_WIRE_HEADER_SIZE) {
return knot_ns_error_response_from_query_wire(nameserver,
query->wire, query->size, rcode, response_wire,
rsize);
}
size_t max_size = *rsize;
uint8_t flags1 = knot_wire_get_flags1(query->wire);
// prepare the generic error response
knot_ns_error_response(nameserver, knot_wire_get_id(query->wire),
&flags1, rcode, response_wire,
rsize);
/* Append question if parsed. */
uint16_t header_len = KNOT_WIRE_HEADER_SIZE;
uint16_t question_len = knot_pkt_question_size(query);
if (question_len > header_len && question_len <= max_size) {
/* Append question only (do not rewrite header). */
uint16_t to_copy = question_len - header_len;
if (response_wire != query->wire) {
memcpy(response_wire + header_len,
query->wire + header_len,
to_copy);
}
*rsize += to_copy;
knot_wire_set_qdcount(response_wire, 1);
}
return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
void knot_ns_error_response_full(knot_nameserver_t *nameserver,
knot_pkt_t *response, uint8_t rcode,
uint8_t *response_wire, size_t *rsize)
{
knot_wire_set_rcode(response->wire, rcode);
knot_ns_error_response_from_query(nameserver,
response->query,
KNOT_RCODE_SERVFAIL,
response_wire, rsize);
}
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
int ns_serial_compare(uint32_t s1, uint32_t s2)
......@@ -688,11 +573,6 @@ int ns_proc_begin(ns_proc_context_t *ctx, void *module_param, const ns_proc_modu
int ns_proc_reset(ns_proc_context_t *ctx)
{
/* Only in operable state. */
if (ctx->state == NS_PROC_NOOP) {
return NS_PROC_NOOP;
}
/* #10 implement */
ctx->state = ctx->module->reset(ctx);
......
......@@ -218,15 +218,6 @@ knot_nameserver_t *knot_ns_create();
*/
int knot_ns_parse_packet(knot_pkt_t *packet, knot_packet_type_t *type);
int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver,
const uint8_t *query, size_t size,
uint8_t rcode, uint8_t *response_wire,
size_t *rsize);
int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
const knot_pkt_t *query,
uint8_t rcode, uint8_t *response_wire,
size_t *rsize);
/*!
* \brief Compares two zone serials.
*
......
......@@ -17,12 +17,16 @@
#include "libknot/nameserver/ixfr.h"
#include "libknot/nameserver/update.h"
#include "knot/server/notify.h"
#include "knot/server/server.h"
#include "knot/server/rrl.h"
#include "knot/conf/conf.h"
/* Forward decls. */
static const knot_zone_t *answer_zone_find(const knot_pkt_t *pkt, knot_zonedb_t *zonedb);
static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_context_t *ctx);
static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx);
static int query_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx);
static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx);
/*! \brief Module implementation. */
const ns_proc_module_t _ns_proc_query = {
......@@ -103,12 +107,6 @@ int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx)
/* Store for processing. */
qdata->pkt = pkt;
/* Check parse state. */
if (pkt->parsed < pkt->size) {
qdata->rcode = KNOT_RCODE_FORMERR;
return NS_PROC_FAIL;
}
/* Declare having response. */
return NS_PROC_FULL;
}
......@@ -119,14 +117,23 @@ int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx)
struct query_data *qdata = QUERY_DATA(ctx);
rcu_read_lock();
/* Check parse state. */
knot_pkt_t *query = qdata->pkt;
int next_state = NS_PROC_FINISH;
if (query->parsed < query->size) {
dbg_ns("%s: query is FORMERR\n", __func__);
qdata->rcode = KNOT_RCODE_FORMERR;
next_state = NS_PROC_FAIL;
goto finish;
}
/* Prepare answer. */
int next_state = NS_PROC_FINISH;
int ret = prepare_answer(qdata->pkt, pkt, ctx);
int ret = prepare_answer(query, pkt, ctx);
if (ret != KNOT_EOK) {
qdata->rcode = KNOT_RCODE_SERVFAIL;
rcu_read_unlock();
return NS_PROC_FAIL;
next_state = NS_PROC_FAIL;
goto finish;
} else {
qdata->rcode = KNOT_RCODE_NOERROR;
}
......@@ -158,6 +165,11 @@ int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx)
}
}
finish:
/* Apply rate limits for positive answers. */
if (ctx->flags & NS_QUERY_RATELIMIT) {
next_state = ratelimit_apply(next_state, pkt, ctx);
}
rcu_read_unlock();
return next_state;
}
......@@ -185,7 +197,11 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
}
/* Transaction security (if applicable). */
return ns_proc_query_sign_response(pkt, qdata);
if (ns_proc_query_sign_response(pkt, qdata) != KNOT_EOK) {
return NS_PROC_FAIL;
}
return NS_PROC_FINISH;
}
bool ns_proc_query_acl_check(acl_t *acl, struct query_data *qdata)
......@@ -361,6 +377,46 @@ static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
return next_state;
}
/*!
* \brief Apply rate limit.
*/
static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx)
{
/* Check if rate limiting applies. */
printf("rate limit\n");
struct query_data *qdata = QUERY_DATA(ctx);
server_t *server = (server_t *)ctx->ns->data;
if (server->rrl == NULL) {
return state;
}
rrl_req_t rrl_rq = {0};
rrl_rq.w = pkt->wire;
rrl_rq.query = qdata->pkt;
rrl_rq.flags = pkt->flags;
if (rrl_query(server->rrl, &qdata->param->query_source,
&rrl_rq, qdata->zone) == KNOT_EOK) {
/* Rate limiting not applied. */
return state;
}
/* Now it is slip or drop. */
if (rrl_slip_roll(conf()->rrl_slip)) {
/* Answer slips. */
printf("answer slipped\n");
if (ns_proc_query_err(pkt, ctx) != KNOT_EOK) {
return NS_PROC_FAIL;
}
knot_wire_set_tc(pkt->wire);
} else {
printf("answer dropped\n");
/* Drop answer. */
pkt->size = 0;
}
return NS_PROC_FINISH;
}
/*!
* \brief Create a response for a given query in the CHAOS class.
*/
......
......@@ -37,8 +37,9 @@ extern const ns_proc_module_t _ns_proc_query;
/* Query processing flags. */
enum ns_proc_query_flag {
NS_QUERY_NO_AXFR = NS_PROCFLAG << 1, /* Don't process AXFR */
NS_QUERY_NO_IXFR = NS_PROCFLAG << 2 /* Don't process IXFR */
NS_QUERY_NO_AXFR = NS_PROCFLAG << 1, /* Don't process AXFR */
NS_QUERY_NO_IXFR = NS_PROCFLAG << 2, /* Don't process IXFR */
NS_QUERY_RATELIMIT = NS_PROCFLAG << 3 /* Apply rate limits. */
};
/* Module load parameters. */
......
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