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_DEVMAPBPF_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_DEVMAPBPF_MAP_TYPE_DEVMAP_HASH,此映射包含对网络设备的引用(用于通过其他端口转发数据包)。

如果映射查找失败,flags 的最低两位用作返回代码。这样,返回的值可以是由调用者选择的 XDP 程序返回代码之一,直到 XDP_TX。可以将 flags 的较高位设置为如下定义的 BPF_F_BROADCASTBPF_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;
}

参考资料