]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
rrl: configurable limits in yaml, deinit docs-develop-rrl-8r8r8r/deployments/3624
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 27 Mar 2024 10:45:30 +0000 (11:45 +0100)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 27 Mar 2024 18:08:44 +0000 (19:08 +0100)
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/lua/kres-gen-32.lua
daemon/lua/kres-gen.sh
daemon/main.c
daemon/rrl/api.c
daemon/rrl/api.h
manager/knot_resolver_manager/datamodel/config_schema.py
manager/knot_resolver_manager/datamodel/rate_limiting_schema.py [new file with mode: 0644]
manager/knot_resolver_manager/datamodel/templates/config.lua.j2
manager/knot_resolver_manager/datamodel/templates/rate_limiting.lua.j2 [new file with mode: 0644]

index 65c6ac5ac723a26829212f52976e55431a9f3f05..937453e92f9c6b911c1baafb9b9d57cd1b402673 100644 (file)
@@ -580,6 +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);
 struct engine {
        char _stub[];
 };
index cb96adf1207cf7323c4fb20c5137b2ad9df24ebc..a8e1793830f42f676eb595b31958bdb9eb1bc322 100644 (file)
@@ -580,6 +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);
 struct engine {
        char _stub[];
 };
index 7a416079cdf72d40dc8e1ca0a906a48ca36c3b89..54be1b0bbbeb9230b5e18706ca76584e7caeda98 100644 (file)
@@ -581,6 +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);
 struct engine {
        char _stub[];
 };
index 752944979cbe18884e1ae4e3f671c565bbbc5e7e..8860ef401ee3404b6c593483f37da428e1b34f3c 100755 (executable)
@@ -335,6 +335,7 @@ ${CDEFS} ${KRESD} functions <<-EOF
        worker_resolve_start
        zi_zone_import
        kr_rrl_request_begin
+       kr_rrl_initialize
 EOF
 
 echo "struct engine" | ${CDEFS} ${KRESD} types | sed '/module_array_t/,$ d'
index 53ecb3e8f93cd74c905d654c7c86b16bb0d32c14..1f65ea77a1faf2e76c8f0e6136526112796c59c8 100644 (file)
@@ -10,6 +10,7 @@
 #include "daemon/network.h"
 #include "daemon/udp_queue.h"
 #include "daemon/worker.h"
+#include "daemon/rrl/api.h"
 
 #ifdef ENABLE_DOH2
 #include "daemon/http.h"
@@ -647,6 +648,7 @@ int main(int argc, char **argv)
 cleanup:/* Cleanup. */
        network_unregister();
 
+       kr_rrl_deinit();
        kr_resolver_deinit();
        worker_deinit();
        engine_deinit();
index 19c2f1779275e4413c80a81836612f8d10fc6341..d6b332933f937c90fdb56cfbb8e6a94eb3bc4a6b 100644 (file)
@@ -26,11 +26,17 @@ struct rrl {
        uint8_t kru[] ALIGNED(64);
 };
 struct rrl *the_rrl = NULL;
+int the_rrl_fd = -1;
+char *the_rrl_mmap_file = NULL;
 
-void kr_rrl_init(char *mmap_path, size_t capacity, uint32_t instant_limit, uint32_t rate_limit) {
-       int fd = open(mmap_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+void kr_rrl_init(const char *mmap_file, size_t capacity, uint32_t instant_limit, uint32_t rate_limit)
+{
+       int fd = the_rrl_fd = open(mmap_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        kr_require(fd != -1);
 
+       the_rrl_mmap_file = malloc(strlen(mmap_file) + 1);
+       strcpy(the_rrl_mmap_file, mmap_file);
+
        size_t capacity_log = 0;
        for (size_t c = capacity - 1; c > 0; c >>= 1) capacity_log++;
 
@@ -99,11 +105,37 @@ void kr_rrl_init(char *mmap_path, size_t capacity, uint32_t instant_limit, uint3
        kr_require(false);  // we can get here for example if signal interrupt is received during fcntl
 }
 
-bool kr_rrl_request_begin(struct kr_request *req)
+void kr_rrl_deinit(void)
 {
-       if (!the_rrl) {
-               kr_rrl_init("/tmp/kresd-kru", 524288, 20, 5);  // TODO call from elsewhere
+       if (the_rrl == NULL) return;
+       int fd = the_rrl_fd;
+
+       struct flock fl = {
+               .l_type   = F_UNLCK,
+               .l_whence = SEEK_SET,
+               .l_start  = 0,
+               .l_len    = 0 };
+       fcntl(fd, F_SETLK, &fl);  // unlock
+
+       fl.l_type = F_WRLCK;
+       if (fcntl(fd, F_SETLK, &fl) != -1) {
+
+               /* If the RRL configuration is updated at runtime, manager should remove the file (TODO)
+                * and keep new processes create it again while old processes are still using the old data.
+                * Here we keep zero-size file not to accidentally remove the new file instead of the old one.
+                * Still truncating the file will cause currently starting processes waiting for read lock on the same file to fail,
+                * but this is not expected to happen. */
+               ftruncate(fd, 0);
+
+               fl.l_type = F_UNLCK;
+               fcntl(fd, F_SETLK, &fl);
        }
+
+       the_rrl = NULL;
+}
+
+bool kr_rrl_request_begin(struct kr_request *req)
+{
        if (!req->qsource.addr)
                return false;  // don't consider internal requests
        bool limited = false;
index 0d155d0999a23c70d154dbdd040f1129eb3b0e8e..8553c4bdc292f24414858a4e1d5b4cd41c4e03b0 100644 (file)
@@ -3,6 +3,16 @@
 #include <lib/defines.h>
 struct kr_request;
 
+/** Initialize rate-limiting with shared mmapped memory.
+ * 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);
+
 /** Do rate-limiting, during knot_layer_api::begin. */
 KR_EXPORT
 bool kr_rrl_request_begin(struct kr_request *req);
+
+/** Remove mmapped file data if not used by other processes. */
+KR_EXPORT
+void kr_rrl_deinit(void);
index 8aa99446c482b57dd283dbecc7c01d601ced3423..9da7007eba369563ced99a6b63c01f9171d682cc 100644 (file)
@@ -21,6 +21,7 @@ from knot_resolver_manager.datamodel.templates import MAIN_TEMPLATE
 from knot_resolver_manager.datamodel.types import Dir, EscapedStr, IntPositive
 from knot_resolver_manager.datamodel.view_schema import ViewSchema
 from knot_resolver_manager.datamodel.webmgmt_schema import WebmgmtSchema
+from knot_resolver_manager.datamodel.rate_limiting_schema import RateLimitingSchema
 from knot_resolver_manager.utils.modeling import ConfigSchema
 from knot_resolver_manager.utils.modeling.base_schema import lazy_default
 
@@ -74,6 +75,7 @@ class KresConfig(ConfigSchema):
         logging: Logging and debugging configuration.
         monitoring: Metrics exposisition configuration (Prometheus, Graphite)
         lua: Custom Lua configuration.
+        rate_limiting: ... TODO
         """
 
         version: int = 1
@@ -94,6 +96,7 @@ class KresConfig(ConfigSchema):
         dns64: Union[bool, Dns64Schema] = False
         logging: LoggingSchema = LoggingSchema()
         monitoring: MonitoringSchema = MonitoringSchema()
+        rate_limiting: Optional[RateLimitingSchema] = None
         lua: LuaSchema = LuaSchema()
 
     _LAYER = Raw
@@ -115,6 +118,7 @@ class KresConfig(ConfigSchema):
     dns64: Union[Literal[False], Dns64Schema]
     logging: LoggingSchema
     monitoring: MonitoringSchema
+    rate_limiting: Optional[RateLimitingSchema]
     lua: LuaSchema
 
     def _hostname(self, obj: Raw) -> Any:
diff --git a/manager/knot_resolver_manager/datamodel/rate_limiting_schema.py b/manager/knot_resolver_manager/datamodel/rate_limiting_schema.py
new file mode 100644 (file)
index 0000000..c38727f
--- /dev/null
@@ -0,0 +1,26 @@
+#from typing_extensions import Literal
+
+from knot_resolver_manager.utils.modeling import ConfigSchema
+
+class RateLimitingSchema(ConfigSchema):
+    """
+    Configuration of rate limiting.
+
+    ---
+    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.
+    """
+
+    capacity: int = 524288
+    rate_limit: int
+    instant_limit: int = 50
+
+    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}")
+        if not 1 <= self.rate_limit <= 1000 * self.instant_limit:
+            raise ValueError(f"'rate-limit' should be in range 1..(1000 * instant-limit)")
+        if not 0 < self.capacity:
+            raise ValueError(f"'capacity' should be positive")
index 856dcf39963c72d5664b0ed6badaf259537e0756..fc82393618d4772c99acf8c667b4c3702fd8efab 100644 (file)
@@ -46,6 +46,9 @@ nsid.name('{{ cfg.nsid }}' .. worker.id)
 -- DNS64 section ------------------------------------
 {% include "dns64.lua.j2" %}
 
+-- RATE-LIMITING section ------------------------------------
+{% include "rate_limiting.lua.j2" %}
+
 {% endif %}
 
 -- LUA section --------------------------------------
diff --git a/manager/knot_resolver_manager/datamodel/templates/rate_limiting.lua.j2 b/manager/knot_resolver_manager/datamodel/templates/rate_limiting.lua.j2
new file mode 100644 (file)
index 0000000..6622d1d
--- /dev/null
@@ -0,0 +1,12 @@
+{% from 'macros/common_macros.lua.j2' import boolean %}
+
+modules = { 'hints > iterate' }
+
+{% if cfg.rate_limiting.rate_limit -%}
+local ffi = require('ffi')
+ffi.C.kr_rrl_init(
+       '{{ cfg.rundir }}/rrl',
+       {{ cfg.rate_limiting.capacity }},
+       {{ cfg.rate_limiting.instant_limit }},
+       {{ cfg.rate_limiting.rate_limit }})
+{%- endif %}