BPF_MAP_TYPE_LPM_TRIE

注意

  • BPF_MAP_TYPE_LPM_TRIE 在内核版本4.11中引入

BPF_MAP_TYPE_LPM_TRIE 提供最长前缀匹配算法,可用于将 IP 地址与存储的前缀集进行匹配。在内部,数据存储在一个不平衡的节点 trie 中,该 trie 使用 prefixlen,data 对作为其键。data 以网络字节顺序(即大端)解释,因此 data[0] 存储最高有效字节。

LPM trie 可以使用 8 到 2048 范围内 8 的倍数的最大前缀长度创建。用于查找和更新操作的键是一个 struct bpf_lpm_trie_key_u8,扩展了 max_prefixlen/8 个字节。

  • 对于 IPv4 地址,数据长度为 4 字节

  • 对于 IPv6 地址,数据长度为 16 字节

LPM trie 中存储的值类型可以是任何用户定义的类型。

注意

创建 BPF_MAP_TYPE_LPM_TRIE 类型的映射时,必须设置 BPF_F_NO_PREALLOC 标志。

用法

内核 BPF

bpf_map_lookup_elem()

void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)

可以使用 bpf_map_lookup_elem() 辅助函数找到给定数据值的最长前缀条目。此辅助函数返回指向与最长匹配的 key 关联的值的指针,如果没有找到条目,则返回 NULL

执行最长前缀查找时,keyprefixlen 应设置为 max_prefixlen。例如,在搜索 IPv4 地址的最长前缀匹配时,prefixlen 应设置为 32

bpf_map_update_elem()

long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)

可以使用 bpf_map_update_elem() 辅助函数添加或更新前缀条目。此辅助函数以原子方式替换现有元素。

bpf_map_update_elem() 成功返回 0,失败则返回负错误。

注意

flags 参数必须是 BPF_ANY、BPF_NOEXIST 或 BPF_EXIST 之一,但该值被忽略,从而提供 BPF_ANY 语义。

bpf_map_delete_elem()

long bpf_map_delete_elem(struct bpf_map *map, const void *key)

可以使用 bpf_map_delete_elem() 辅助函数删除前缀条目。此辅助函数成功返回 0,失败则返回负错误。

用户空间

从用户空间访问使用 libbpf API,其名称与上述相同,映射由 fd 标识。

bpf_map_get_next_key()

int bpf_map_get_next_key (int fd, const void *cur_key, void *next_key)

用户空间程序可以使用 libbpf 的 bpf_map_get_next_key() 函数迭代 LPM trie 中的条目。可以通过调用 bpf_map_get_next_key() 并将 cur_key 设置为 NULL 来获取第一个键。后续调用将获取当前键之后的下一个键。 bpf_map_get_next_key() 成功返回 0,如果 cur_key 是 trie 中的最后一个键,则返回 -ENOENT,失败则返回负错误。

bpf_map_get_next_key() 将首先从最左边的叶子开始迭代 LPM trie 元素。这意味着迭代将先返回更具体的键,然后再返回不太具体的键。

示例

有关从用户空间使用 LPM trie 的示例,请参阅 tools/testing/selftests/bpf/test_lpm_map.c。以下代码片段演示了 API 的用法。

内核 BPF

以下 BPF 代码片段显示了如何为 IPv4 地址前缀声明新的 LPM trie

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct ipv4_lpm_key {
        __u32 prefixlen;
        __u32 data;
};

struct {
        __uint(type, BPF_MAP_TYPE_LPM_TRIE);
        __type(key, struct ipv4_lpm_key);
        __type(value, __u32);
        __uint(map_flags, BPF_F_NO_PREALLOC);
        __uint(max_entries, 255);
} ipv4_lpm_map SEC(".maps");

以下 BPF 代码片段显示了如何通过 IPv4 地址查找

void *lookup(__u32 ipaddr)
{
        struct ipv4_lpm_key key = {
                .prefixlen = 32,
                .data = ipaddr
        };

        return bpf_map_lookup_elem(&ipv4_lpm_map, &key);
}

用户空间

以下片段显示了如何将 IPv4 前缀条目插入到 LPM trie 中

int add_prefix_entry(int lpm_fd, __u32 addr, __u32 prefixlen, struct value *value)
{
        struct ipv4_lpm_key ipv4_key = {
                .prefixlen = prefixlen,
                .data = addr
        };
        return bpf_map_update_elem(lpm_fd, &ipv4_key, value, BPF_ANY);
}

以下片段显示了一个用户空间程序遍历 LPM trie 的条目

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

void iterate_lpm_trie(int map_fd)
{
        struct ipv4_lpm_key *cur_key = NULL;
        struct ipv4_lpm_key next_key;
        struct value value;
        int err;

        for (;;) {
                err = bpf_map_get_next_key(map_fd, cur_key, &next_key);
                if (err)
                        break;

                bpf_map_lookup_elem(map_fd, &next_key, &value);

                /* Use key and value here */

                cur_key = &next_key;
        }
}