BPF_MAP_TYPE_DEVMAP 和 BPF_MAP_TYPE_DEVMAP_HASH¶
注意
BPF_MAP_TYPE_DEVMAP
在内核版本 4.14 中引入BPF_MAP_TYPE_DEVMAP_HASH
在内核版本 5.4 中引入
BPF_MAP_TYPE_DEVMAP
和 BPF_MAP_TYPE_DEVMAP_HASH
是 BPF 映射,主要用作 XDP BPF 辅助函数调用 bpf_redirect_map()
的后端映射。BPF_MAP_TYPE_DEVMAP
由一个数组支持,该数组使用键作为索引来查找对网络设备的引用。而 BPF_MAP_TYPE_DEVMAP_HASH
则由一个哈希表支持,该哈希表使用键来查找对网络设备的引用。用户提供 <key
/ ifindex
> 或 <key
/ struct bpf_devmap_val
> 对来更新具有新网络设备的映射。
注意
哈希映射的键不必是
ifindex
。虽然
BPF_MAP_TYPE_DEVMAP_HASH
允许密集打包网络设备,但代价是在执行查找时对键进行哈希运算。
两种类型的 devmap 共享设置和数据包入队/发送代码;只有查找和插入不同。
用法¶
内核 BPF¶
bpf_redirect_map()¶
long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags)
将数据包重定向到 map
中索引为 key
的端点。对于 BPF_MAP_TYPE_DEVMAP
和 BPF_MAP_TYPE_DEVMAP_HASH
,此映射包含对网络设备的引用(用于通过其他端口转发数据包)。
如果映射查找失败,flags 的最低两位用作返回代码。这样,返回的值可以是由调用者选择的 XDP 程序返回代码之一,直到 XDP_TX
。可以将 flags
的较高位设置为如下定义的 BPF_F_BROADCAST
或 BPF_F_EXCLUDE_INGRESS
。
使用 BPF_F_BROADCAST
,数据包将广播到映射中的所有接口,使用 BPF_F_EXCLUDE_INGRESS
,入口接口将从广播中排除。
注意
如果设置了 BPF_F_BROADCAST,则忽略键。
广播功能还可用于实现多播转发:只需创建多个 DEVMAP,每个 DEVMAP 对应一个多播组。
如果成功,此辅助函数将返回 XDP_REDIRECT
;如果映射查找失败,则返回 flags
参数的最低两位的值。
有关重定向的更多信息,请参见 重定向
bpf_map_lookup_elem()¶
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
可以使用 bpf_map_lookup_elem()
辅助函数来检索网络设备条目。
用户空间¶
注意
DEVMAP 条目只能从用户空间更新/删除,而不能从 eBPF 程序更新/删除。尝试从内核 eBPF 程序调用这些函数将导致程序加载失败并出现验证程序警告。
bpf_map_update_elem()¶
int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags);
可以使用 bpf_map_update_elem()
辅助函数来添加或更新网络设备条目。此辅助函数以原子方式替换现有元素。value
参数可以是 struct bpf_devmap_val
,为了向后兼容,也可以是简单的 int ifindex
。
struct bpf_devmap_val { __u32 ifindex; /* device index */ union { int fd; /* prog fd on map write */ __u32 id; /* prog id on map read */ } bpf_prog; };
flags
参数可以是以下之一BPF_ANY
:创建新元素或更新现有元素。BPF_NOEXIST
:仅当新元素不存在时才创建。BPF_EXIST
:更新现有元素。
DEVMAP 可以通过将 bpf_prog.fd
添加到 struct bpf_devmap_val
来将程序与设备条目关联。程序在 XDP_REDIRECT
之后运行,并且可以访问 Rx 设备和 Tx 设备。与 fd
关联的程序必须具有 XDP 类型,且预期附加类型为 xdp_devmap
。当程序与设备索引关联时,该程序会在 XDP_REDIRECT
上运行,并在将缓冲区添加到每个 CPU 队列之前运行。有关如何附加/使用 xdp_devmap 程序示例,请参见内核自检
tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c
tools/testing/selftests/bpf/progs/test_xdp_with_devmap_helpers.c
bpf_map_lookup_elem()¶
-
int bpf_map_lookup_elem(int fd, const void *key, void *value);¶
可以使用 bpf_map_lookup_elem()
辅助函数来检索网络设备条目。
bpf_map_delete_elem()¶
-
int bpf_map_delete_elem(int fd, const void *key);¶
可以使用 bpf_map_delete_elem()
辅助函数来删除网络设备条目。如果成功,此辅助函数将返回 0;如果失败,则返回负错误。
示例¶
内核 BPF¶
以下代码段显示了如何声明名为 tx_port 的 BPF_MAP_TYPE_DEVMAP
。
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, 256);
} tx_port SEC(".maps");
以下代码段显示了如何声明名为 forward_map 的 BPF_MAP_TYPE_DEVMAP_HASH
。
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP_HASH);
__type(key, __u32);
__type(value, struct bpf_devmap_val);
__uint(max_entries, 32);
} forward_map SEC(".maps");
注意
以上 DEVMAP 中的值类型为 struct bpf_devmap_val
以下代码段显示了一个简单的 xdp_redirect_map 程序。此程序将与用户空间程序一起工作,该程序根据入口 ifindex 填充 devmap forward_map
。BPF 程序(如下)使用入口 ifindex
作为 key
重定向数据包。
SEC("xdp")
int xdp_redirect_map_func(struct xdp_md *ctx)
{
int index = ctx->ingress_ifindex;
return bpf_redirect_map(&forward_map, index, 0);
}
以下代码段显示了一个 BPF 程序,该程序将数据包广播到 tx_port
devmap 中的所有接口。
SEC("xdp")
int xdp_redirect_map_func(struct xdp_md *ctx)
{
return bpf_redirect_map(&tx_port, 0, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
}
用户空间¶
以下代码段显示了如何更新名为 tx_port
的 devmap。
int update_devmap(int ifindex, int redirect_ifindex)
{
int ret;
ret = bpf_map_update_elem(bpf_map__fd(tx_port), &ifindex, &redirect_ifindex, 0);
if (ret < 0) {
fprintf(stderr, "Failed to update devmap_ value: %s\n",
strerror(errno));
}
return ret;
}
以下代码段显示了如何更新名为 forward_map
的 hash_devmap。
int update_devmap(int ifindex, int redirect_ifindex)
{
struct bpf_devmap_val devmap_val = { .ifindex = redirect_ifindex };
int ret;
ret = bpf_map_update_elem(bpf_map__fd(forward_map), &ifindex, &devmap_val, 0);
if (ret < 0) {
fprintf(stderr, "Failed to update devmap_ value: %s\n",
strerror(errno));
}
return ret;
}