]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/ratelimiting: use slip instead of tc_limit
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Thu, 24 Oct 2024 16:37:00 +0000 (18:37 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 4 Nov 2024 13:39:07 +0000 (14:39 +0100)
daemon/lua/kres-gen-33.lua
daemon/ratelimiting.c
daemon/ratelimiting.h
daemon/ratelimiting.test/tests.inc.c
doc/_static/config.schema.json
python/knot_resolver/datamodel/rate_limiting_schema.py
python/knot_resolver/datamodel/templates/rate_limiting.lua.j2

index 26547de5e56c7e7cb0097b00255e2e1866587fcf..fcd3eb3528ff828da3f8fc10350b5efb44194e3e 100644 (file)
@@ -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[];
index dbb10668851b0b14707529fccde145bd76944205..8d34efbca92b5ffacf5dd6aacb5ef1e99fb1a658 100644 (file)
@@ -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;
 
index 8322772d2d5e6c8daafb10fee05014aef2806be2..def9d2f3f482e3350db404cbd85968f76a3c1f4e 100644 (file)
@@ -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
index 467473c02012a5a69e946755c1662e573a67dbd1..e2427b5f2ea9ee7f94ad4b3722d7c6b3cc88f164 100644 (file)
@@ -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;
index ca031d6023c4e509c4de8c015df6d42f647ff185..8ecbbbd4399bbf38ad7892bd3ca604d8acdf7ee0 100644 (file)
                     "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
index 109dede696cd80f607da01e5d81cd38b0745ac23..3030312de3a08f08b886ba5363df1d0304a33d17 100644 (file)
@@ -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")
index 096c7f3c2d7f8915adfbb31958e00f57e2d829b0..c25b5bb1a659edec5280e2bf2523cbd817d89050 100644 (file)
@@ -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 %}