1 // SPDX-License-Identifier: GPL-2.0
3 * Randomized tests for eBPF longest-prefix-match maps
5 * This program runs randomized tests against the lpm-bpf-map. It implements a
6 * "Trivial Longest Prefix Match" (tlpm) based on simple, linear, singly linked
7 * lists. The implementation should be pretty straightforward.
9 * Based on tlpm, this inserts randomized data into bpf-lpm-maps and verifies
10 * the trie-based bpf-map implementation behaves the same way as tlpm.
16 #include <linux/bpf.h>
23 #include <arpa/inet.h>
29 #include "bpf_rlimit.h"
32 struct tlpm_node
*next
;
37 static struct tlpm_node
*tlpm_match(struct tlpm_node
*list
,
41 static struct tlpm_node
*tlpm_add(struct tlpm_node
*list
,
45 struct tlpm_node
*node
;
50 /* 'overwrite' an equivalent entry if one already exists */
51 node
= tlpm_match(list
, key
, n_bits
);
52 if (node
&& node
->n_bits
== n_bits
) {
53 memcpy(node
->key
, key
, n
);
57 /* add new entry with @key/@n_bits to @list and return new head */
59 node
= malloc(sizeof(*node
) + n
);
63 node
->n_bits
= n_bits
;
64 memcpy(node
->key
, key
, n
);
69 static void tlpm_clear(struct tlpm_node
*list
)
71 struct tlpm_node
*node
;
73 /* free all entries in @list */
75 while ((node
= list
)) {
81 static struct tlpm_node
*tlpm_match(struct tlpm_node
*list
,
85 struct tlpm_node
*best
= NULL
;
88 /* Perform longest prefix-match on @key/@n_bits. That is, iterate all
89 * entries and match each prefix against @key. Remember the "best"
90 * entry we find (i.e., the longest prefix that matches) and return it
91 * to the caller when done.
94 for ( ; list
; list
= list
->next
) {
95 for (i
= 0; i
< n_bits
&& i
< list
->n_bits
; ++i
) {
96 if ((key
[i
/ 8] & (1 << (7 - i
% 8))) !=
97 (list
->key
[i
/ 8] & (1 << (7 - i
% 8))))
101 if (i
>= list
->n_bits
) {
102 if (!best
|| i
> best
->n_bits
)
110 static struct tlpm_node
*tlpm_delete(struct tlpm_node
*list
,
114 struct tlpm_node
*best
= tlpm_match(list
, key
, n_bits
);
115 struct tlpm_node
*node
;
117 if (!best
|| best
->n_bits
!= n_bits
)
126 for (node
= list
; node
; node
= node
->next
) {
127 if (node
->next
== best
) {
128 node
->next
= best
->next
;
133 /* should never get here */
138 static void test_lpm_basic(void)
140 struct tlpm_node
*list
= NULL
, *t1
, *t2
;
142 /* very basic, static tests to verify tlpm works as expected */
144 assert(!tlpm_match(list
, (uint8_t[]){ 0xff }, 8));
146 t1
= list
= tlpm_add(list
, (uint8_t[]){ 0xff }, 8);
147 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff }, 8));
148 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff, 0xff }, 16));
149 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff, 0x00 }, 16));
150 assert(!tlpm_match(list
, (uint8_t[]){ 0x7f }, 8));
151 assert(!tlpm_match(list
, (uint8_t[]){ 0xfe }, 8));
152 assert(!tlpm_match(list
, (uint8_t[]){ 0xff }, 7));
154 t2
= list
= tlpm_add(list
, (uint8_t[]){ 0xff, 0xff }, 16);
155 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff }, 8));
156 assert(t2
== tlpm_match(list
, (uint8_t[]){ 0xff, 0xff }, 16));
157 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff, 0xff }, 15));
158 assert(!tlpm_match(list
, (uint8_t[]){ 0x7f, 0xff }, 16));
160 list
= tlpm_delete(list
, (uint8_t[]){ 0xff, 0xff }, 16);
161 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff }, 8));
162 assert(t1
== tlpm_match(list
, (uint8_t[]){ 0xff, 0xff }, 16));
164 list
= tlpm_delete(list
, (uint8_t[]){ 0xff }, 8);
165 assert(!tlpm_match(list
, (uint8_t[]){ 0xff }, 8));
170 static void test_lpm_order(void)
172 struct tlpm_node
*t1
, *t2
, *l1
= NULL
, *l2
= NULL
;
175 /* Verify the tlpm implementation works correctly regardless of the
176 * order of entries. Insert a random set of entries into @l1, and copy
177 * the same data in reverse order into @l2. Then verify a lookup of
178 * random keys will yield the same result in both sets.
181 for (i
= 0; i
< (1 << 12); ++i
)
182 l1
= tlpm_add(l1
, (uint8_t[]){
187 for (t1
= l1
; t1
; t1
= t1
->next
)
188 l2
= tlpm_add(l2
, t1
->key
, t1
->n_bits
);
190 for (i
= 0; i
< (1 << 8); ++i
) {
191 uint8_t key
[] = { rand() % 0xff, rand() % 0xff };
193 t1
= tlpm_match(l1
, key
, 16);
194 t2
= tlpm_match(l2
, key
, 16);
198 assert(t1
->n_bits
== t2
->n_bits
);
199 for (j
= 0; j
< t1
->n_bits
; ++j
)
200 assert((t1
->key
[j
/ 8] & (1 << (7 - j
% 8))) ==
201 (t2
->key
[j
/ 8] & (1 << (7 - j
% 8))));
209 static void test_lpm_map(int keysize
)
211 size_t i
, j
, n_matches
, n_matches_after_delete
, n_nodes
, n_lookups
;
212 struct tlpm_node
*t
, *list
= NULL
;
213 struct bpf_lpm_trie_key
*key
;
214 uint8_t *data
, *value
;
217 /* Compare behavior of tlpm vs. bpf-lpm. Create a randomized set of
218 * prefixes and insert it into both tlpm and bpf-lpm. Then run some
219 * randomized lookups and verify both maps return the same result.
223 n_matches_after_delete
= 0;
227 data
= alloca(keysize
);
228 memset(data
, 0, keysize
);
230 value
= alloca(keysize
+ 1);
231 memset(value
, 0, keysize
+ 1);
233 key
= alloca(sizeof(*key
) + keysize
);
234 memset(key
, 0, sizeof(*key
) + keysize
);
236 map
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
,
237 sizeof(*key
) + keysize
,
243 for (i
= 0; i
< n_nodes
; ++i
) {
244 for (j
= 0; j
< keysize
; ++j
)
245 value
[j
] = rand() & 0xff;
246 value
[keysize
] = rand() % (8 * keysize
+ 1);
248 list
= tlpm_add(list
, value
, value
[keysize
]);
250 key
->prefixlen
= value
[keysize
];
251 memcpy(key
->data
, value
, keysize
);
252 r
= bpf_map_update_elem(map
, key
, value
, 0);
256 for (i
= 0; i
< n_lookups
; ++i
) {
257 for (j
= 0; j
< keysize
; ++j
)
258 data
[j
] = rand() & 0xff;
260 t
= tlpm_match(list
, data
, 8 * keysize
);
262 key
->prefixlen
= 8 * keysize
;
263 memcpy(key
->data
, data
, keysize
);
264 r
= bpf_map_lookup_elem(map
, key
, value
);
265 assert(!r
|| errno
== ENOENT
);
270 assert(t
->n_bits
== value
[keysize
]);
271 for (j
= 0; j
< t
->n_bits
; ++j
)
272 assert((t
->key
[j
/ 8] & (1 << (7 - j
% 8))) ==
273 (value
[j
/ 8] & (1 << (7 - j
% 8))));
277 /* Remove the first half of the elements in the tlpm and the
278 * corresponding nodes from the bpf-lpm. Then run the same
279 * large number of random lookups in both and make sure they match.
280 * Note: we need to count the number of nodes actually inserted
281 * since there may have been duplicates.
283 for (i
= 0, t
= list
; t
; i
++, t
= t
->next
)
285 for (j
= 0; j
< i
/ 2; ++j
) {
286 key
->prefixlen
= list
->n_bits
;
287 memcpy(key
->data
, list
->key
, keysize
);
288 r
= bpf_map_delete_elem(map
, key
);
290 list
= tlpm_delete(list
, list
->key
, list
->n_bits
);
293 for (i
= 0; i
< n_lookups
; ++i
) {
294 for (j
= 0; j
< keysize
; ++j
)
295 data
[j
] = rand() & 0xff;
297 t
= tlpm_match(list
, data
, 8 * keysize
);
299 key
->prefixlen
= 8 * keysize
;
300 memcpy(key
->data
, data
, keysize
);
301 r
= bpf_map_lookup_elem(map
, key
, value
);
302 assert(!r
|| errno
== ENOENT
);
306 ++n_matches_after_delete
;
307 assert(t
->n_bits
== value
[keysize
]);
308 for (j
= 0; j
< t
->n_bits
; ++j
)
309 assert((t
->key
[j
/ 8] & (1 << (7 - j
% 8))) ==
310 (value
[j
/ 8] & (1 << (7 - j
% 8))));
317 /* With 255 random nodes in the map, we are pretty likely to match
318 * something on every lookup. For statistics, use this:
320 * printf(" nodes: %zu\n"
323 * "matches(delete): %zu\n",
324 * n_nodes, n_lookups, n_matches, n_matches_after_delete);
328 /* Test the implementation with some 'real world' examples */
330 static void test_lpm_ipaddr(void)
332 struct bpf_lpm_trie_key
*key_ipv4
;
333 struct bpf_lpm_trie_key
*key_ipv6
;
334 size_t key_size_ipv4
;
335 size_t key_size_ipv6
;
340 key_size_ipv4
= sizeof(*key_ipv4
) + sizeof(__u32
);
341 key_size_ipv6
= sizeof(*key_ipv6
) + sizeof(__u32
) * 4;
342 key_ipv4
= alloca(key_size_ipv4
);
343 key_ipv6
= alloca(key_size_ipv6
);
345 map_fd_ipv4
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
,
346 key_size_ipv4
, sizeof(value
),
347 100, BPF_F_NO_PREALLOC
);
348 assert(map_fd_ipv4
>= 0);
350 map_fd_ipv6
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
,
351 key_size_ipv6
, sizeof(value
),
352 100, BPF_F_NO_PREALLOC
);
353 assert(map_fd_ipv6
>= 0);
355 /* Fill data some IPv4 and IPv6 address ranges */
357 key_ipv4
->prefixlen
= 16;
358 inet_pton(AF_INET
, "192.168.0.0", key_ipv4
->data
);
359 assert(bpf_map_update_elem(map_fd_ipv4
, key_ipv4
, &value
, 0) == 0);
362 key_ipv4
->prefixlen
= 24;
363 inet_pton(AF_INET
, "192.168.0.0", key_ipv4
->data
);
364 assert(bpf_map_update_elem(map_fd_ipv4
, key_ipv4
, &value
, 0) == 0);
367 key_ipv4
->prefixlen
= 24;
368 inet_pton(AF_INET
, "192.168.128.0", key_ipv4
->data
);
369 assert(bpf_map_update_elem(map_fd_ipv4
, key_ipv4
, &value
, 0) == 0);
372 key_ipv4
->prefixlen
= 24;
373 inet_pton(AF_INET
, "192.168.1.0", key_ipv4
->data
);
374 assert(bpf_map_update_elem(map_fd_ipv4
, key_ipv4
, &value
, 0) == 0);
377 key_ipv4
->prefixlen
= 23;
378 inet_pton(AF_INET
, "192.168.0.0", key_ipv4
->data
);
379 assert(bpf_map_update_elem(map_fd_ipv4
, key_ipv4
, &value
, 0) == 0);
382 key_ipv6
->prefixlen
= 64;
383 inet_pton(AF_INET6
, "2a00:1450:4001:814::200e", key_ipv6
->data
);
384 assert(bpf_map_update_elem(map_fd_ipv6
, key_ipv6
, &value
, 0) == 0);
386 /* Set tprefixlen to maximum for lookups */
387 key_ipv4
->prefixlen
= 32;
388 key_ipv6
->prefixlen
= 128;
390 /* Test some lookups that should come back with a value */
391 inet_pton(AF_INET
, "192.168.128.23", key_ipv4
->data
);
392 assert(bpf_map_lookup_elem(map_fd_ipv4
, key_ipv4
, &value
) == 0);
395 inet_pton(AF_INET
, "192.168.0.1", key_ipv4
->data
);
396 assert(bpf_map_lookup_elem(map_fd_ipv4
, key_ipv4
, &value
) == 0);
399 inet_pton(AF_INET6
, "2a00:1450:4001:814::", key_ipv6
->data
);
400 assert(bpf_map_lookup_elem(map_fd_ipv6
, key_ipv6
, &value
) == 0);
401 assert(value
== 0xdeadbeef);
403 inet_pton(AF_INET6
, "2a00:1450:4001:814::1", key_ipv6
->data
);
404 assert(bpf_map_lookup_elem(map_fd_ipv6
, key_ipv6
, &value
) == 0);
405 assert(value
== 0xdeadbeef);
407 /* Test some lookups that should not match any entry */
408 inet_pton(AF_INET
, "10.0.0.1", key_ipv4
->data
);
409 assert(bpf_map_lookup_elem(map_fd_ipv4
, key_ipv4
, &value
) == -1 &&
412 inet_pton(AF_INET
, "11.11.11.11", key_ipv4
->data
);
413 assert(bpf_map_lookup_elem(map_fd_ipv4
, key_ipv4
, &value
) == -1 &&
416 inet_pton(AF_INET6
, "2a00:ffff::", key_ipv6
->data
);
417 assert(bpf_map_lookup_elem(map_fd_ipv6
, key_ipv6
, &value
) == -1 &&
424 static void test_lpm_delete(void)
426 struct bpf_lpm_trie_key
*key
;
431 key_size
= sizeof(*key
) + sizeof(__u32
);
432 key
= alloca(key_size
);
434 map_fd
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
,
435 key_size
, sizeof(value
),
436 100, BPF_F_NO_PREALLOC
);
442 * 192.168.128.0/24 (3)
453 inet_pton(AF_INET
, "192.168.0.0", key
->data
);
454 assert(bpf_map_update_elem(map_fd
, key
, &value
, 0) == 0);
458 inet_pton(AF_INET
, "192.168.0.0", key
->data
);
459 assert(bpf_map_update_elem(map_fd
, key
, &value
, 0) == 0);
463 inet_pton(AF_INET
, "192.168.128.0", key
->data
);
464 assert(bpf_map_update_elem(map_fd
, key
, &value
, 0) == 0);
468 inet_pton(AF_INET
, "192.168.1.0", key
->data
);
469 assert(bpf_map_update_elem(map_fd
, key
, &value
, 0) == 0);
471 /* remove non-existent node */
473 inet_pton(AF_INET
, "10.0.0.1", key
->data
);
474 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == -1 &&
477 key
->prefixlen
= 30; // unused prefix so far
478 inet_pton(AF_INET
, "192.255.0.0", key
->data
);
479 assert(bpf_map_delete_elem(map_fd
, key
) == -1 &&
482 key
->prefixlen
= 16; // same prefix as the root node
483 inet_pton(AF_INET
, "192.255.0.0", key
->data
);
484 assert(bpf_map_delete_elem(map_fd
, key
) == -1 &&
487 /* assert initial lookup */
489 inet_pton(AF_INET
, "192.168.0.1", key
->data
);
490 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == 0);
493 /* remove leaf node */
495 inet_pton(AF_INET
, "192.168.0.0", key
->data
);
496 assert(bpf_map_delete_elem(map_fd
, key
) == 0);
499 inet_pton(AF_INET
, "192.168.0.1", key
->data
);
500 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == 0);
503 /* remove leaf (and intermediary) node */
505 inet_pton(AF_INET
, "192.168.1.0", key
->data
);
506 assert(bpf_map_delete_elem(map_fd
, key
) == 0);
509 inet_pton(AF_INET
, "192.168.1.1", key
->data
);
510 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == 0);
513 /* remove root node */
515 inet_pton(AF_INET
, "192.168.0.0", key
->data
);
516 assert(bpf_map_delete_elem(map_fd
, key
) == 0);
519 inet_pton(AF_INET
, "192.168.128.1", key
->data
);
520 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == 0);
523 /* remove last node */
525 inet_pton(AF_INET
, "192.168.128.0", key
->data
);
526 assert(bpf_map_delete_elem(map_fd
, key
) == 0);
529 inet_pton(AF_INET
, "192.168.128.1", key
->data
);
530 assert(bpf_map_lookup_elem(map_fd
, key
, &value
) == -1 &&
536 static void test_lpm_get_next_key(void)
538 struct bpf_lpm_trie_key
*key_p
, *next_key_p
;
543 key_size
= sizeof(*key_p
) + sizeof(__u32
);
544 key_p
= alloca(key_size
);
545 next_key_p
= alloca(key_size
);
547 map_fd
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
, key_size
, sizeof(value
),
548 100, BPF_F_NO_PREALLOC
);
551 /* empty tree. get_next_key should return ENOENT */
552 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == -1 &&
555 /* get and verify the first key, get the second one should fail. */
556 key_p
->prefixlen
= 16;
557 inet_pton(AF_INET
, "192.168.0.0", key_p
->data
);
558 assert(bpf_map_update_elem(map_fd
, key_p
, &value
, 0) == 0);
560 memset(key_p
, 0, key_size
);
561 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
562 assert(key_p
->prefixlen
== 16 && key_p
->data
[0] == 192 &&
563 key_p
->data
[1] == 168);
565 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == -1 &&
568 /* no exact matching key should get the first one in post order. */
569 key_p
->prefixlen
= 8;
570 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
571 assert(key_p
->prefixlen
== 16 && key_p
->data
[0] == 192 &&
572 key_p
->data
[1] == 168);
574 /* add one more element (total two) */
575 key_p
->prefixlen
= 24;
576 inet_pton(AF_INET
, "192.168.128.0", key_p
->data
);
577 assert(bpf_map_update_elem(map_fd
, key_p
, &value
, 0) == 0);
579 memset(key_p
, 0, key_size
);
580 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
581 assert(key_p
->prefixlen
== 24 && key_p
->data
[0] == 192 &&
582 key_p
->data
[1] == 168 && key_p
->data
[2] == 128);
584 memset(next_key_p
, 0, key_size
);
585 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
586 assert(next_key_p
->prefixlen
== 16 && next_key_p
->data
[0] == 192 &&
587 next_key_p
->data
[1] == 168);
589 memcpy(key_p
, next_key_p
, key_size
);
590 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == -1 &&
593 /* Add one more element (total three) */
594 key_p
->prefixlen
= 24;
595 inet_pton(AF_INET
, "192.168.0.0", key_p
->data
);
596 assert(bpf_map_update_elem(map_fd
, key_p
, &value
, 0) == 0);
598 memset(key_p
, 0, key_size
);
599 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
600 assert(key_p
->prefixlen
== 24 && key_p
->data
[0] == 192 &&
601 key_p
->data
[1] == 168 && key_p
->data
[2] == 0);
603 memset(next_key_p
, 0, key_size
);
604 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
605 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
606 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 128);
608 memcpy(key_p
, next_key_p
, key_size
);
609 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
610 assert(next_key_p
->prefixlen
== 16 && next_key_p
->data
[0] == 192 &&
611 next_key_p
->data
[1] == 168);
613 memcpy(key_p
, next_key_p
, key_size
);
614 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == -1 &&
617 /* Add one more element (total four) */
618 key_p
->prefixlen
= 24;
619 inet_pton(AF_INET
, "192.168.1.0", key_p
->data
);
620 assert(bpf_map_update_elem(map_fd
, key_p
, &value
, 0) == 0);
622 memset(key_p
, 0, key_size
);
623 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
624 assert(key_p
->prefixlen
== 24 && key_p
->data
[0] == 192 &&
625 key_p
->data
[1] == 168 && key_p
->data
[2] == 0);
627 memset(next_key_p
, 0, key_size
);
628 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
629 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
630 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 1);
632 memcpy(key_p
, next_key_p
, key_size
);
633 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
634 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
635 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 128);
637 memcpy(key_p
, next_key_p
, key_size
);
638 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
639 assert(next_key_p
->prefixlen
== 16 && next_key_p
->data
[0] == 192 &&
640 next_key_p
->data
[1] == 168);
642 memcpy(key_p
, next_key_p
, key_size
);
643 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == -1 &&
646 /* Add one more element (total five) */
647 key_p
->prefixlen
= 28;
648 inet_pton(AF_INET
, "192.168.1.128", key_p
->data
);
649 assert(bpf_map_update_elem(map_fd
, key_p
, &value
, 0) == 0);
651 memset(key_p
, 0, key_size
);
652 assert(bpf_map_get_next_key(map_fd
, NULL
, key_p
) == 0);
653 assert(key_p
->prefixlen
== 24 && key_p
->data
[0] == 192 &&
654 key_p
->data
[1] == 168 && key_p
->data
[2] == 0);
656 memset(next_key_p
, 0, key_size
);
657 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
658 assert(next_key_p
->prefixlen
== 28 && next_key_p
->data
[0] == 192 &&
659 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 1 &&
660 next_key_p
->data
[3] == 128);
662 memcpy(key_p
, next_key_p
, key_size
);
663 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
664 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
665 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 1);
667 memcpy(key_p
, next_key_p
, key_size
);
668 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
669 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
670 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 128);
672 memcpy(key_p
, next_key_p
, key_size
);
673 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
674 assert(next_key_p
->prefixlen
== 16 && next_key_p
->data
[0] == 192 &&
675 next_key_p
->data
[1] == 168);
677 memcpy(key_p
, next_key_p
, key_size
);
678 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == -1 &&
681 /* no exact matching key should return the first one in post order */
682 key_p
->prefixlen
= 22;
683 inet_pton(AF_INET
, "192.168.1.0", key_p
->data
);
684 assert(bpf_map_get_next_key(map_fd
, key_p
, next_key_p
) == 0);
685 assert(next_key_p
->prefixlen
== 24 && next_key_p
->data
[0] == 192 &&
686 next_key_p
->data
[1] == 168 && next_key_p
->data
[2] == 0);
691 #define MAX_TEST_KEYS 4
692 struct lpm_mt_test_info
{
693 int cmd
; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */
699 } key
[MAX_TEST_KEYS
];
702 static void *lpm_test_command(void *arg
)
704 int i
, j
, ret
, iter
, key_size
;
705 struct lpm_mt_test_info
*info
= arg
;
706 struct bpf_lpm_trie_key
*key_p
;
708 key_size
= sizeof(struct bpf_lpm_trie_key
) + sizeof(__u32
);
709 key_p
= alloca(key_size
);
710 for (iter
= 0; iter
< info
->iter
; iter
++)
711 for (i
= 0; i
< MAX_TEST_KEYS
; i
++) {
712 /* first half of iterations in forward order,
713 * and second half in backward order.
715 j
= (iter
< (info
->iter
/ 2)) ? i
: MAX_TEST_KEYS
- i
- 1;
716 key_p
->prefixlen
= info
->key
[j
].prefixlen
;
717 memcpy(key_p
->data
, &info
->key
[j
].data
, sizeof(__u32
));
718 if (info
->cmd
== 0) {
720 /* update must succeed */
721 assert(bpf_map_update_elem(info
->map_fd
, key_p
, &value
, 0) == 0);
722 } else if (info
->cmd
== 1) {
723 ret
= bpf_map_delete_elem(info
->map_fd
, key_p
);
724 assert(ret
== 0 || errno
== ENOENT
);
725 } else if (info
->cmd
== 2) {
727 ret
= bpf_map_lookup_elem(info
->map_fd
, key_p
, &value
);
728 assert(ret
== 0 || errno
== ENOENT
);
730 struct bpf_lpm_trie_key
*next_key_p
= alloca(key_size
);
731 ret
= bpf_map_get_next_key(info
->map_fd
, key_p
, next_key_p
);
732 assert(ret
== 0 || errno
== ENOENT
|| errno
== ENOMEM
);
736 // Pass successful exit info back to the main thread
737 pthread_exit((void *)info
);
740 static void setup_lpm_mt_test_info(struct lpm_mt_test_info
*info
, int map_fd
)
743 info
->map_fd
= map_fd
;
744 info
->key
[0].prefixlen
= 16;
745 inet_pton(AF_INET
, "192.168.0.0", &info
->key
[0].data
);
746 info
->key
[1].prefixlen
= 24;
747 inet_pton(AF_INET
, "192.168.0.0", &info
->key
[1].data
);
748 info
->key
[2].prefixlen
= 24;
749 inet_pton(AF_INET
, "192.168.128.0", &info
->key
[2].data
);
750 info
->key
[3].prefixlen
= 24;
751 inet_pton(AF_INET
, "192.168.1.0", &info
->key
[3].data
);
754 static void test_lpm_multi_thread(void)
756 struct lpm_mt_test_info info
[4];
757 size_t key_size
, value_size
;
758 pthread_t thread_id
[4];
763 value_size
= sizeof(__u32
);
764 key_size
= sizeof(struct bpf_lpm_trie_key
) + value_size
;
765 map_fd
= bpf_create_map(BPF_MAP_TYPE_LPM_TRIE
, key_size
, value_size
,
766 100, BPF_F_NO_PREALLOC
);
768 /* create 4 threads to test update, delete, lookup and get_next_key */
769 setup_lpm_mt_test_info(&info
[0], map_fd
);
770 for (i
= 0; i
< 4; i
++) {
772 memcpy(&info
[i
], &info
[0], sizeof(info
[i
]));
774 assert(pthread_create(&thread_id
[i
], NULL
, &lpm_test_command
, &info
[i
]) == 0);
777 for (i
= 0; i
< 4; i
++)
778 assert(pthread_join(thread_id
[i
], &ret
) == 0 && ret
== (void *)&info
[i
]);
787 /* we want predictable, pseudo random tests */
793 /* Test with 8, 16, 24, 32, ... 128 bit prefix length */
794 for (i
= 1; i
<= 16; ++i
)
799 test_lpm_get_next_key();
800 test_lpm_multi_thread();
802 printf("test_lpm: OK\n");