From: Lukáš Ondráček Date: Wed, 10 Apr 2024 11:08:10 +0000 (+0200) Subject: rrl: disable parallel tests under valgrind in CI X-Git-Tag: v6.0.9~1^2~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9e282ca92edee6aeded671469568901b5b3f943b;p=thirdparty%2Fknot-resolver.git rrl: disable parallel tests under valgrind in CI --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5abec54b..bbb982699 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -349,8 +349,18 @@ respdiff:basic: test:valgrind: <<: *test_flaky script: - - ${MESON_TEST} --suite unit --suite config --no-suite snowflake --wrap="valgrind --leak-check=full --trace-children=yes --quiet --suppressions=/lj.supp" - - MESON_TESTTHREADS=1 ${MESON_TEST} --wrap="valgrind --leak-check=full --trace-children=yes --quiet --suppressions=/lj.supp" --suite snowflake + - > + ${MESON_TEST} + --suite unit + --suite config + --no-suite skip_valgrind + --no-suite snowflake + --wrap="valgrind --leak-check=full --trace-children=yes --quiet --suppressions=/lj.supp" + - > + MESON_TESTTHREADS=1 ${MESON_TEST} + --no-suite skip_valgrind + --wrap="valgrind --leak-check=full --trace-children=yes --quiet --suppressions=/lj.supp" + --suite snowflake manager: stage: test diff --git a/daemon/rrl/meson.build b/daemon/rrl/meson.build index 9f2275a24..8a815d70d 100644 --- a/daemon/rrl/meson.build +++ b/daemon/rrl/meson.build @@ -14,5 +14,8 @@ kresd_deps += [ ] unit_tests += [ - ['rrl', files('tests.c', 'kru-generic.c', 'kru-avx2.c', '../../contrib/openbsd/siphash.c') + libkres_src ] + ['rrl', files('tests.c', 'kru-generic.c', 'kru-avx2.c', '../../contrib/openbsd/siphash.c') + libkres_src ], + + # parallel tests timeouts under valgrind; they checks mainly for race conditions, which is not needed there + ['rrl-parallel', files('tests-parallel.c', 'kru-generic.c', 'kru-avx2.c', '../../contrib/openbsd/siphash.c') + libkres_src, ['skip_valgrind']] ] diff --git a/daemon/rrl/tests-parallel.c b/daemon/rrl/tests-parallel.c new file mode 100644 index 000000000..934ef5f94 --- /dev/null +++ b/daemon/rrl/tests-parallel.c @@ -0,0 +1,175 @@ +/* Copyright (C) 2024 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +static void the_tests(void **state); + +#include "daemon/rrl/tests.inc.c" + +#define THREADS 4 + +#define BATCH_QUERIES_LOG 3 // threads acquire queries in batches of 8 +#define HOSTS_LOG 3 // at most 6 attackers + 2 wildcard addresses for normal users +#define TICK_QUERIES_LOG 13 // at most 1024 queries per host per tick + +// Expected range of limits for parallel test. +#define RANGE_INST(Vx, prefix) INST(Vx, prefix) - 1, INST(Vx, prefix) + THREADS - 1 +#define RANGE_RATEM(Vx, prefix) RATEM(Vx, prefix) - 1, RATEM(Vx, prefix) +#define RANGE_UNLIM(queries) queries, queries + +struct host { + uint32_t queries_per_tick; + int addr_family; + char *addr_format; + uint32_t min_passed, max_passed; + _Atomic uint32_t passed; +}; + +struct stage { + uint32_t first_tick, last_tick; + struct host hosts[1 << HOSTS_LOG]; +}; + +struct runnable_data { + int prime; + _Atomic uint32_t *queries_acquired, *queries_done; + struct stage *stages; +}; + + +static void *runnable(void *arg) +{ + struct runnable_data *d = (struct runnable_data *)arg; + size_t si = 0; + + char addr_str[40]; + struct sockaddr_storage addr; + + uint8_t wire[KNOT_WIRE_MIN_PKTSIZE] = { 0 }; + knot_pkt_t answer = { .wire = wire }; + struct kr_request req = { + .qsource.addr = (struct sockaddr *) &addr, + .answer = &answer + }; + + while (true) { + uint32_t qi1 = atomic_fetch_add(d->queries_acquired, 1 << BATCH_QUERIES_LOG); + + /* increment time if needed; sync on incrementing using spinlock */ + uint32_t tick = qi1 >> TICK_QUERIES_LOG; + for (size_t i = 1; tick != fakeclock_tick; i++) { + if ((*d->queries_done >> TICK_QUERIES_LOG) >= tick) { + fakeclock_tick = tick; + } + if (i % (1<<14) == 0) sched_yield(); + __sync_synchronize(); + } + + /* increment stage if needed */ + while (tick > d->stages[si].last_tick) { + ++si; + if (!d->stages[si].first_tick) return NULL; + } + + if (tick >= d->stages[si].first_tick) { + uint32_t qi2 = 0; + do { + uint32_t qi = qi1 + qi2; + + /* perform query qi */ + uint32_t hi = qi % (1 << HOSTS_LOG); + if (!d->stages[si].hosts[hi].queries_per_tick) continue; + uint32_t hqi = (qi % (1 << TICK_QUERIES_LOG)) >> HOSTS_LOG; // host query index within tick + if (hqi >= d->stages[si].hosts[hi].queries_per_tick) continue; + hqi += (qi >> TICK_QUERIES_LOG) * d->stages[si].hosts[hi].queries_per_tick; // across ticks + (void)snprintf(addr_str, sizeof(addr_str), d->stages[si].hosts[hi].addr_format, + hqi % 0xff, (hqi >> 8) % 0xff, (hqi >> 16) % 0xff); + kr_straddr_socket_set((struct sockaddr *)&addr, addr_str, 0); + + if (!kr_rrl_request_begin(&req)) { + atomic_fetch_add(&d->stages[si].hosts[hi].passed, 1); + } + + } while ((qi2 = (qi2 + d->prime) % (1 << BATCH_QUERIES_LOG))); + } + atomic_fetch_add(d->queries_done, 1 << BATCH_QUERIES_LOG); + } +} + + +static void the_tests(void **state) +{ + /* parallel tests */ + struct stage stages[] = { + /* first tick, last tick, hosts */ + {32, 32, { + /* queries per tick, family, address, min passed, max passed */ + {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET, "3.3.3.3", RANGE_INST ( V4, 32 )}, + { 512, AF_INET, "4.4.4.4", RANGE_INST ( V4, 32 )}, + {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET6, "3333::3333", RANGE_INST ( V6, 128 )}, + { 512, AF_INET6, "4444::4444", RANGE_INST ( V6, 128 )} + }}, + {33, 255, { + {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, + { 512, AF_INET, "4.4.4.4", RANGE_RATEM ( V4, 32 )}, + {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )}, + { 512, AF_INET6, "4444::4444", RANGE_RATEM ( V6, 128 )}, + }}, + {256, 511, { + {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, + {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )} + }}, + {512, 512, { + {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, + { 512, AF_INET, "4.4.4.4", RANGE_INST ( V4, 32 )}, + {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, + {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )}, + { 512, AF_INET6, "4444::4444", RANGE_INST ( V6, 128 )} + }}, + {0} + }; + + pthread_t thr[THREADS]; + struct runnable_data rd[THREADS]; + _Atomic uint32_t queries_acquired = 0, queries_done = 0; + int primes[] = {3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61}; + assert(sizeof(primes)/sizeof(*primes) >= THREADS); + + for (unsigned i = 0; i < THREADS; ++i) { + rd[i].queries_acquired = &queries_acquired; + rd[i].queries_done = &queries_done; + rd[i].prime = primes[i]; + rd[i].stages = stages; + pthread_create(thr + i, NULL, &runnable, rd + i); + } + for (unsigned i = 0; i < THREADS; ++i) { + pthread_join(thr[i], NULL); + } + + unsigned si = 0; + do { + struct host * const h = stages[si].hosts; + uint32_t ticks = stages[si].last_tick - stages[si].first_tick + 1; + for (size_t i = 0; h[i].queries_per_tick; i++) { + assert_int_between(h[i].passed, ticks * h[i].min_passed, ticks * h[i].max_passed, + "parallel stage %d, addr %-25s", si, h[i].addr_format); + } + } while (stages[++si].first_tick); +} diff --git a/daemon/rrl/tests.c b/daemon/rrl/tests.c index 048107f19..abccaafa7 100644 --- a/daemon/rrl/tests.c +++ b/daemon/rrl/tests.c @@ -14,181 +14,11 @@ along with this program. If not, see . */ -#include -#include -#include -#include +static void the_tests(void **state); -#include "tests/unit/test.h" -#include "libdnssec/crypto.h" -#include "libdnssec/random.h" -#include "libknot/libknot.h" -#include "contrib/openbsd/siphash.h" -#include "lib/resolve.h" - -#include "lib/utils.h" -uint64_t fakeclock_now(void); -#define kr_now fakeclock_now -#include "daemon/rrl/api.c" -#undef kr_now - -#define RRL_TABLE_SIZE (1 << 20) -#define RRL_INSTANT_LIMIT (1 << 8) -#define RRL_RATE_LIMIT (1 << 17) -#define RRL_BASE_PRICE (KRU_LIMIT / RRL_INSTANT_LIMIT) - -#define RRL_THREADS 4 -//#define RRL_SYNC_WITH_REAL_TIME - -#define BATCH_QUERIES_LOG 3 // threads acquire queries in batches of 8 -#define HOSTS_LOG 3 // at most 6 attackers + 2 wildcard addresses for normal users -#define TICK_QUERIES_LOG 13 // at most 1024 queries per host per tick - -// Accessing RRL configuration of INSTANT/RATE limits for V4/V6 and specific prefix. -#define LIMIT(type, Vx, prefix) (RRL_MULT(Vx, prefix) * RRL_ ## type ## _LIMIT) - -#define RRL_CONFIG(Vx, name) RRL_ ## Vx ## _ ## name -#define RRL_MULT(Vx, prefix) get_mult(RRL_CONFIG(Vx, PREFIXES), RRL_CONFIG(Vx, RATE_MULT), RRL_CONFIG(Vx, PREFIXES_CNT), prefix) -static inline kru_price_t get_mult(uint8_t prefixes[], kru_price_t mults[], size_t cnt, uint8_t wanted_prefix) { - for (size_t i = 0; i < cnt; i++) - if (prefixes[i] == wanted_prefix) - return mults[i]; - assert(0); - return 0; -} - -// Instant limits and rate limits per msec. -#define INST(Vx, prefix) LIMIT(INSTANT, Vx, prefix) -#define RATEM(Vx, prefix) (LIMIT(RATE, Vx, prefix) / 1000) - -// Expected range of limits for parallel test. -#define RANGE_INST(Vx, prefix) INST(Vx, prefix) - 1, INST(Vx, prefix) + RRL_THREADS - 1 -#define RANGE_RATEM(Vx, prefix) RATEM(Vx, prefix) - 1, RATEM(Vx, prefix) -#define RANGE_UNLIM(queries) queries, queries - -/* Fix seed for randomness in RLL module. Change if improbable collisions arise. (one byte) */ -#define RRL_SEED_GENERIC 1 -#define RRL_SEED_AVX2 1 - -struct kru_generic { - SIPHASH_KEY hash_key; - // ... -}; -struct kru_avx2 { - char hash_key[48] ALIGNED(32); - // ... -}; - -/* Override time. */ -uint64_t fakeclock_tick = 0; - -void fakeclock_init(void) -{ - fakeclock_tick = kr_now(); -} - -uint64_t fakeclock_now(void) -{ - return fakeclock_tick; -} - -struct host { - uint32_t queries_per_tick; - int addr_family; - char *addr_format; - uint32_t min_passed, max_passed; - _Atomic uint32_t passed; -}; - -struct stage { - uint32_t first_tick, last_tick; - struct host hosts[1 << HOSTS_LOG]; -}; - -struct runnable_data { - int prime; - _Atomic uint32_t *queries_acquired, *queries_done; - struct stage *stages; -}; - - -static void *rrl_runnable(void *arg) -{ - struct runnable_data *d = (struct runnable_data *)arg; - size_t si = 0; - - char addr_str[40]; - struct sockaddr_storage addr; - - uint8_t wire[KNOT_WIRE_MIN_PKTSIZE] = { 0 }; - knot_pkt_t answer = { .wire = wire }; - struct kr_request req = { - .qsource.addr = (struct sockaddr *) &addr, - .answer = &answer - }; - - while (true) { - uint32_t qi1 = atomic_fetch_add(d->queries_acquired, 1 << BATCH_QUERIES_LOG); - - /* increment time if needed; sync on incrementing using spinlock */ - uint32_t tick = qi1 >> TICK_QUERIES_LOG; - for (size_t i = 1; tick != fakeclock_tick; i++) { - if ((*d->queries_done >> TICK_QUERIES_LOG) >= tick) { - fakeclock_tick = tick; - } - if (i % (1<<14) == 0) sched_yield(); - __sync_synchronize(); - } - - /* increment stage if needed */ - while (tick > d->stages[si].last_tick) { - ++si; - if (!d->stages[si].first_tick) return NULL; - } - -#ifdef RRL_SYNC_WITH_REAL_TIME - { - struct timespec ts_fake, ts_real; - do { - fakeclock_gettime(CLOCK_MONOTONIC_COARSE, &ts_fake); - clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_real); - } while (!((ts_real.tv_sec > ts_fake.tv_sec) || - ((ts_real.tv_sec == ts_fake.tv_sec) && (ts_real.tv_nsec >= ts_fake.tv_nsec)))); - } -#endif - - if (tick >= d->stages[si].first_tick) { - uint32_t qi2 = 0; - do { - uint32_t qi = qi1 + qi2; - - /* perform query qi */ - uint32_t hi = qi % (1 << HOSTS_LOG); - if (!d->stages[si].hosts[hi].queries_per_tick) continue; - uint32_t hqi = (qi % (1 << TICK_QUERIES_LOG)) >> HOSTS_LOG; // host query index within tick - if (hqi >= d->stages[si].hosts[hi].queries_per_tick) continue; - hqi += (qi >> TICK_QUERIES_LOG) * d->stages[si].hosts[hi].queries_per_tick; // across ticks - (void)snprintf(addr_str, sizeof(addr_str), d->stages[si].hosts[hi].addr_format, - hqi % 0xff, (hqi >> 8) % 0xff, (hqi >> 16) % 0xff); - kr_straddr_socket_set((struct sockaddr *)&addr, addr_str, 0); - - if (!kr_rrl_request_begin(&req)) { - atomic_fetch_add(&d->stages[si].hosts[hi].passed, 1); - } - - } while ((qi2 = (qi2 + d->prime) % (1 << BATCH_QUERIES_LOG))); - } - atomic_fetch_add(d->queries_done, 1 << BATCH_QUERIES_LOG); - } -} - -char *impl_name = ""; +#include "daemon/rrl/tests.inc.c" // defining count_test as macro to let it print usable line number on failure -#define assert_int_between(VAL, MIN, MAX, ...) \ - if (((MIN) > (VAL)) || ((VAL) > (MAX))) { \ - fprintf(stderr, __VA_ARGS__); fprintf(stderr, ": %d <= %d <= %d, ", MIN, VAL, MAX); \ - assert_true(false); } #define count_test(DESC, EXPECTED_PASSING, MARGIN_FRACT, ...) { \ int _max_diff = (EXPECTED_PASSING) * (MARGIN_FRACT); \ int cnt = _count_test(EXPECTED_PASSING, __VA_ARGS__); \ @@ -220,27 +50,8 @@ uint32_t _count_test(int expected_passing, int addr_family, char *addr_format, u return cnt; } -static void test_rrl(void **state) +static void the_tests(void **state) { - dnssec_crypto_init(); - fakeclock_init(); - - /* create rrl table */ - 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); - - if (KRU.initialize == KRU_GENERIC.initialize) { - struct kru_generic *kru = (struct kru_generic *) the_rrl->kru; - memset(&kru->hash_key, RRL_SEED_GENERIC, sizeof(kru->hash_key)); - } else if (KRU.initialize == KRU_AVX2.initialize) { - struct kru_avx2 *kru = (struct kru_avx2 *) the_rrl->kru; - memset(&kru->hash_key, RRL_SEED_AVX2, sizeof(kru->hash_key)); - } else { - assert(0); - } - /* IPv4 multi-prefix tests */ static_assert(RRL_V4_PREFIXES_CNT == 4, "There are no more IPv4 limited prefixes (/32, /24, /20, /18 will be tested)."); @@ -311,99 +122,4 @@ static void test_rrl(void **state) count_test("IPv6 rate limit /128 after 1 msec", RATEM(V6, 128), 0, AF_INET6, "8000::", 0, 0); - - /* parallel tests */ - struct stage stages[] = { - /* first tick, last tick, hosts */ - {32, 32, { - /* queries per tick, family, address, min passed, max passed */ - {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET, "3.3.3.3", RANGE_INST ( V4, 32 )}, - { 512, AF_INET, "4.4.4.4", RANGE_INST ( V4, 32 )}, - {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET6, "3333::3333", RANGE_INST ( V6, 128 )}, - { 512, AF_INET6, "4444::4444", RANGE_INST ( V6, 128 )} - }}, - {33, 255, { - {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, - { 512, AF_INET, "4.4.4.4", RANGE_RATEM ( V4, 32 )}, - {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )}, - { 512, AF_INET6, "4444::4444", RANGE_RATEM ( V6, 128 )}, - }}, - {256, 511, { - {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, - {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )} - }}, - {512, 512, { - {1024, AF_INET, "%d.%d.%d.1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET, "3.3.3.3", RANGE_RATEM ( V4, 32 )}, - { 512, AF_INET, "4.4.4.4", RANGE_INST ( V4, 32 )}, - {1024, AF_INET6, "%x%x:%x00::1", RANGE_UNLIM ( 1024 )}, - {1024, AF_INET6, "3333::3333", RANGE_RATEM ( V6, 128 )}, - { 512, AF_INET6, "4444::4444", RANGE_INST ( V6, 128 )} - }}, - {0} - }; - - pthread_t thr[RRL_THREADS]; - struct runnable_data rd[RRL_THREADS]; - _Atomic uint32_t queries_acquired = 0, queries_done = 0; - int primes[] = {3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61}; - assert(sizeof(primes)/sizeof(*primes) >= RRL_THREADS); - - for (unsigned i = 0; i < RRL_THREADS; ++i) { - rd[i].queries_acquired = &queries_acquired; - rd[i].queries_done = &queries_done; - rd[i].prime = primes[i]; - rd[i].stages = stages; - pthread_create(thr + i, NULL, &rrl_runnable, rd + i); - } - for (unsigned i = 0; i < RRL_THREADS; ++i) { - pthread_join(thr[i], NULL); - } - - unsigned si = 0; - do { - struct host * const h = stages[si].hosts; - uint32_t ticks = stages[si].last_tick - stages[si].first_tick + 1; - for (size_t i = 0; h[i].queries_per_tick; i++) { - assert_int_between(h[i].passed, ticks * h[i].min_passed, ticks * h[i].max_passed, - "parallel stage %d, addr %-25s", si, h[i].addr_format); - } - } while (stages[++si].first_tick); - - kr_rrl_deinit(); - test_tmpdir_remove(tmpdir); - dnssec_crypto_cleanup(); -} - -static void test_rrl_generic(void **state) { - KRU = KRU_GENERIC; - impl_name = "KRU_GENERIC"; - test_rrl(state); -} - -static void test_rrl_avx2(void **state) { - KRU = KRU_AVX2; - impl_name = "KRU_AVX2"; - test_rrl(state); -} - -int main(int argc, char *argv[]) -{ - assert(KRU_GENERIC.initialize != KRU_AVX2.initialize); - if (KRU.initialize == KRU_AVX2.initialize) { - const UnitTest tests[] = { - unit_test(test_rrl_generic), - unit_test(test_rrl_avx2) - }; - return run_tests(tests); - } else { - const UnitTest tests[] = { - unit_test(test_rrl_generic) - }; - return run_tests(tests); - } } diff --git a/daemon/rrl/tests.inc.c b/daemon/rrl/tests.inc.c new file mode 100644 index 000000000..ec21b15b0 --- /dev/null +++ b/daemon/rrl/tests.inc.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2024 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "tests/unit/test.h" +#include "libdnssec/crypto.h" +#include "libdnssec/random.h" +#include "libknot/libknot.h" +#include "contrib/openbsd/siphash.h" +#include "lib/resolve.h" + +#include "lib/utils.h" +uint64_t fakeclock_now(void); +#define kr_now fakeclock_now +#include "daemon/rrl/api.c" +#undef kr_now + +#define RRL_TABLE_SIZE (1 << 20) +#define RRL_INSTANT_LIMIT (1 << 8) +#define RRL_RATE_LIMIT (1 << 17) +#define RRL_BASE_PRICE (KRU_LIMIT / RRL_INSTANT_LIMIT) + +// Accessing RRL configuration of INSTANT/RATE limits for V4/V6 and specific prefix. +#define LIMIT(type, Vx, prefix) (RRL_MULT(Vx, prefix) * RRL_ ## type ## _LIMIT) + +#define RRL_CONFIG(Vx, name) RRL_ ## Vx ## _ ## name +#define RRL_MULT(Vx, prefix) get_mult(RRL_CONFIG(Vx, PREFIXES), RRL_CONFIG(Vx, RATE_MULT), RRL_CONFIG(Vx, PREFIXES_CNT), prefix) +static inline kru_price_t get_mult(uint8_t prefixes[], kru_price_t mults[], size_t cnt, uint8_t wanted_prefix) { + for (size_t i = 0; i < cnt; i++) + if (prefixes[i] == wanted_prefix) + return mults[i]; + assert(0); + return 0; +} + +// Instant limits and rate limits per msec. +#define INST(Vx, prefix) LIMIT(INSTANT, Vx, prefix) +#define RATEM(Vx, prefix) (LIMIT(RATE, Vx, prefix) / 1000) + +/* Fix seed for randomness in RLL module. Change if improbable collisions arise. (one byte) */ +#define RRL_SEED_GENERIC 1 +#define RRL_SEED_AVX2 1 + +#define assert_int_between(VAL, MIN, MAX, ...) \ + if (((MIN) > (VAL)) || ((VAL) > (MAX))) { \ + fprintf(stderr, __VA_ARGS__); fprintf(stderr, ": %d <= %d <= %d, ", MIN, VAL, MAX); \ + assert_true(false); } + +struct kru_generic { + SIPHASH_KEY hash_key; + // ... +}; +struct kru_avx2 { + char hash_key[48] ALIGNED(32); + // ... +}; + +/* Override time. */ +uint64_t fakeclock_tick = 0; +uint64_t fakeclock_start = 0; + +void fakeclock_init(void) +{ + fakeclock_start = kr_now(); + fakeclock_tick = 0; +} + +uint64_t fakeclock_now(void) +{ + return fakeclock_start + fakeclock_tick; +} + +static void test_rrl(void **state) { + dnssec_crypto_init(); + fakeclock_init(); + + /* create rrl table */ + 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); + + if (KRU.initialize == KRU_GENERIC.initialize) { + struct kru_generic *kru = (struct kru_generic *) the_rrl->kru; + memset(&kru->hash_key, RRL_SEED_GENERIC, sizeof(kru->hash_key)); + } else if (KRU.initialize == KRU_AVX2.initialize) { + struct kru_avx2 *kru = (struct kru_avx2 *) the_rrl->kru; + memset(&kru->hash_key, RRL_SEED_AVX2, sizeof(kru->hash_key)); + } else { + assert(0); + } + + the_tests(state); + + kr_rrl_deinit(); + test_tmpdir_remove(tmpdir); + dnssec_crypto_cleanup(); +} + +static void test_rrl_generic(void **state) { + KRU = KRU_GENERIC; + test_rrl(state); +} + +static void test_rrl_avx2(void **state) { + KRU = KRU_AVX2; + test_rrl(state); +} + +int main(int argc, char *argv[]) +{ + assert(KRU_GENERIC.initialize != KRU_AVX2.initialize); + if (KRU.initialize == KRU_AVX2.initialize) { + const UnitTest tests[] = { + unit_test(test_rrl_generic), + unit_test(test_rrl_avx2) + }; + return run_tests(tests); + } else { + const UnitTest tests[] = { + unit_test(test_rrl_generic) + }; + return run_tests(tests); + } +} diff --git a/meson.build b/meson.build index 9ee683d77..dd42f46cf 100644 --- a/meson.build +++ b/meson.build @@ -174,7 +174,8 @@ c_src_lint = files() # These lists are added to from subdir() and finally used in tests/* unit_tests = [ - # [test_name, files(test)] + # [name, files(test)] + # [name, files(test), [extra_suites]] ] config_tests = [ diff --git a/tests/unit/meson.build b/tests/unit/meson.build index a6fb0d08a..59e3b3ef2 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -31,10 +31,14 @@ foreach unit_test : unit_tests (meson.get_compiler('c').find_library('m', required : false)), ], ) + + # additional suites + extra_suites = unit_test.length() >= 3 ? unit_test[2] : [] + test( 'unit.' + unit_test[0], exec_test, - suite: 'unit', + suite: [ 'unit' ] + extra_suites, # they take very short time kwargs: meson.version().version_compare('<0.52') ? {} : { 'priority': -5 }, )