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[];
};
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[];
};
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[];
};
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'
#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"
cleanup:/* Cleanup. */
network_unregister();
+ kr_rrl_deinit();
kr_resolver_deinit();
worker_deinit();
engine_deinit();
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++;
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;
#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);
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
logging: Logging and debugging configuration.
monitoring: Metrics exposisition configuration (Prometheus, Graphite)
lua: Custom Lua configuration.
+ rate_limiting: ... TODO
"""
version: int = 1
dns64: Union[bool, Dns64Schema] = False
logging: LoggingSchema = LoggingSchema()
monitoring: MonitoringSchema = MonitoringSchema()
+ rate_limiting: Optional[RateLimitingSchema] = None
lua: LuaSchema = LuaSchema()
_LAYER = Raw
dns64: Union[Literal[False], Dns64Schema]
logging: LoggingSchema
monitoring: MonitoringSchema
+ rate_limiting: Optional[RateLimitingSchema]
lua: LuaSchema
def _hostname(self, obj: Raw) -> Any:
--- /dev/null
+#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")
-- DNS64 section ------------------------------------
{% include "dns64.lua.j2" %}
+-- RATE-LIMITING section ------------------------------------
+{% include "rate_limiting.lua.j2" %}
+
{% endif %}
-- LUA section --------------------------------------
--- /dev/null
+{% 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 %}