]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
rrl: truncating answers when close to limit, dropping over limit docs-develop-rrl-8r8r8r/deployments/4008
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Mon, 6 May 2024 15:32:45 +0000 (17:32 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Mon, 6 May 2024 15:32:45 +0000 (17:32 +0200)
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/lua/kres-gen-32.lua
daemon/rrl/api.c
daemon/rrl/api.h
daemon/rrl/tests.inc.c
manager/knot_resolver_manager/datamodel/rate_limiting_schema.py
manager/knot_resolver_manager/datamodel/templates/rate_limiting.lua.j2

index 937453e92f9c6b911c1baafb9b9d57cd1b402673..9401279e824e02923f13be1432493c74e5011177 100644 (file)
@@ -580,7 +580,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 kr_rrl_request_begin(struct kr_request *);
-void kr_rrl_init(const char *, size_t, uint32_t, uint32_t);
+void kr_rrl_init(const char *, size_t, uint32_t, uint32_t, int);
 struct engine {
        char _stub[];
 };
index a8e1793830f42f676eb595b31958bdb9eb1bc322..a8b14626c1903d1c2b869ecd13effe2e495c089a 100644 (file)
@@ -580,7 +580,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 kr_rrl_request_begin(struct kr_request *);
-void kr_rrl_init(const char *, size_t, uint32_t, uint32_t);
+void kr_rrl_init(const char *, size_t, uint32_t, uint32_t, int);
 struct engine {
        char _stub[];
 };
index 54be1b0bbbeb9230b5e18706ca76584e7caeda98..7cbf817a3b666ddd843f62c44caf51773557ecd3 100644 (file)
@@ -581,7 +581,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 kr_rrl_request_begin(struct kr_request *);
-void kr_rrl_init(const char *, size_t, uint32_t, uint32_t);
+void kr_rrl_init(const char *, size_t, uint32_t, uint32_t, int);
 struct engine {
        char _stub[];
 };
index a3396661548c6810dac76e99cc361a3c45408def..5aa729e61fc34fc09f280325085a91a7d22f1f34 100644 (file)
@@ -21,6 +21,7 @@ struct rrl {
        size_t capacity;
        uint32_t instant_limit;
        uint32_t rate_limit;
+       uint16_t tc_limit;
        bool using_avx2;
        kru_price_t v4_prices[RRL_V4_PREFIXES_CNT];
        kru_price_t v6_prices[RRL_V6_PREFIXES_CNT];
@@ -38,7 +39,7 @@ static bool using_avx2(void)
        return result;
 }
 
-void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit)
+void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, int tc_limit_perc)
 {
        int fd = the_rrl_fd = open(mmap_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd == -1) {
@@ -55,6 +56,8 @@ void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit,
 
        size_t size = offsetof(struct rrl, kru) + KRU.get_size(capacity_log);
 
+       uint16_t tc_limit = (tc_limit_perc == 100 ? -1 : ((uint32_t)tc_limit_perc << 16) / 100);
+
        // try to acquire write lock; initialize KRU on success
        struct flock fl = {
                .l_type   = F_WRLCK,
@@ -74,6 +77,7 @@ void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit,
                the_rrl->capacity = capacity;
                the_rrl->instant_limit = instant_limit;
                the_rrl->rate_limit = rate_limit;
+               the_rrl->tc_limit = tc_limit;
                the_rrl->using_avx2 = using_avx2();
 
                const kru_price_t base_price = KRU_LIMIT / instant_limit;
@@ -110,7 +114,7 @@ void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit,
                the_rrl = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
                kr_require(the_rrl != MAP_FAILED);
                if ((the_rrl->capacity != capacity) || (the_rrl->instant_limit != instant_limit) ||
-                               (the_rrl->rate_limit != rate_limit)) goto check_fail;
+                               (the_rrl->rate_limit != rate_limit) || (the_rrl->tc_limit != tc_limit)) goto check_fail;
                if (using_avx2() != the_rrl->using_avx2) goto check_fail;
                kr_log_info(SYSTEM, "Using existing RRL data.\n");
 
@@ -157,40 +161,52 @@ bool kr_rrl_request_begin(struct kr_request *req)
 {
        if (!req->qsource.addr)
                return false;  // don't consider internal requests
-       bool limited = false;
+       uint8_t limited = 0;  // 0: not limited, 1: truncated, 2: no answer
+       uint16_t max_final_load = 0;
        if (the_rrl) {
                uint8_t key[16] ALIGNED(16) = {0, };
                uint8_t limited_prefix;
-               // uint16_t max_final_load = 0;  // TODO use for query ordering and/or soft limit with TC=1
                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 *)the_rrl->kru, kr_now(),
-                                       1, key, RRL_V6_PREFIXES, the_rrl->v6_prices, RRL_V6_PREFIXES_CNT, /* &max_final_load */ NULL);
+                                       1, key, RRL_V6_PREFIXES, the_rrl->v6_prices, RRL_V6_PREFIXES_CNT, &max_final_load);
                } 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 *)the_rrl->kru, kr_now(),
-                                       0, key, RRL_V4_PREFIXES, the_rrl->v4_prices, RRL_V4_PREFIXES_CNT, /* &max_final_load */ NULL);
+                                       0, key, RRL_V4_PREFIXES, the_rrl->v4_prices, RRL_V4_PREFIXES_CNT, &max_final_load);
                }
-               limited = limited_prefix;
+               limited = (limited_prefix ? 2 : (max_final_load > the_rrl->tc_limit ? 1 : 0));
        }
-       if (!limited) return limited;
+       if (!limited) return false;
 
        knot_pkt_t *answer = kr_request_ensure_answer(req);
        if (!answer) { // something bad; TODO: perhaps improve recovery from this
                kr_assert(false);
-               return limited;
+               return true;
        }
        // at this point the packet should be pretty clear
 
-       // Example limiting: REFUSED.
-       knot_wire_set_rcode(answer->wire, KNOT_RCODE_REFUSED);
-       kr_request_set_extended_error(req, KNOT_EDNS_EDE_OTHER, "YRAA: rate-limited");
-
-       req->state = KR_STATE_DONE;
+       if (limited == 1) {
+               // TC=1.
+               knot_wire_set_tc(answer->wire);
+               knot_wire_clear_ad(answer->wire);
+               req->state = KR_STATE_DONE;
+       } else {
+               /*
+               // Example limiting: REFUSED.
+               knot_wire_set_rcode(answer->wire, KNOT_RCODE_REFUSED);
+               kr_request_set_extended_error(req, KNOT_EDNS_EDE_OTHER, "YRAA: rate-limited");
+               req->state = KR_STATE_DONE;
+               */
+
+               // no answer
+               req->options.NO_ANSWER = true;
+               req->state = KR_STATE_FAIL;
+       }
 
-       return limited;
+       return true;
 }
index 8553c4bdc292f24414858a4e1d5b4cd41c4e03b0..d6f5841ed7467b4bcdf5357accd92476b7da93d9 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
-void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit);
+void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit, int tc_limit_perc);
 
 /** Do rate-limiting, during knot_layer_api::begin. */
 KR_EXPORT
index ec21b15b01365f353bb201a39298673dcb38b8b8..a352d94c04b93a933aee9432206ddf306e3fc08e 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");
-       kr_rrl_init(mmap_file, RRL_TABLE_SIZE, RRL_INSTANT_LIMIT, RRL_RATE_LIMIT);
+       kr_rrl_init(mmap_file, RRL_TABLE_SIZE, RRL_INSTANT_LIMIT, RRL_RATE_LIMIT, 100);
 
        if (KRU.initialize == KRU_GENERIC.initialize) {
                struct kru_generic *kru = (struct kru_generic *) the_rrl->kru;
index 20706aa5036f543937dbf1949935d6f44420dfe9..f2b99f4418d1ebadccaa36d32919d1524bdc4718 100644 (file)
@@ -1,4 +1,5 @@
 from knot_resolver_manager.utils.modeling import ConfigSchema
+from knot_resolver_manager.datamodel.types import Percent
 
 
 class RateLimitingSchema(ConfigSchema):
@@ -9,11 +10,13 @@ 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.
     """
 
     capacity: int = 524288
     rate_limit: int
     instant_limit: int = 50
+    tc_limit_perc: Percent = Percent(90);
 
     def _validate(self) -> None:
         max_instant_limit = int(2**32 / 768 - 1)
index cb83045faaa6099fde5792d869c750500bebe643..552a9dcb2d1ac6886efbfca6b3266f233af2fc53 100644 (file)
@@ -5,5 +5,6 @@ C.kr_rrl_init(
        '{{ cfg.rundir }}/rrl',
        {{ cfg.rate_limiting.capacity }},
        {{ cfg.rate_limiting.instant_limit }},
-       {{ cfg.rate_limiting.rate_limit }})
+       {{ cfg.rate_limiting.rate_limit }},
+       {{ cfg.rate_limiting.tc_limit_perc }})
 {%- endif %}