From: Lukáš Ondráček Date: Thu, 24 Oct 2024 16:37:00 +0000 (+0200) Subject: daemon/ratelimiting: use slip instead of tc_limit X-Git-Tag: v6.0.9~1^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=960739fc892591043911cc38a55ddd35156ca730;p=thirdparty%2Fknot-resolver.git daemon/ratelimiting: use slip instead of tc_limit --- diff --git a/daemon/lua/kres-gen-33.lua b/daemon/lua/kres-gen-33.lua index 26547de5e..fcd3eb352 100644 --- a/daemon/lua/kres-gen-33.lua +++ b/daemon/lua/kres-gen-33.lua @@ -616,7 +616,7 @@ knot_pkt_t *worker_resolve_mk_pkt(const char *, uint16_t, uint16_t, const struct struct qr_task *worker_resolve_start(knot_pkt_t *, struct kr_qflags); int zi_zone_import(const zi_config_t); _Bool ratelimiting_request_begin(struct kr_request *); -int ratelimiting_init(const char *, size_t, uint32_t, uint32_t, int); +int ratelimiting_init(const char *, size_t, uint32_t, uint32_t, uint16_t); int defer_init(const char *, int); struct engine { char _stub[]; diff --git a/daemon/ratelimiting.c b/daemon/ratelimiting.c index dbb106688..8d34efbca 100644 --- a/daemon/ratelimiting.c +++ b/daemon/ratelimiting.c @@ -18,7 +18,7 @@ struct ratelimiting { size_t capacity; uint32_t instant_limit; uint32_t rate_limit; - uint16_t tc_limit; + uint16_t slip; bool using_avx2; kru_price_t v4_prices[V4_PREFIXES_CNT]; kru_price_t v6_prices[V6_PREFIXES_CNT]; @@ -35,7 +35,7 @@ static bool using_avx2(void) return result; } -int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, int tc_limit_perc) +int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, uint16_t slip) { size_t capacity_log = 0; @@ -47,7 +47,7 @@ int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_l .capacity = capacity, .instant_limit = instant_limit, .rate_limit = rate_limit, - .tc_limit = (tc_limit_perc == 100 ? -1 : ((uint32_t)tc_limit_perc << 16) / 100), + .slip = slip, .using_avx2 = using_avx2() }; @@ -56,7 +56,7 @@ int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_l sizeof(header.capacity) + sizeof(header.instant_limit) + sizeof(header.rate_limit) + - sizeof(header.tc_limit) + + sizeof(header.slip) + sizeof(header.using_avx2)); // no undefined padding inside int ret = mmapped_init(&ratelimiting_mmapped, mmap_file, size, &header, header_size); @@ -121,21 +121,25 @@ bool ratelimiting_request_begin(struct kr_request *req) if (ratelimiting) { _Alignas(16) uint8_t key[16] = {0, }; uint8_t limited_prefix; - uint16_t max_final_load = 0; if (req->qsource.addr->sa_family == AF_INET6) { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)req->qsource.addr; memcpy(key, &ipv6->sin6_addr, 16); limited_prefix = KRU.limited_multi_prefix_or((struct kru *)ratelimiting->kru, kr_now(), - 1, key, V6_PREFIXES, ratelimiting->v6_prices, V6_PREFIXES_CNT, &max_final_load); + 1, key, V6_PREFIXES, ratelimiting->v6_prices, V6_PREFIXES_CNT, NULL); } else { struct sockaddr_in *ipv4 = (struct sockaddr_in *)req->qsource.addr; memcpy(key, &ipv4->sin_addr, 4); // TODO append port? limited_prefix = KRU.limited_multi_prefix_or((struct kru *)ratelimiting->kru, kr_now(), - 0, key, V4_PREFIXES, ratelimiting->v4_prices, V4_PREFIXES_CNT, &max_final_load); + 0, key, V4_PREFIXES, ratelimiting->v4_prices, V4_PREFIXES_CNT, NULL); + } + if (limited_prefix) { + limited = + (ratelimiting->slip > 1) ? + ((kr_rand_bytes(1) % ratelimiting->slip == 0) ? 1 : 2) : + ((ratelimiting->slip == 1) ? 1 : 2); } - limited = (limited_prefix ? 2 : (max_final_load > ratelimiting->tc_limit ? 1 : 0)); } if (!limited) return false; diff --git a/daemon/ratelimiting.h b/daemon/ratelimiting.h index 8322772d2..def9d2f3f 100644 --- a/daemon/ratelimiting.h +++ b/daemon/ratelimiting.h @@ -7,7 +7,7 @@ struct kr_request; * The existing data are used if another instance is already using the file * and it was initialized with the same parameters; it fails on mismatch. */ KR_EXPORT -int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, int tc_limit_perc); +int ratelimiting_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, uint16_t slip); /** Do rate-limiting, during knot_layer_api::begin. */ KR_EXPORT diff --git a/daemon/ratelimiting.test/tests.inc.c b/daemon/ratelimiting.test/tests.inc.c index 467473c02..e2427b5f2 100644 --- a/daemon/ratelimiting.test/tests.inc.c +++ b/daemon/ratelimiting.test/tests.inc.c @@ -95,7 +95,7 @@ static void test_rrl(void **state) { const char *tmpdir = test_tmpdir_create(); char mmap_file[64]; stpcpy(stpcpy(mmap_file, tmpdir), "/rrl"); - ratelimiting_init(mmap_file, RRL_TABLE_SIZE, RRL_INSTANT_LIMIT, RRL_RATE_LIMIT, 100); + ratelimiting_init(mmap_file, RRL_TABLE_SIZE, RRL_INSTANT_LIMIT, RRL_RATE_LIMIT, 0); if (KRU.initialize == KRU_GENERIC.initialize) { struct kru_generic *kru = (struct kru_generic *) ratelimiting->kru; diff --git a/doc/_static/config.schema.json b/doc/_static/config.schema.json index ca031d602..8ecbbbd43 100644 --- a/doc/_static/config.schema.json +++ b/doc/_static/config.schema.json @@ -1689,12 +1689,10 @@ "description": "Number of allowed queries at a single point in time from a single host.", "default": 50 }, - "tc-limit-perc": { + "slip": { "type": "integer", - "minimum": 0, - "maximum": 100, - "description": "Percent of rate/instant limit from which responses are sent as truncated.", - "default": 90 + "description": "Number of restricted responses out of which one is sent as truncated, the others are dropped.", + "default": 2 } }, "default": null diff --git a/python/knot_resolver/datamodel/rate_limiting_schema.py b/python/knot_resolver/datamodel/rate_limiting_schema.py index 109dede69..3030312de 100644 --- a/python/knot_resolver/datamodel/rate_limiting_schema.py +++ b/python/knot_resolver/datamodel/rate_limiting_schema.py @@ -1,4 +1,3 @@ -from knot_resolver.datamodel.types import Percent from knot_resolver.utils.modeling import ConfigSchema @@ -10,19 +9,21 @@ class RateLimitingSchema(ConfigSchema): capacity: Expected maximal number of blocked networks/hosts at the same time. rate_limit: Number of allowed queries per second from a single host. instant_limit: Number of allowed queries at a single point in time from a single host. - tc_limit_perc: Percent of rate/instant limit from which responses are sent as truncated. + slip: Number of restricted responses out of which one is sent as truncated, the others are dropped. """ capacity: int = 524288 rate_limit: int instant_limit: int = 50 - tc_limit_perc: Percent = Percent(90) + slip: int = 2 def _validate(self) -> None: max_instant_limit = int(2**32 / 768 - 1) if not 1 <= self.instant_limit <= max_instant_limit: - raise ValueError(f"'instant-limit' should be in range 1..{max_instant_limit}") + raise ValueError(f"'instant-limit' has to be in range 1..{max_instant_limit}") if not 1 <= self.rate_limit <= 1000 * self.instant_limit: - raise ValueError("'rate-limit' should be in range 1..(1000 * instant-limit)") - if self.capacity <= 0: - raise ValueError("'capacity' should be positive") + raise ValueError("'rate-limit' has to be in range 1..(1000 * instant-limit)") + if not 0 < self.capacity: + raise ValueError("'capacity' has to be positive") + if not 0 <= self.slip <= 100: + raise ValueError("'slip' has to be in range 0..100") diff --git a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 index 096c7f3c2..c25b5bb1a 100644 --- a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 +++ b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 @@ -6,5 +6,5 @@ assert(C.ratelimiting_init( {{ cfg.rate_limiting.capacity }}, {{ cfg.rate_limiting.instant_limit }}, {{ cfg.rate_limiting.rate_limit }}, - {{ cfg.rate_limiting.tc_limit_perc }}) == 0) + {{ cfg.rate_limiting.slip }}) == 0) {%- endif %}