If the leftmost parent node of the tree has does not have a child on the left side, then trie_get_next_key (and bpftool map dump) will not look at the child on the right. This leads to the traversal missing elements. Lookup is not affected. Update selftest to handle this case. Reproducer: bpftool map create /sys/fs/bpf/lpm type lpm_trie key 6 \ value 1 entries 256 name test_lpm flags 1 bpftool map update pinned /sys/fs/bpf/lpm key 8 0 0 0 0 0 value 1 bpftool map update pinned /sys/fs/bpf/lpm key 16 0 0 0 0 128 value 2 bpftool map dump pinned /sys/fs/bpf/lpm Returns only 1 element. (2 expected)
Unified · Split
| @@ -716,9 +716,14 @@ find_leftmost: · 715 unmodified lines | |||
| 716 | * have exact two children, so this function will never return NULL. | 716 | * have exact two children, so this function will never return NULL. |
| 717 | */ | 717 | */ |
| 718 | for (node = search_root; node;) { | 718 | for (node = search_root; node;) { |
| 719 | if (!(node->flags & LPM_TREE_NODE_FLAG_IM)) | 719 | if (node->flags & LPM_TREE_NODE_FLAG_IM) { |
| 720 | node = rcu_dereference(node->child[0]); | ||
| 721 | } else { | ||
| 720 | next_node = node; | 722 | next_node = node; |
| 721 | node = rcu_dereference(node->child[0]); | 723 | node = rcu_dereference(node->child[0]); |
| 724 | if (!node) | ||
| 725 | node = rcu_dereference(next_node->child[1]); | ||
| 726 | } | ||
| 722 | } | 727 | } |
| 723 | do_copy: | 728 | do_copy: |
| 724 | next_key->prefixlen = next_node->prefixlen; | 729 | next_key->prefixlen = next_node->prefixlen; |
| @@ -573,13 +573,13 @@ static void test_lpm_get_next_key(void) · 572 unmodified lines | |||
| 573 | 573 | ||
| 574 | /* add one more element (total two) */ | 574 | /* add one more element (total two) */ |
| 575 | key_p->prefixlen = 24; | 575 | key_p->prefixlen = 24; |
| 576 | inet_pton(AF_INET, "192.168.0.0", key_p->data); | 576 | inet_pton(AF_INET, "192.168.128.0", key_p->data); |
| 577 | assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); | 577 | assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); |
| 578 | 578 | ||
| 579 | memset(key_p, 0, key_size); | 579 | memset(key_p, 0, key_size); |
| 580 | assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); | 580 | assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); |
| 581 | assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && | 581 | assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && |
| 582 | key_p->data[1] == 168 && key_p->data[2] == 0); | 582 | key_p->data[1] == 168 && key_p->data[2] == 128); |
| 583 | 583 | ||
| 584 | memset(next_key_p, 0, key_size); | 584 | memset(next_key_p, 0, key_size); |
| 585 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); | 585 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); |
| @@ -592,7 +592,7 @@ static void test_lpm_get_next_key(void) · 6 unmodified lines | |||
| 592 | 592 | ||
| 593 | /* Add one more element (total three) */ | 593 | /* Add one more element (total three) */ |
| 594 | key_p->prefixlen = 24; | 594 | key_p->prefixlen = 24; |
| 595 | inet_pton(AF_INET, "192.168.128.0", key_p->data); | 595 | inet_pton(AF_INET, "192.168.0.0", key_p->data); |
| 596 | assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); | 596 | assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); |
| 597 | 597 | ||
| 598 | memset(key_p, 0, key_size); | 598 | memset(key_p, 0, key_size); |
| @@ -643,6 +643,41 @@ static void test_lpm_get_next_key(void) · 44 unmodified lines | |||
| 643 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && | 643 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && |
| 644 | errno == ENOENT); | 644 | errno == ENOENT); |
| 645 | 645 | ||
| 646 | /* Add one more element (total five) */ | ||
| 647 | key_p->prefixlen = 28; | ||
| 648 | inet_pton(AF_INET, "192.168.1.128", key_p->data); | ||
| 649 | assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0); | ||
| 650 | |||
| 651 | memset(key_p, 0, key_size); | ||
| 652 | assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0); | ||
| 653 | assert(key_p->prefixlen == 24 && key_p->data[0] == 192 && | ||
| 654 | key_p->data[1] == 168 && key_p->data[2] == 0); | ||
| 655 | |||
| 656 | memset(next_key_p, 0, key_size); | ||
| 657 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); | ||
| 658 | assert(next_key_p->prefixlen == 28 && next_key_p->data[0] == 192 && | ||
| 659 | next_key_p->data[1] == 168 && next_key_p->data[2] == 1 && | ||
| 660 | next_key_p->data[3] == 128); | ||
| 661 | |||
| 662 | memcpy(key_p, next_key_p, key_size); | ||
| 663 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); | ||
| 664 | assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && | ||
| 665 | next_key_p->data[1] == 168 && next_key_p->data[2] == 1); | ||
| 666 | |||
| 667 | memcpy(key_p, next_key_p, key_size); | ||
| 668 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); | ||
| 669 | assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 && | ||
| 670 | next_key_p->data[1] == 168 && next_key_p->data[2] == 128); | ||
| 671 | |||
| 672 | memcpy(key_p, next_key_p, key_size); | ||
| 673 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0); | ||
| 674 | assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 && | ||
| 675 | next_key_p->data[1] == 168); | ||
| 676 | |||
| 677 | memcpy(key_p, next_key_p, key_size); | ||
| 678 | assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 && | ||
| 679 | errno == ENOENT); | ||
| 680 | |||
| 646 | /* no exact matching key should return the first one in post order */ | 681 | /* no exact matching key should return the first one in post order */ |
| 647 | key_p->prefixlen = 22; | 682 | key_p->prefixlen = 22; |
| 648 | inet_pton(AF_INET, "192.168.1.0", key_p->data); | 683 | inet_pton(AF_INET, "192.168.1.0", key_p->data); |