Skip to content
Snippets Groups Projects
Commit 79f2cd39 authored by Daniel Salzman's avatar Daniel Salzman
Browse files

Merge branch 'rrl-tests' into 'master'

RRL functional tests



See merge request !521
parents 701cccc9 69cf1ac5
No related branches found
No related tags found
No related merge requests found
......@@ -138,7 +138,7 @@ static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls,
rrl_req_t *req, const zone_t *zone)
{
/* Fallback zone (for errors etc.) */
const knot_dname_t *dn = (const knot_dname_t*)"\x00";
const knot_dname_t *dn = (const knot_dname_t *)"\x00";
/* Found associated zone. */
if (zone != NULL) {
......@@ -178,7 +178,7 @@ static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage
uint64_t nb = 0;
if (a->ss_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)a;
nb = *((uint64_t*)(&ipv6->sin6_addr)) & RRL_V6_PREFIX;
nb = *((uint64_t *)(&ipv6->sin6_addr)) & RRL_V6_PREFIX;
} else {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)a;
nb = ((uint32_t)ipv4->sin_addr.s_addr) & RRL_V4_PREFIX;
......@@ -186,11 +186,11 @@ static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage
if (blklen + sizeof(nb) > maxlen) {
return KNOT_ESPACE;
}
memcpy(dst + blklen, (void*)&nb, sizeof(nb));
memcpy(dst + blklen, (void *)&nb, sizeof(nb));
blklen += sizeof(nb);
/* Name */
uint16_t *nlen = (uint16_t*)(dst + blklen);
uint16_t *nlen = (uint16_t *)(dst + blklen);
blklen += sizeof(uint16_t);
int len = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z);
if (len < 0) {
......@@ -203,9 +203,8 @@ static int rrl_classify(char *dst, size_t maxlen, const struct sockaddr_storage
if (blklen + sizeof(seed) > maxlen) {
return KNOT_ESPACE;
}
if (memcpy(dst + blklen, (void*)&seed, sizeof(seed)) == 0) {
blklen += sizeof(seed);
}
memcpy(dst + blklen, (void *)&seed, sizeof(seed));
blklen += sizeof(seed);
return blklen;
}
......@@ -375,7 +374,7 @@ 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,
rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p,
const zone_t *zone, uint32_t stamp, int *lock)
{
char buf[RRL_CLSBLK_MAXLEN];
......@@ -390,11 +389,11 @@ rrl_item_t* rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t
pthread_mutex_lock(&t->ll);
/* Find an exact match in <id, id + HOP_LEN). */
uint16_t *qname = (uint16_t*)(buf + sizeof(uint8_t) + sizeof(uint64_t));
uint16_t *qname = (uint16_t *)(buf + sizeof(uint8_t) + sizeof(uint64_t));
rrl_item_t match = {
0, *((uint64_t*)(buf + 1)), t->rate, /* hop, netblk, ntok */
buf[0], RRL_BF_NULL, /* cls, flags */
hash((char*)(qname + 1), *qname), stamp /* qname, time*/
0, *((uint64_t *)(buf + 1)), t->rate, /* hop, netblk, ntok */
buf[0], RRL_BF_NULL, /* cls, flags */
hash((char *)(qname + 1), *qname), stamp /* qname, time */
};
unsigned d = find_match(t, id, &match);
......@@ -415,7 +414,7 @@ rrl_item_t* rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t
/* found free elm 'k' which is in <id, id + HOP_LEN) */
t->arr[id].hop |= (1 << d);
rrl_item_t* b = t->arr + f;
rrl_item_t *b = t->arr + f;
assert(f == (id+d) % t->size);
/* Inspect bucket state. */
......
......@@ -2,7 +2,10 @@
'''Basic RRL functionality test'''
import dns.name
import dns.exception
import dns.message
import dns.query
import time
from dnstest.test import Test
from dnstest.utils import *
......@@ -10,33 +13,80 @@ from dnstest.utils import *
t = Test(stress=False)
knot = t.server("knot")
zone = t.zone("example.com.")
t.link(zone, knot)
# Enable RRL.
knot.ratelimit = 2
def send_queries(server, run_time=1.0, query_time=0.05):
"""
Send UDP queries to the server for certain time and get replies statistics.
"""
replied, truncated, dropped = 0, 0, 0
start = time.time()
while time.time() < start + run_time:
try:
query = dns.message.make_query("example.com", "SOA", want_dnssec=True)
response = dns.query.udp(query, server.addr, port=server.port, timeout=query_time)
except dns.exception.Timeout:
response = None
t.start()
if response is None:
dropped += 1
elif response.flags & dns.flags.TC:
truncated += 1
else:
replied += 1
return dict(replied=replied, truncated=truncated, dropped=dropped)
def rrl_result(name, stats, success):
detail_log("RRL %s" % name)
detail_log(", ".join(["%s %d" % (s, stats[s]) for s in ["replied", "truncated", "dropped"]]))
if success:
detail_log("success")
else:
detail_log("error")
set_err("RRL ERROR")
t.start()
knot.zone_wait(zone)
t.sleep(1)
tc_bit = False
#
# We cannot send queries in parallel. And we have to give the server some time
# to respond, especially under valgrind. Therefore we have to be tolerant when
# counting responses when packets are being dropped.
#
stats = send_queries(knot)
ok = stats["replied"] >= 100 and stats["truncated"] == 0 and stats["dropped"] == 0
rrl_result("RRL disabled", stats, ok)
knot.ratelimit = 5
knot.gen_confile()
knot.reload()
stats = send_queries(knot)
ok = stats["replied"] > 0 and stats["replied"] < 100 and stats["truncated"] >= 100 and stats["dropped"] == 0
rrl_result("RRL enabled, all slips", stats, ok)
time.sleep(5)
def have_flag(response, flag):
flag_val = dns.flags.from_text(flag)
return (response.resp.flags & flag_val) != 0
knot.ratelimit_slip = 0
knot.gen_confile()
knot.reload()
stats = send_queries(knot)
ok = stats["replied"] > 0 and stats["replied"] < 100 and stats["truncated"] == 0 and stats["dropped"] >= 5
rrl_result("RRL enabled, no slips", stats, ok)
for i in range(20):
resp = knot.dig("example.com", "SOA", udp=True, timeout=0.05, tries=1)
resp.check(rcode="NOERROR")
knot.ratelimit_slip = 2
knot.gen_confile()
knot.reload()
stats = send_queries(knot)
ok = stats["replied"] > 0 and stats["replied"] < 100 and stats["truncated"] >= 5 and stats["dropped"] >= 5
rrl_result("RRL enabled, 50% slips", stats, ok)
# Check for proper flags after and before active RRL.
if i < knot.ratelimit and have_flag(resp, "TC"):
set_err("CHECK NO TC FLAG")
check_log("ERROR: CHECK TC FLAG ABSENCE")
elif i > 10 and not have_flag(resp, "TC"):
set_err("CHECK TC FLAG")
check_log("ERROR: CHECK TC FLAG PRESENCE")
knot.ratelimit_whitelist = knot.addr
knot.gen_confile()
knot.reload()
stats = send_queries(knot)
ok = stats["replied"] >= 100 and stats["truncated"] == 0 and stats["dropped"] == 0
rrl_result("RRL enabled, whitelist effective", stats, ok)
t.end()
......@@ -96,6 +96,8 @@ class Server(object):
self.zones = dict()
self.ratelimit = None
self.ratelimit_slip = None
self.ratelimit_whitelist = None
self.disable_any = None
self.disable_notify = None
self.tcp_reply_timeout = None
......@@ -859,8 +861,12 @@ class Knot(Server):
s.item_str("listen", "%s@%s" % (self.addr, self.port))
if (self.tcp_reply_timeout):
s.item_str("tcp-reply-timeout", self.tcp_reply_timeout)
if (self.ratelimit):
if self.ratelimit is not None:
s.item_str("rate-limit", self.ratelimit)
if self.ratelimit_slip is not None:
s.item_str("rate-limit-slip", self.ratelimit_slip)
if self.ratelimit_whitelist is not None:
s.item_str("rate-limit-whitelist", self.ratelimit_whitelist)
s.end()
s.begin("control")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment