Commit e8d17499 authored by Craig Gallek's avatar Craig Gallek Committed by David S. Miller

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: default avatarCraig Gallek <kraig@google.com>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bae30468
...@@ -104,6 +104,34 @@ static struct tlpm_node *tlpm_match(struct tlpm_node *list, ...@@ -104,6 +104,34 @@ static struct tlpm_node *tlpm_match(struct tlpm_node *list,
return best; 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) static void test_lpm_basic(void)
{ {
struct tlpm_node *list = NULL, *t1, *t2; struct tlpm_node *list = NULL, *t1, *t2;
...@@ -126,6 +154,13 @@ static void test_lpm_basic(void) ...@@ -126,6 +154,13 @@ static void test_lpm_basic(void)
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15)); assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15));
assert(!tlpm_match(list, (uint8_t[]){ 0x7f, 0xff }, 16)); 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); tlpm_clear(list);
} }
...@@ -170,7 +205,7 @@ static void test_lpm_order(void) ...@@ -170,7 +205,7 @@ static void test_lpm_order(void)
static void test_lpm_map(int keysize) 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 tlpm_node *t, *list = NULL;
struct bpf_lpm_trie_key *key; struct bpf_lpm_trie_key *key;
uint8_t *data, *value; uint8_t *data, *value;
...@@ -182,6 +217,7 @@ static void test_lpm_map(int keysize) ...@@ -182,6 +217,7 @@ static void test_lpm_map(int keysize)
*/ */
n_matches = 0; n_matches = 0;
n_matches_after_delete = 0;
n_nodes = 1 << 8; n_nodes = 1 << 8;
n_lookups = 1 << 16; n_lookups = 1 << 16;
...@@ -235,15 +271,54 @@ static void test_lpm_map(int keysize) ...@@ -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); close(map);
tlpm_clear(list); tlpm_clear(list);
/* With 255 random nodes in the map, we are pretty likely to match /* With 255 random nodes in the map, we are pretty likely to match
* something on every lookup. For statistics, use this: * something on every lookup. For statistics, use this:
* *
* printf(" nodes: %zu\n" * printf(" nodes: %zu\n"
* "lookups: %zu\n" * " lookups: %zu\n"
* "matches: %zu\n", n_nodes, n_lookups, n_matches); * " 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) ...@@ -343,6 +418,108 @@ static void test_lpm_ipaddr(void)
close(map_fd_ipv6); 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) int main(void)
{ {
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
...@@ -365,6 +542,8 @@ int main(void) ...@@ -365,6 +542,8 @@ int main(void)
test_lpm_ipaddr(); test_lpm_ipaddr();
test_lpm_delete();
printf("test_lpm: OK\n"); printf("test_lpm: OK\n");
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment