18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Randomized tests for eBPF longest-prefix-match maps
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program runs randomized tests against the lpm-bpf-map. It implements a
68c2ecf20Sopenharmony_ci * "Trivial Longest Prefix Match" (tlpm) based on simple, linear, singly linked
78c2ecf20Sopenharmony_ci * lists. The implementation should be pretty straightforward.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on tlpm, this inserts randomized data into bpf-lpm-maps and verifies
108c2ecf20Sopenharmony_ci * the trie-based bpf-map implementation behaves the same way as tlpm.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <assert.h>
148c2ecf20Sopenharmony_ci#include <errno.h>
158c2ecf20Sopenharmony_ci#include <inttypes.h>
168c2ecf20Sopenharmony_ci#include <linux/bpf.h>
178c2ecf20Sopenharmony_ci#include <pthread.h>
188c2ecf20Sopenharmony_ci#include <stdio.h>
198c2ecf20Sopenharmony_ci#include <stdlib.h>
208c2ecf20Sopenharmony_ci#include <string.h>
218c2ecf20Sopenharmony_ci#include <time.h>
228c2ecf20Sopenharmony_ci#include <unistd.h>
238c2ecf20Sopenharmony_ci#include <arpa/inet.h>
248c2ecf20Sopenharmony_ci#include <sys/time.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "bpf_util.h"
298c2ecf20Sopenharmony_ci#include "bpf_rlimit.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct tlpm_node {
328c2ecf20Sopenharmony_ci	struct tlpm_node *next;
338c2ecf20Sopenharmony_ci	size_t n_bits;
348c2ecf20Sopenharmony_ci	uint8_t key[];
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct tlpm_node *tlpm_match(struct tlpm_node *list,
388c2ecf20Sopenharmony_ci				    const uint8_t *key,
398c2ecf20Sopenharmony_ci				    size_t n_bits);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct tlpm_node *tlpm_add(struct tlpm_node *list,
428c2ecf20Sopenharmony_ci				  const uint8_t *key,
438c2ecf20Sopenharmony_ci				  size_t n_bits)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct tlpm_node *node;
468c2ecf20Sopenharmony_ci	size_t n;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	n = (n_bits + 7) / 8;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* 'overwrite' an equivalent entry if one already exists */
518c2ecf20Sopenharmony_ci	node = tlpm_match(list, key, n_bits);
528c2ecf20Sopenharmony_ci	if (node && node->n_bits == n_bits) {
538c2ecf20Sopenharmony_ci		memcpy(node->key, key, n);
548c2ecf20Sopenharmony_ci		return list;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* add new entry with @key/@n_bits to @list and return new head */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	node = malloc(sizeof(*node) + n);
608c2ecf20Sopenharmony_ci	assert(node);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	node->next = list;
638c2ecf20Sopenharmony_ci	node->n_bits = n_bits;
648c2ecf20Sopenharmony_ci	memcpy(node->key, key, n);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return node;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void tlpm_clear(struct tlpm_node *list)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct tlpm_node *node;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* free all entries in @list */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	while ((node = list)) {
768c2ecf20Sopenharmony_ci		list = list->next;
778c2ecf20Sopenharmony_ci		free(node);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct tlpm_node *tlpm_match(struct tlpm_node *list,
828c2ecf20Sopenharmony_ci				    const uint8_t *key,
838c2ecf20Sopenharmony_ci				    size_t n_bits)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct tlpm_node *best = NULL;
868c2ecf20Sopenharmony_ci	size_t i;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Perform longest prefix-match on @key/@n_bits. That is, iterate all
898c2ecf20Sopenharmony_ci	 * entries and match each prefix against @key. Remember the "best"
908c2ecf20Sopenharmony_ci	 * entry we find (i.e., the longest prefix that matches) and return it
918c2ecf20Sopenharmony_ci	 * to the caller when done.
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for ( ; list; list = list->next) {
958c2ecf20Sopenharmony_ci		for (i = 0; i < n_bits && i < list->n_bits; ++i) {
968c2ecf20Sopenharmony_ci			if ((key[i / 8] & (1 << (7 - i % 8))) !=
978c2ecf20Sopenharmony_ci			    (list->key[i / 8] & (1 << (7 - i % 8))))
988c2ecf20Sopenharmony_ci				break;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if (i >= list->n_bits) {
1028c2ecf20Sopenharmony_ci			if (!best || i > best->n_bits)
1038c2ecf20Sopenharmony_ci				best = list;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return best;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct tlpm_node *tlpm_delete(struct tlpm_node *list,
1118c2ecf20Sopenharmony_ci				     const uint8_t *key,
1128c2ecf20Sopenharmony_ci				     size_t n_bits)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct tlpm_node *best = tlpm_match(list, key, n_bits);
1158c2ecf20Sopenharmony_ci	struct tlpm_node *node;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (!best || best->n_bits != n_bits)
1188c2ecf20Sopenharmony_ci		return list;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (best == list) {
1218c2ecf20Sopenharmony_ci		node = best->next;
1228c2ecf20Sopenharmony_ci		free(best);
1238c2ecf20Sopenharmony_ci		return node;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	for (node = list; node; node = node->next) {
1278c2ecf20Sopenharmony_ci		if (node->next == best) {
1288c2ecf20Sopenharmony_ci			node->next = best->next;
1298c2ecf20Sopenharmony_ci			free(best);
1308c2ecf20Sopenharmony_ci			return list;
1318c2ecf20Sopenharmony_ci		}
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	/* should never get here */
1348c2ecf20Sopenharmony_ci	assert(0);
1358c2ecf20Sopenharmony_ci	return list;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void test_lpm_basic(void)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct tlpm_node *list = NULL, *t1, *t2;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* very basic, static tests to verify tlpm works as expected */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 8));
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	t1 = list = tlpm_add(list, (uint8_t[]){ 0xff }, 8);
1478c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8));
1488c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16));
1498c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0x00 }, 16));
1508c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0x7f }, 8));
1518c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0xfe }, 8));
1528c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 7));
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	t2 = list = tlpm_add(list, (uint8_t[]){ 0xff, 0xff }, 16);
1558c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8));
1568c2ecf20Sopenharmony_ci	assert(t2 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16));
1578c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15));
1588c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0x7f, 0xff }, 16));
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	list = tlpm_delete(list, (uint8_t[]){ 0xff, 0xff }, 16);
1618c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8));
1628c2ecf20Sopenharmony_ci	assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16));
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	list = tlpm_delete(list, (uint8_t[]){ 0xff }, 8);
1658c2ecf20Sopenharmony_ci	assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 8));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	tlpm_clear(list);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void test_lpm_order(void)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct tlpm_node *t1, *t2, *l1 = NULL, *l2 = NULL;
1738c2ecf20Sopenharmony_ci	size_t i, j;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Verify the tlpm implementation works correctly regardless of the
1768c2ecf20Sopenharmony_ci	 * order of entries. Insert a random set of entries into @l1, and copy
1778c2ecf20Sopenharmony_ci	 * the same data in reverse order into @l2. Then verify a lookup of
1788c2ecf20Sopenharmony_ci	 * random keys will yield the same result in both sets.
1798c2ecf20Sopenharmony_ci	 */
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << 12); ++i)
1828c2ecf20Sopenharmony_ci		l1 = tlpm_add(l1, (uint8_t[]){
1838c2ecf20Sopenharmony_ci					rand() % 0xff,
1848c2ecf20Sopenharmony_ci					rand() % 0xff,
1858c2ecf20Sopenharmony_ci				}, rand() % 16 + 1);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	for (t1 = l1; t1; t1 = t1->next)
1888c2ecf20Sopenharmony_ci		l2 = tlpm_add(l2, t1->key, t1->n_bits);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << 8); ++i) {
1918c2ecf20Sopenharmony_ci		uint8_t key[] = { rand() % 0xff, rand() % 0xff };
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		t1 = tlpm_match(l1, key, 16);
1948c2ecf20Sopenharmony_ci		t2 = tlpm_match(l2, key, 16);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		assert(!t1 == !t2);
1978c2ecf20Sopenharmony_ci		if (t1) {
1988c2ecf20Sopenharmony_ci			assert(t1->n_bits == t2->n_bits);
1998c2ecf20Sopenharmony_ci			for (j = 0; j < t1->n_bits; ++j)
2008c2ecf20Sopenharmony_ci				assert((t1->key[j / 8] & (1 << (7 - j % 8))) ==
2018c2ecf20Sopenharmony_ci				       (t2->key[j / 8] & (1 << (7 - j % 8))));
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	tlpm_clear(l1);
2068c2ecf20Sopenharmony_ci	tlpm_clear(l2);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void test_lpm_map(int keysize)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	size_t i, j, n_matches, n_matches_after_delete, n_nodes, n_lookups;
2128c2ecf20Sopenharmony_ci	struct tlpm_node *t, *list = NULL;
2138c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key;
2148c2ecf20Sopenharmony_ci	uint8_t *data, *value;
2158c2ecf20Sopenharmony_ci	int r, map;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Compare behavior of tlpm vs. bpf-lpm. Create a randomized set of
2188c2ecf20Sopenharmony_ci	 * prefixes and insert it into both tlpm and bpf-lpm. Then run some
2198c2ecf20Sopenharmony_ci	 * randomized lookups and verify both maps return the same result.
2208c2ecf20Sopenharmony_ci	 */
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	n_matches = 0;
2238c2ecf20Sopenharmony_ci	n_matches_after_delete = 0;
2248c2ecf20Sopenharmony_ci	n_nodes = 1 << 8;
2258c2ecf20Sopenharmony_ci	n_lookups = 1 << 16;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	data = alloca(keysize);
2288c2ecf20Sopenharmony_ci	memset(data, 0, keysize);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	value = alloca(keysize + 1);
2318c2ecf20Sopenharmony_ci	memset(value, 0, keysize + 1);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	key = alloca(sizeof(*key) + keysize);
2348c2ecf20Sopenharmony_ci	memset(key, 0, sizeof(*key) + keysize);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	map = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
2378c2ecf20Sopenharmony_ci			     sizeof(*key) + keysize,
2388c2ecf20Sopenharmony_ci			     keysize + 1,
2398c2ecf20Sopenharmony_ci			     4096,
2408c2ecf20Sopenharmony_ci			     BPF_F_NO_PREALLOC);
2418c2ecf20Sopenharmony_ci	assert(map >= 0);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	for (i = 0; i < n_nodes; ++i) {
2448c2ecf20Sopenharmony_ci		for (j = 0; j < keysize; ++j)
2458c2ecf20Sopenharmony_ci			value[j] = rand() & 0xff;
2468c2ecf20Sopenharmony_ci		value[keysize] = rand() % (8 * keysize + 1);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		list = tlpm_add(list, value, value[keysize]);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		key->prefixlen = value[keysize];
2518c2ecf20Sopenharmony_ci		memcpy(key->data, value, keysize);
2528c2ecf20Sopenharmony_ci		r = bpf_map_update_elem(map, key, value, 0);
2538c2ecf20Sopenharmony_ci		assert(!r);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (i = 0; i < n_lookups; ++i) {
2578c2ecf20Sopenharmony_ci		for (j = 0; j < keysize; ++j)
2588c2ecf20Sopenharmony_ci			data[j] = rand() & 0xff;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		t = tlpm_match(list, data, 8 * keysize);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		key->prefixlen = 8 * keysize;
2638c2ecf20Sopenharmony_ci		memcpy(key->data, data, keysize);
2648c2ecf20Sopenharmony_ci		r = bpf_map_lookup_elem(map, key, value);
2658c2ecf20Sopenharmony_ci		assert(!r || errno == ENOENT);
2668c2ecf20Sopenharmony_ci		assert(!t == !!r);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		if (t) {
2698c2ecf20Sopenharmony_ci			++n_matches;
2708c2ecf20Sopenharmony_ci			assert(t->n_bits == value[keysize]);
2718c2ecf20Sopenharmony_ci			for (j = 0; j < t->n_bits; ++j)
2728c2ecf20Sopenharmony_ci				assert((t->key[j / 8] & (1 << (7 - j % 8))) ==
2738c2ecf20Sopenharmony_ci				       (value[j / 8] & (1 << (7 - j % 8))));
2748c2ecf20Sopenharmony_ci		}
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Remove the first half of the elements in the tlpm and the
2788c2ecf20Sopenharmony_ci	 * corresponding nodes from the bpf-lpm.  Then run the same
2798c2ecf20Sopenharmony_ci	 * large number of random lookups in both and make sure they match.
2808c2ecf20Sopenharmony_ci	 * Note: we need to count the number of nodes actually inserted
2818c2ecf20Sopenharmony_ci	 * since there may have been duplicates.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	for (i = 0, t = list; t; i++, t = t->next)
2848c2ecf20Sopenharmony_ci		;
2858c2ecf20Sopenharmony_ci	for (j = 0; j < i / 2; ++j) {
2868c2ecf20Sopenharmony_ci		key->prefixlen = list->n_bits;
2878c2ecf20Sopenharmony_ci		memcpy(key->data, list->key, keysize);
2888c2ecf20Sopenharmony_ci		r = bpf_map_delete_elem(map, key);
2898c2ecf20Sopenharmony_ci		assert(!r);
2908c2ecf20Sopenharmony_ci		list = tlpm_delete(list, list->key, list->n_bits);
2918c2ecf20Sopenharmony_ci		assert(list);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci	for (i = 0; i < n_lookups; ++i) {
2948c2ecf20Sopenharmony_ci		for (j = 0; j < keysize; ++j)
2958c2ecf20Sopenharmony_ci			data[j] = rand() & 0xff;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		t = tlpm_match(list, data, 8 * keysize);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		key->prefixlen = 8 * keysize;
3008c2ecf20Sopenharmony_ci		memcpy(key->data, data, keysize);
3018c2ecf20Sopenharmony_ci		r = bpf_map_lookup_elem(map, key, value);
3028c2ecf20Sopenharmony_ci		assert(!r || errno == ENOENT);
3038c2ecf20Sopenharmony_ci		assert(!t == !!r);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		if (t) {
3068c2ecf20Sopenharmony_ci			++n_matches_after_delete;
3078c2ecf20Sopenharmony_ci			assert(t->n_bits == value[keysize]);
3088c2ecf20Sopenharmony_ci			for (j = 0; j < t->n_bits; ++j)
3098c2ecf20Sopenharmony_ci				assert((t->key[j / 8] & (1 << (7 - j % 8))) ==
3108c2ecf20Sopenharmony_ci				       (value[j / 8] & (1 << (7 - j % 8))));
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	close(map);
3158c2ecf20Sopenharmony_ci	tlpm_clear(list);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* With 255 random nodes in the map, we are pretty likely to match
3188c2ecf20Sopenharmony_ci	 * something on every lookup. For statistics, use this:
3198c2ecf20Sopenharmony_ci	 *
3208c2ecf20Sopenharmony_ci	 *     printf("          nodes: %zu\n"
3218c2ecf20Sopenharmony_ci	 *            "        lookups: %zu\n"
3228c2ecf20Sopenharmony_ci	 *            "        matches: %zu\n"
3238c2ecf20Sopenharmony_ci	 *            "matches(delete): %zu\n",
3248c2ecf20Sopenharmony_ci	 *            n_nodes, n_lookups, n_matches, n_matches_after_delete);
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/* Test the implementation with some 'real world' examples */
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void test_lpm_ipaddr(void)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key_ipv4;
3338c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key_ipv6;
3348c2ecf20Sopenharmony_ci	size_t key_size_ipv4;
3358c2ecf20Sopenharmony_ci	size_t key_size_ipv6;
3368c2ecf20Sopenharmony_ci	int map_fd_ipv4;
3378c2ecf20Sopenharmony_ci	int map_fd_ipv6;
3388c2ecf20Sopenharmony_ci	__u64 value;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	key_size_ipv4 = sizeof(*key_ipv4) + sizeof(__u32);
3418c2ecf20Sopenharmony_ci	key_size_ipv6 = sizeof(*key_ipv6) + sizeof(__u32) * 4;
3428c2ecf20Sopenharmony_ci	key_ipv4 = alloca(key_size_ipv4);
3438c2ecf20Sopenharmony_ci	key_ipv6 = alloca(key_size_ipv6);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	map_fd_ipv4 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
3468c2ecf20Sopenharmony_ci				     key_size_ipv4, sizeof(value),
3478c2ecf20Sopenharmony_ci				     100, BPF_F_NO_PREALLOC);
3488c2ecf20Sopenharmony_ci	assert(map_fd_ipv4 >= 0);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	map_fd_ipv6 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
3518c2ecf20Sopenharmony_ci				     key_size_ipv6, sizeof(value),
3528c2ecf20Sopenharmony_ci				     100, BPF_F_NO_PREALLOC);
3538c2ecf20Sopenharmony_ci	assert(map_fd_ipv6 >= 0);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Fill data some IPv4 and IPv6 address ranges */
3568c2ecf20Sopenharmony_ci	value = 1;
3578c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 16;
3588c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
3598c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	value = 2;
3628c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 24;
3638c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
3648c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	value = 3;
3678c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 24;
3688c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.0", key_ipv4->data);
3698c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	value = 5;
3728c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 24;
3738c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", key_ipv4->data);
3748c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	value = 4;
3778c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 23;
3788c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
3798c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	value = 0xdeadbeef;
3828c2ecf20Sopenharmony_ci	key_ipv6->prefixlen = 64;
3838c2ecf20Sopenharmony_ci	inet_pton(AF_INET6, "2a00:1450:4001:814::200e", key_ipv6->data);
3848c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd_ipv6, key_ipv6, &value, 0) == 0);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Set tprefixlen to maximum for lookups */
3878c2ecf20Sopenharmony_ci	key_ipv4->prefixlen = 32;
3888c2ecf20Sopenharmony_ci	key_ipv6->prefixlen = 128;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Test some lookups that should come back with a value */
3918c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.23", key_ipv4->data);
3928c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0);
3938c2ecf20Sopenharmony_ci	assert(value == 3);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.1", key_ipv4->data);
3968c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0);
3978c2ecf20Sopenharmony_ci	assert(value == 2);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	inet_pton(AF_INET6, "2a00:1450:4001:814::", key_ipv6->data);
4008c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0);
4018c2ecf20Sopenharmony_ci	assert(value == 0xdeadbeef);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	inet_pton(AF_INET6, "2a00:1450:4001:814::1", key_ipv6->data);
4048c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0);
4058c2ecf20Sopenharmony_ci	assert(value == 0xdeadbeef);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* Test some lookups that should not match any entry */
4088c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "10.0.0.1", key_ipv4->data);
4098c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 &&
4108c2ecf20Sopenharmony_ci	       errno == ENOENT);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "11.11.11.11", key_ipv4->data);
4138c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 &&
4148c2ecf20Sopenharmony_ci	       errno == ENOENT);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	inet_pton(AF_INET6, "2a00:ffff::", key_ipv6->data);
4178c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == -1 &&
4188c2ecf20Sopenharmony_ci	       errno == ENOENT);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	close(map_fd_ipv4);
4218c2ecf20Sopenharmony_ci	close(map_fd_ipv6);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void test_lpm_delete(void)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key;
4278c2ecf20Sopenharmony_ci	size_t key_size;
4288c2ecf20Sopenharmony_ci	int map_fd;
4298c2ecf20Sopenharmony_ci	__u64 value;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	key_size = sizeof(*key) + sizeof(__u32);
4328c2ecf20Sopenharmony_ci	key = alloca(key_size);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
4358c2ecf20Sopenharmony_ci				key_size, sizeof(value),
4368c2ecf20Sopenharmony_ci				100, BPF_F_NO_PREALLOC);
4378c2ecf20Sopenharmony_ci	assert(map_fd >= 0);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Add nodes:
4408c2ecf20Sopenharmony_ci	 * 192.168.0.0/16   (1)
4418c2ecf20Sopenharmony_ci	 * 192.168.0.0/24   (2)
4428c2ecf20Sopenharmony_ci	 * 192.168.128.0/24 (3)
4438c2ecf20Sopenharmony_ci	 * 192.168.1.0/24   (4)
4448c2ecf20Sopenharmony_ci	 *
4458c2ecf20Sopenharmony_ci	 *         (1)
4468c2ecf20Sopenharmony_ci	 *        /   \
4478c2ecf20Sopenharmony_ci         *     (IM)    (3)
4488c2ecf20Sopenharmony_ci	 *    /   \
4498c2ecf20Sopenharmony_ci         *   (2)  (4)
4508c2ecf20Sopenharmony_ci	 */
4518c2ecf20Sopenharmony_ci	value = 1;
4528c2ecf20Sopenharmony_ci	key->prefixlen = 16;
4538c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key->data);
4548c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	value = 2;
4578c2ecf20Sopenharmony_ci	key->prefixlen = 24;
4588c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key->data);
4598c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	value = 3;
4628c2ecf20Sopenharmony_ci	key->prefixlen = 24;
4638c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.0", key->data);
4648c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	value = 4;
4678c2ecf20Sopenharmony_ci	key->prefixlen = 24;
4688c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", key->data);
4698c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* remove non-existent node */
4728c2ecf20Sopenharmony_ci	key->prefixlen = 32;
4738c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "10.0.0.1", key->data);
4748c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
4758c2ecf20Sopenharmony_ci		errno == ENOENT);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	key->prefixlen = 30; // unused prefix so far
4788c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.255.0.0", key->data);
4798c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == -1 &&
4808c2ecf20Sopenharmony_ci		errno == ENOENT);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	key->prefixlen = 16; // same prefix as the root node
4838c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.255.0.0", key->data);
4848c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == -1 &&
4858c2ecf20Sopenharmony_ci		errno == ENOENT);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* assert initial lookup */
4888c2ecf20Sopenharmony_ci	key->prefixlen = 32;
4898c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.1", key->data);
4908c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
4918c2ecf20Sopenharmony_ci	assert(value == 2);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* remove leaf node */
4948c2ecf20Sopenharmony_ci	key->prefixlen = 24;
4958c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key->data);
4968c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == 0);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	key->prefixlen = 32;
4998c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.1", key->data);
5008c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
5018c2ecf20Sopenharmony_ci	assert(value == 1);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* remove leaf (and intermediary) node */
5048c2ecf20Sopenharmony_ci	key->prefixlen = 24;
5058c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", key->data);
5068c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == 0);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	key->prefixlen = 32;
5098c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.1", key->data);
5108c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
5118c2ecf20Sopenharmony_ci	assert(value == 1);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* remove root node */
5148c2ecf20Sopenharmony_ci	key->prefixlen = 16;
5158c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key->data);
5168c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == 0);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	key->prefixlen = 32;
5198c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.1", key->data);
5208c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
5218c2ecf20Sopenharmony_ci	assert(value == 3);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* remove last node */
5248c2ecf20Sopenharmony_ci	key->prefixlen = 24;
5258c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.0", key->data);
5268c2ecf20Sopenharmony_ci	assert(bpf_map_delete_elem(map_fd, key) == 0);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	key->prefixlen = 32;
5298c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.1", key->data);
5308c2ecf20Sopenharmony_ci	assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
5318c2ecf20Sopenharmony_ci		errno == ENOENT);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	close(map_fd);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void test_lpm_get_next_key(void)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key_p, *next_key_p;
5398c2ecf20Sopenharmony_ci	size_t key_size;
5408c2ecf20Sopenharmony_ci	__u32 value = 0;
5418c2ecf20Sopenharmony_ci	int map_fd;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	key_size = sizeof(*key_p) + sizeof(__u32);
5448c2ecf20Sopenharmony_ci	key_p = alloca(key_size);
5458c2ecf20Sopenharmony_ci	next_key_p = alloca(key_size);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, sizeof(value),
5488c2ecf20Sopenharmony_ci				100, BPF_F_NO_PREALLOC);
5498c2ecf20Sopenharmony_ci	assert(map_fd >= 0);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* empty tree. get_next_key should return ENOENT */
5528c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == -1 &&
5538c2ecf20Sopenharmony_ci	       errno == ENOENT);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* get and verify the first key, get the second one should fail. */
5568c2ecf20Sopenharmony_ci	key_p->prefixlen = 16;
5578c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key_p->data);
5588c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	memset(key_p, 0, key_size);
5618c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
5628c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 16 && key_p->data[0] == 192 &&
5638c2ecf20Sopenharmony_ci	       key_p->data[1] == 168);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
5668c2ecf20Sopenharmony_ci	       errno == ENOENT);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* no exact matching key should get the first one in post order. */
5698c2ecf20Sopenharmony_ci	key_p->prefixlen = 8;
5708c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
5718c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 16 && key_p->data[0] == 192 &&
5728c2ecf20Sopenharmony_ci	       key_p->data[1] == 168);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* add one more element (total two) */
5758c2ecf20Sopenharmony_ci	key_p->prefixlen = 24;
5768c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.0", key_p->data);
5778c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	memset(key_p, 0, key_size);
5808c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
5818c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
5828c2ecf20Sopenharmony_ci	       key_p->data[1] == 168 && key_p->data[2] == 128);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	memset(next_key_p, 0, key_size);
5858c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
5868c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
5878c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
5908c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
5918c2ecf20Sopenharmony_ci	       errno == ENOENT);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	/* Add one more element (total three) */
5948c2ecf20Sopenharmony_ci	key_p->prefixlen = 24;
5958c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", key_p->data);
5968c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	memset(key_p, 0, key_size);
5998c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
6008c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
6018c2ecf20Sopenharmony_ci	       key_p->data[1] == 168 && key_p->data[2] == 0);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	memset(next_key_p, 0, key_size);
6048c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6058c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6068c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6098c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6108c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
6118c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6148c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
6158c2ecf20Sopenharmony_ci	       errno == ENOENT);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	/* Add one more element (total four) */
6188c2ecf20Sopenharmony_ci	key_p->prefixlen = 24;
6198c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", key_p->data);
6208c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	memset(key_p, 0, key_size);
6238c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
6248c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
6258c2ecf20Sopenharmony_ci	       key_p->data[1] == 168 && key_p->data[2] == 0);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	memset(next_key_p, 0, key_size);
6288c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6298c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6308c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 1);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6338c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6348c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6358c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6388c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6398c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
6408c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6438c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
6448c2ecf20Sopenharmony_ci	       errno == ENOENT);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Add one more element (total five) */
6478c2ecf20Sopenharmony_ci	key_p->prefixlen = 28;
6488c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.128", key_p->data);
6498c2ecf20Sopenharmony_ci	assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	memset(key_p, 0, key_size);
6528c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
6538c2ecf20Sopenharmony_ci	assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
6548c2ecf20Sopenharmony_ci	       key_p->data[1] == 168 && key_p->data[2] == 0);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	memset(next_key_p, 0, key_size);
6578c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6588c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 28 && next_key_p->data[0] == 192 &&
6598c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 1 &&
6608c2ecf20Sopenharmony_ci	       next_key_p->data[3] == 128);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6638c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6648c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6658c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 1);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6688c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6698c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6708c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6738c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6748c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
6758c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	memcpy(key_p, next_key_p, key_size);
6788c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
6798c2ecf20Sopenharmony_ci	       errno == ENOENT);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* no exact matching key should return the first one in post order */
6828c2ecf20Sopenharmony_ci	key_p->prefixlen = 22;
6838c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", key_p->data);
6848c2ecf20Sopenharmony_ci	assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
6858c2ecf20Sopenharmony_ci	assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
6868c2ecf20Sopenharmony_ci	       next_key_p->data[1] == 168 && next_key_p->data[2] == 0);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	close(map_fd);
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci#define MAX_TEST_KEYS	4
6928c2ecf20Sopenharmony_cistruct lpm_mt_test_info {
6938c2ecf20Sopenharmony_ci	int cmd; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */
6948c2ecf20Sopenharmony_ci	int iter;
6958c2ecf20Sopenharmony_ci	int map_fd;
6968c2ecf20Sopenharmony_ci	struct {
6978c2ecf20Sopenharmony_ci		__u32 prefixlen;
6988c2ecf20Sopenharmony_ci		__u32 data;
6998c2ecf20Sopenharmony_ci	} key[MAX_TEST_KEYS];
7008c2ecf20Sopenharmony_ci};
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic void *lpm_test_command(void *arg)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	int i, j, ret, iter, key_size;
7058c2ecf20Sopenharmony_ci	struct lpm_mt_test_info *info = arg;
7068c2ecf20Sopenharmony_ci	struct bpf_lpm_trie_key *key_p;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	key_size = sizeof(struct bpf_lpm_trie_key) + sizeof(__u32);
7098c2ecf20Sopenharmony_ci	key_p = alloca(key_size);
7108c2ecf20Sopenharmony_ci	for (iter = 0; iter < info->iter; iter++)
7118c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_TEST_KEYS; i++) {
7128c2ecf20Sopenharmony_ci			/* first half of iterations in forward order,
7138c2ecf20Sopenharmony_ci			 * and second half in backward order.
7148c2ecf20Sopenharmony_ci			 */
7158c2ecf20Sopenharmony_ci			j = (iter < (info->iter / 2)) ? i : MAX_TEST_KEYS - i - 1;
7168c2ecf20Sopenharmony_ci			key_p->prefixlen = info->key[j].prefixlen;
7178c2ecf20Sopenharmony_ci			memcpy(key_p->data, &info->key[j].data, sizeof(__u32));
7188c2ecf20Sopenharmony_ci			if (info->cmd == 0) {
7198c2ecf20Sopenharmony_ci				__u32 value = j;
7208c2ecf20Sopenharmony_ci				/* update must succeed */
7218c2ecf20Sopenharmony_ci				assert(bpf_map_update_elem(info->map_fd, key_p, &value, 0) == 0);
7228c2ecf20Sopenharmony_ci			} else if (info->cmd == 1) {
7238c2ecf20Sopenharmony_ci				ret = bpf_map_delete_elem(info->map_fd, key_p);
7248c2ecf20Sopenharmony_ci				assert(ret == 0 || errno == ENOENT);
7258c2ecf20Sopenharmony_ci			} else if (info->cmd == 2) {
7268c2ecf20Sopenharmony_ci				__u32 value;
7278c2ecf20Sopenharmony_ci				ret = bpf_map_lookup_elem(info->map_fd, key_p, &value);
7288c2ecf20Sopenharmony_ci				assert(ret == 0 || errno == ENOENT);
7298c2ecf20Sopenharmony_ci			} else {
7308c2ecf20Sopenharmony_ci				struct bpf_lpm_trie_key *next_key_p = alloca(key_size);
7318c2ecf20Sopenharmony_ci				ret = bpf_map_get_next_key(info->map_fd, key_p, next_key_p);
7328c2ecf20Sopenharmony_ci				assert(ret == 0 || errno == ENOENT || errno == ENOMEM);
7338c2ecf20Sopenharmony_ci			}
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	// Pass successful exit info back to the main thread
7378c2ecf20Sopenharmony_ci	pthread_exit((void *)info);
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic void setup_lpm_mt_test_info(struct lpm_mt_test_info *info, int map_fd)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	info->iter = 2000;
7438c2ecf20Sopenharmony_ci	info->map_fd = map_fd;
7448c2ecf20Sopenharmony_ci	info->key[0].prefixlen = 16;
7458c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", &info->key[0].data);
7468c2ecf20Sopenharmony_ci	info->key[1].prefixlen = 24;
7478c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.0.0", &info->key[1].data);
7488c2ecf20Sopenharmony_ci	info->key[2].prefixlen = 24;
7498c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.128.0", &info->key[2].data);
7508c2ecf20Sopenharmony_ci	info->key[3].prefixlen = 24;
7518c2ecf20Sopenharmony_ci	inet_pton(AF_INET, "192.168.1.0", &info->key[3].data);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic void test_lpm_multi_thread(void)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	struct lpm_mt_test_info info[4];
7578c2ecf20Sopenharmony_ci	size_t key_size, value_size;
7588c2ecf20Sopenharmony_ci	pthread_t thread_id[4];
7598c2ecf20Sopenharmony_ci	int i, map_fd;
7608c2ecf20Sopenharmony_ci	void *ret;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	/* create a trie */
7638c2ecf20Sopenharmony_ci	value_size = sizeof(__u32);
7648c2ecf20Sopenharmony_ci	key_size = sizeof(struct bpf_lpm_trie_key) + value_size;
7658c2ecf20Sopenharmony_ci	map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, value_size,
7668c2ecf20Sopenharmony_ci				100, BPF_F_NO_PREALLOC);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* create 4 threads to test update, delete, lookup and get_next_key */
7698c2ecf20Sopenharmony_ci	setup_lpm_mt_test_info(&info[0], map_fd);
7708c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
7718c2ecf20Sopenharmony_ci		if (i != 0)
7728c2ecf20Sopenharmony_ci			memcpy(&info[i], &info[0], sizeof(info[i]));
7738c2ecf20Sopenharmony_ci		info[i].cmd = i;
7748c2ecf20Sopenharmony_ci		assert(pthread_create(&thread_id[i], NULL, &lpm_test_command, &info[i]) == 0);
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
7788c2ecf20Sopenharmony_ci		assert(pthread_join(thread_id[i], &ret) == 0 && ret == (void *)&info[i]);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	close(map_fd);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ciint main(void)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	int i;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* we want predictable, pseudo random tests */
7888c2ecf20Sopenharmony_ci	srand(0xf00ba1);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	test_lpm_basic();
7918c2ecf20Sopenharmony_ci	test_lpm_order();
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* Test with 8, 16, 24, 32, ... 128 bit prefix length */
7948c2ecf20Sopenharmony_ci	for (i = 1; i <= 16; ++i)
7958c2ecf20Sopenharmony_ci		test_lpm_map(i);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	test_lpm_ipaddr();
7988c2ecf20Sopenharmony_ci	test_lpm_delete();
7998c2ecf20Sopenharmony_ci	test_lpm_get_next_key();
8008c2ecf20Sopenharmony_ci	test_lpm_multi_thread();
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	printf("test_lpm: OK\n");
8038c2ecf20Sopenharmony_ci	return 0;
8048c2ecf20Sopenharmony_ci}
805