mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 16:41:58 +00:00
bpf: Test deletion in BPF_MAP_TYPE_LPM_TRIE
Extend the 'random' operation tests to include a delete operation (delete half of the nodes from both lpm implementions and ensure that lookups are still equivalent). Also, add a simple IPv4 test which verifies lookup behavior as nodes are deleted from the tree. Signed-off-by: Craig Gallek <kraig@google.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bae30468df
commit
e8d1749910
@ -104,6 +104,34 @@ static struct tlpm_node *tlpm_match(struct tlpm_node *list,
|
||||
return best;
|
||||
}
|
||||
|
||||
static struct tlpm_node *tlpm_delete(struct tlpm_node *list,
|
||||
const uint8_t *key,
|
||||
size_t n_bits)
|
||||
{
|
||||
struct tlpm_node *best = tlpm_match(list, key, n_bits);
|
||||
struct tlpm_node *node;
|
||||
|
||||
if (!best || best->n_bits != n_bits)
|
||||
return list;
|
||||
|
||||
if (best == list) {
|
||||
node = best->next;
|
||||
free(best);
|
||||
return node;
|
||||
}
|
||||
|
||||
for (node = list; node; node = node->next) {
|
||||
if (node->next == best) {
|
||||
node->next = best->next;
|
||||
free(best);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
/* should never get here */
|
||||
assert(0);
|
||||
return list;
|
||||
}
|
||||
|
||||
static void test_lpm_basic(void)
|
||||
{
|
||||
struct tlpm_node *list = NULL, *t1, *t2;
|
||||
@ -126,6 +154,13 @@ static void test_lpm_basic(void)
|
||||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15));
|
||||
assert(!tlpm_match(list, (uint8_t[]){ 0x7f, 0xff }, 16));
|
||||
|
||||
list = tlpm_delete(list, (uint8_t[]){ 0xff, 0xff }, 16);
|
||||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8));
|
||||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16));
|
||||
|
||||
list = tlpm_delete(list, (uint8_t[]){ 0xff }, 8);
|
||||
assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 8));
|
||||
|
||||
tlpm_clear(list);
|
||||
}
|
||||
|
||||
@ -170,7 +205,7 @@ static void test_lpm_order(void)
|
||||
|
||||
static void test_lpm_map(int keysize)
|
||||
{
|
||||
size_t i, j, n_matches, n_nodes, n_lookups;
|
||||
size_t i, j, n_matches, n_matches_after_delete, n_nodes, n_lookups;
|
||||
struct tlpm_node *t, *list = NULL;
|
||||
struct bpf_lpm_trie_key *key;
|
||||
uint8_t *data, *value;
|
||||
@ -182,6 +217,7 @@ static void test_lpm_map(int keysize)
|
||||
*/
|
||||
|
||||
n_matches = 0;
|
||||
n_matches_after_delete = 0;
|
||||
n_nodes = 1 << 8;
|
||||
n_lookups = 1 << 16;
|
||||
|
||||
@ -235,15 +271,54 @@ static void test_lpm_map(int keysize)
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the first half of the elements in the tlpm and the
|
||||
* corresponding nodes from the bpf-lpm. Then run the same
|
||||
* large number of random lookups in both and make sure they match.
|
||||
* Note: we need to count the number of nodes actually inserted
|
||||
* since there may have been duplicates.
|
||||
*/
|
||||
for (i = 0, t = list; t; i++, t = t->next)
|
||||
;
|
||||
for (j = 0; j < i / 2; ++j) {
|
||||
key->prefixlen = list->n_bits;
|
||||
memcpy(key->data, list->key, keysize);
|
||||
r = bpf_map_delete_elem(map, key);
|
||||
assert(!r);
|
||||
list = tlpm_delete(list, list->key, list->n_bits);
|
||||
assert(list);
|
||||
}
|
||||
for (i = 0; i < n_lookups; ++i) {
|
||||
for (j = 0; j < keysize; ++j)
|
||||
data[j] = rand() & 0xff;
|
||||
|
||||
t = tlpm_match(list, data, 8 * keysize);
|
||||
|
||||
key->prefixlen = 8 * keysize;
|
||||
memcpy(key->data, data, keysize);
|
||||
r = bpf_map_lookup_elem(map, key, value);
|
||||
assert(!r || errno == ENOENT);
|
||||
assert(!t == !!r);
|
||||
|
||||
if (t) {
|
||||
++n_matches_after_delete;
|
||||
assert(t->n_bits == value[keysize]);
|
||||
for (j = 0; j < t->n_bits; ++j)
|
||||
assert((t->key[j / 8] & (1 << (7 - j % 8))) ==
|
||||
(value[j / 8] & (1 << (7 - j % 8))));
|
||||
}
|
||||
}
|
||||
|
||||
close(map);
|
||||
tlpm_clear(list);
|
||||
|
||||
/* With 255 random nodes in the map, we are pretty likely to match
|
||||
* something on every lookup. For statistics, use this:
|
||||
*
|
||||
* printf(" nodes: %zu\n"
|
||||
* "lookups: %zu\n"
|
||||
* "matches: %zu\n", n_nodes, n_lookups, n_matches);
|
||||
* printf(" nodes: %zu\n"
|
||||
* " lookups: %zu\n"
|
||||
* " matches: %zu\n"
|
||||
* "matches(delete): %zu\n",
|
||||
* n_nodes, n_lookups, n_matches, n_matches_after_delete);
|
||||
*/
|
||||
}
|
||||
|
||||
@ -343,6 +418,108 @@ static void test_lpm_ipaddr(void)
|
||||
close(map_fd_ipv6);
|
||||
}
|
||||
|
||||
static void test_lpm_delete(void)
|
||||
{
|
||||
struct bpf_lpm_trie_key *key;
|
||||
size_t key_size;
|
||||
int map_fd;
|
||||
__u64 value;
|
||||
|
||||
key_size = sizeof(*key) + sizeof(__u32);
|
||||
key = alloca(key_size);
|
||||
|
||||
map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
|
||||
key_size, sizeof(value),
|
||||
100, BPF_F_NO_PREALLOC);
|
||||
assert(map_fd >= 0);
|
||||
|
||||
/* Add nodes:
|
||||
* 192.168.0.0/16 (1)
|
||||
* 192.168.0.0/24 (2)
|
||||
* 192.168.128.0/24 (3)
|
||||
* 192.168.1.0/24 (4)
|
||||
*
|
||||
* (1)
|
||||
* / \
|
||||
* (IM) (3)
|
||||
* / \
|
||||
* (2) (4)
|
||||
*/
|
||||
value = 1;
|
||||
key->prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 2;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 3;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 4;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
/* remove non-existent node */
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "10.0.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* assert initial lookup */
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 2);
|
||||
|
||||
/* remove leaf node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 1);
|
||||
|
||||
/* remove leaf (and intermediary) node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.1.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 1);
|
||||
|
||||
/* remove root node */
|
||||
key->prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.128.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 3);
|
||||
|
||||
/* remove last node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.128.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
close(map_fd);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
@ -365,6 +542,8 @@ int main(void)
|
||||
|
||||
test_lpm_ipaddr();
|
||||
|
||||
test_lpm_delete();
|
||||
|
||||
printf("test_lpm: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user