]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
rrl: disable parallel tests under valgrind in CI
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 10 Apr 2024 11:08:10 +0000 (13:08 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 10 Apr 2024 11:09:21 +0000 (13:09 +0200)
.gitlab-ci.yml
daemon/rrl/meson.build
daemon/rrl/tests-parallel.c [new file with mode: 0644]
daemon/rrl/tests.c
daemon/rrl/tests.inc.c [new file with mode: 0644]
meson.build
tests/unit/meson.build

index f5abec54b5441019899bfa1a86f8871a5f63f100..bbb9826991fd87e80cfa10aaf3deaae002418235 100644 (file)
@@ -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
index 9f2275a24214fc7b997855374395c47ca47a1892..8a815d70d5dc42d203fd7b6cd5dba7394807b1f9 100644 (file)
@@ -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 (file)
index 0000000..934ef5f
--- /dev/null
@@ -0,0 +1,175 @@
+/*  Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    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 <https://www.gnu.org/licenses/>.
+ */
+
+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);
+}
index 048107f196c409975152687457e6f13e602df4e3..abccaafa78efe4d3bd3556c242a2a9c3a04ce1f2 100644 (file)
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include <pthread.h>
-#include <sched.h>
-#include <stdio.h>
-#include <stdatomic.h>
+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 (file)
index 0000000..ec21b15
--- /dev/null
@@ -0,0 +1,142 @@
+/*  Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdatomic.h>
+
+#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);
+       }
+}
index 9ee683d77624a70ca9b2cb6c9418ddb60d2e7301..dd42f46cfda8a09390d35b80f8222d3304af863b 100644 (file)
@@ -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 = [
index a6fb0d08a1e17e2132a39ac596817f16bc2ca148..59e3b3ef2ff33dad48237994d21525be72c2e36f 100644 (file)
@@ -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 },
   )