Skip to content

Commit

Permalink
bpf: Test deletion in BPF_MAP_TYPE_LPM_TRIE
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Craig Gallek authored and David S. Miller committed Sep 19, 2017
1 parent bae3046 commit e8d1749
Showing 1 changed file with 183 additions and 4 deletions.
187 changes: 183 additions & 4 deletions tools/testing/selftests/bpf/test_lpm_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);
*/
}

Expand Down Expand Up @@ -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 };
Expand All @@ -365,6 +542,8 @@ int main(void)

test_lpm_ipaddr();

test_lpm_delete();

printf("test_lpm: OK\n");
return 0;
}

0 comments on commit e8d1749

Please sign in to comment.