|
@@ -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;
|
|
|
}
|