XDP之BPF_MAP_TYPE_LPM_TRIE的使用

介绍

BPF_MAP_TYPE_LPM_TRIE是BPF众多MAP的一种,主要功能为最长前缀匹配(Longest Prefix Matching),如IP匹配等。

Linux Kernel文档见 BPF_MAP_TYPE_LPM_TRIE — The Linux Kernel documentation

源码见lpm_trie.c - kernel/bpf/lpm_trie.c - Linux source code (v5.13) - Bootlin

原理

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

在创建LPM_TRIE时,可以以8的倍数作为最长前缀长度,实际范围为8-2048。用于查询和更新MAP的键是一个结构struct bpf_lpm_trie_key,包含u32的值prefixlen和max_prefixlen/8字节的数据。

Note1:

        BPF_MAP_TYPE_LPM_TRIE是在内核版本4.11中引入的。

Note2:

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

在实现中,用一个trie存储前缀和值,内核中的结构如下

struct lpm_trie_node {
	struct rcu_head rcu;
	struct lpm_trie_node __rcu	*child[2];
	u32				prefixlen;
	u32				flags;
	u8				data[];
};

struct lpm_trie {
	struct bpf_map			map;
	struct lpm_trie_node __rcu	*root;
	size_t				n_entries;
	size_t				max_prefixlen;
	size_t				data_size;
	spinlock_t			lock;
};

 举例说明trie的构建过程,以ipv4地址前缀匹配为例,假设初始情况trie为空,此时插入一个前缀192.168.0.0/16,对应值为1。如下图,建立一个新的节点(1),存储该前缀和值

 接下来插入另一个前缀192.168.0.0/24,值为2,由于已经存在一个data相同但长度更短的前缀,新前缀将作为节点(1)的子节点存储。新前缀(192.168.0.0/24)在旧前缀(192.168.0.0/16)之后的下一位为0,因此将成为孩子0。

 接下来插入192.168.128.0/24,值为3,与上面过程类似。

 再插入一个前缀192.168.1.0/24,值为4。该前缀明显不能放入节点(2)或节点(3)的子节点,那么我们引入一个辅助节点(4)。

 不难看出节点(4)存储了节点(2)和(5)的最长公共前缀,按照下一位(第24位)的不同存储两个子节点(2)和(5),且这个节点不存储值,搜索也不会返回该类节点,而是返回搜索路径上经过的有效最长前缀。

用法

Kernel端

查询:使用helper 

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

即可,需要特别注意的是,其中key同样使用struct bpf_lpm_trie_key结构,且prefixlen设置为最长前缀长度,如在IPv4匹配中将设置为32。

更新:使用helper

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

注意flags参数必须设置为BPF_ANY, BPF_NOEXIST 或 BPF_EXIST其中之一,但由于BPF_ANY的语义,值会被忽略。

删除:使用helper

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

User端

可以使用helper

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

遍历所有key,第一个键可以通过调用bpf_map_get_next_key()并将cur_key设置为NULL来获取。随后的调用将获取当前键之后的下一个键。bpf_map_get_next_key()成功时返回0,如果cur_key是树中的最后一个键则返回-ENOENT,如果失败则返回负值表示错误。

bpf_map_get_next_key()将首先从最左边的叶开始遍历LPM trie。这意味着迭代将先返回更具体的前缀,再返回不那么具体的前缀。

User端也可以调用bpf_map_update_elem()向map中插入前缀,此时prefixlen即为想要插入的前缀长度。

示例见BPF_MAP_TYPE_LPM_TRIE — The Linux Kernel documentation

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值