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[];
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];
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;
.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()
};
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);
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;
* 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
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;
"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
-from knot_resolver.datamodel.types import Percent
from knot_resolver.utils.modeling import 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")
{{ 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 %}