XDP RX 元数据

本文档描述了 eXpress Data Path (XDP) 程序如何使用一组辅助函数访问与数据包相关的硬件元数据,以及如何将该元数据传递给其他消费者。

总体设计

XDP 可以访问一组 kfuncs 来操作 XDP 帧中的元数据。每个希望公开额外数据包元数据的设备驱动程序都可以实现这些 kfuncs。kfuncs 的集合在 include/net/xdp.h 中通过 XDP_METADATA_KFUNC_xxx 声明。

目前,支持以下 kfuncs。未来,随着支持的元数据越来越多,这个集合将会扩大

__bpf_kfunc int bpf_xdp_metadata_rx_timestamp(const struct xdp_md *ctx, u64 *timestamp)

读取 XDP 帧 RX 时间戳。

参数

const struct xdp_md *ctx

XDP 上下文指针。

u64 *timestamp

返回值指针。

返回

  • 成功时返回 0,错误时返回 -errno

  • -EOPNOTSUPP:表示设备驱动程序未实现 kfunc

  • -ENODATA:表示此帧没有可用的 RX-时间戳

__bpf_kfunc int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, u32 *hash, enum xdp_rss_hash_type *rss_type)

读取 XDP 帧 RX 哈希值。

参数

const struct xdp_md *ctx

XDP 上下文指针。

u32 *hash

返回值指针。

enum xdp_rss_hash_type *rss_type

RSS 类型的返回值指针。

描述

RSS 哈希类型 (rss_type) 指定了网卡硬件在计算 RSS 哈希值时使用的数据包报头的哪一部分。RSS 类型可以通过 enum xdp_rss_hash_type 解码,可以匹配单个 L3/L4 位 XDP_RSS_L* 或组合传统的 *RSS 哈希类型* XDP_RSS_TYPE_L*

返回

  • 成功时返回 0,错误时返回 -errno

  • -EOPNOTSUPP:表示设备驱动程序未实现 kfunc

  • -ENODATA:表示此帧没有可用的 RX 哈希值

__bpf_kfunc int bpf_xdp_metadata_rx_vlan_tag(const struct xdp_md *ctx, __be16 *vlan_proto, u16 *vlan_tci)

获取 XDP 数据包最外层的 VLAN 标签

参数

const struct xdp_md *ctx

XDP 上下文指针。

__be16 *vlan_proto

VLAN 标签协议标识符 (TPID) 的目标指针。

u16 *vlan_tci

VLAN TCI (VID + DEI + PCP) 的目标指针

描述

如果成功,vlan_proto 包含 标签协议标识符 (TPID),通常是 ETH_P_8021QETH_P_8021AD,但某些网络可以使用自定义 TPID。vlan_proto网络字节序 (BE) 存储,应按如下方式使用: if (vlan_proto == bpf_htons(ETH_P_8021Q)) do_something();

vlan_tci 包含 VLAN 标签的剩余 16 位。驱动程序应以 主机字节序 (通常为 LE) 提供这些位,因此 bpf 程序不应执行字节转换。根据 802.1Q 标准,VLAN TCI(标签控制信息) 是一个位字段,其中包含:VLAN 标识符 (VID),可以使用 vlan_tci & 0xfff 读取,丢弃资格指示符 (DEI) - 1 位,优先级代码点 (PCP) - 3 位。有关 DEI 和 PCP 的详细含义,请参阅其他资料。

返回

  • 成功时返回 0,错误时返回 -errno

  • -EOPNOTSUPP:设备驱动程序未实现 kfunc

  • -ENODATA:VLAN 标签未被剥离或不可用

XDP 程序可以使用这些 kfuncs 将元数据读取到堆栈变量中以供自身使用。或者,为了将元数据传递给其他使用者,XDP 程序可以将其存储在数据包前面携带的元数据区域中。并非所有数据包都必然具有可用的请求元数据,在这种情况下,驱动程序将返回 -ENODATA

并非所有 kfuncs 都必须由设备驱动程序实现;如果未实现,将使用返回 -EOPNOTSUPP 的默认 kfuncs 来指示设备驱动程序未实现此 kfunc。

在 XDP 帧中,元数据布局(通过 xdp_buff 访问)如下:

+----------+-----------------+------+
| headroom | custom metadata | data |
+----------+-----------------+------+
           ^                 ^
           |                 |
 xdp_buff->data_meta   xdp_buff->data

XDP 程序可以将单独的元数据项以其选择的任何格式存储到此 data_meta 区域中。元数据的后续使用者必须通过一些带外合约(例如对于 AF_XDP 用例,请参见下文)就格式达成一致。

AF_XDP

AF_XDP 用例意味着将 XDP 帧重定向到 AF_XDP 套接字 (XSK) 的 BPF 程序与最终使用者之间存在合约。因此,BPF 程序通过 bpf_xdp_adjust_meta 从元数据中手动分配固定数量的字节,并调用 kfuncs 的子集来填充它。用户空间 XSK 使用者计算 xsk_umem__get_data() - METADATA_SIZE 来定位该元数据。请注意,xsk_umem__get_datalibxdp 中定义,而 METADATA_SIZE 是特定于应用程序的常量 (AF_XDP 接收描述符 _不_ 显式携带元数据的大小)。

这是 AF_XDP 使用者的布局(请注意缺少 data_meta 指针)

+----------+-----------------+------+
| headroom | custom metadata | data |
+----------+-----------------+------+
                             ^
                             |
                      rx_desc->address

XDP_PASS

这是 XDP 程序处理的数据包被传递到内核的路径。内核从 xdp_buff 内容创建 skb。目前,每个驱动程序都有自定义内核代码来解析描述符并在进行此 xdp_buff->skb 转换时填充 skb 元数据,并且内核在构建 skb 时不使用 XDP 元数据。但是,TC-BPF 程序可以使用 data_meta 指针访问 XDP 元数据区域。

将来,我们希望支持这样一种情况:XDP 程序可以覆盖用于构建 skb 的某些元数据。

bpf_redirect_map

bpf_redirect_map 可以将帧重定向到不同的设备。某些设备(如虚拟以太网链路)支持在重定向后运行第二个 XDP 程序。但是,最终使用者无法访问原始硬件描述符,也无法访问任何原始元数据。这同样适用于安装到 devmaps 和 cpumaps 中的 XDP 程序。

这意味着对于重定向的数据包,目前仅支持自定义元数据,该元数据必须在重定向之前由初始 XDP 程序准备。如果帧最终传递给内核,则从此类帧创建的 skb 将不会在其 skb 中填充任何硬件元数据。如果这样的数据包稍后被重定向到 XSK 中,那么它也只能访问自定义元数据。

bpf_tail_call

目前不支持将访问元数据 kfuncs 的程序添加到 BPF_MAP_TYPE_PROG_ARRAY

支持的设备

可以通过 netlink 查询特定 netdev 实现了哪个 kfunc。请参阅 Documentation/netlink/specs/netdev.yaml 中设置的 xdp-rx-metadata-features 属性。

示例

有关处理 XDP 元数据的 BPF 程序的示例,请参阅 tools/testing/selftests/bpf/progs/xdp_metadata.ctools/testing/selftests/bpf/prog_tests/xdp_metadata.c