XDP-0-xdp-tools

  • xdp 的军火库 -> xdp-tools

  • 资料来源:

    • <>
  • 更新

    1
    2024.12.07 初始

导语

上战场前先看看军火库

xdp-tools 是一族 xdp 工具集合. 包含 libxdp 和基于 libxdp 的一组工具.

  • libxdp 基于 libbpf 的一系列 xdp 操作封装, 经久耐用.
  • xdp-bench
  • xdp-dump: 与 tcpdump 类似, xdp 的抓包工具;
  • xdp-filter: 基于 xdp 的简单数据过滤器
  • xdp-forward: 指定的网络接口之间加速数据包转发
  • xdp-loader: 加载/卸载/查看 xdp 状态等
  • xdp-monitor: 监控统计信息等
  • xdp-trafficgen: 生成流量工具

安装

1
2
3
# dnf search xdp
# 可以看到由 libxdp 和 xdp-tools
dnf install libxdp xdp-tools.x86_64

如果宿主机太老, 建议自行编译

1
2
3
4
5
6
7
8
9
10
11
git clone https://github.com/xdp-project/xdp-tools.git
# 如果本机没有安装 libbpf 或 libbpf 版本太低
# 下载 libbpf 源码,同时编译 libbpf
git submodule init && git submodule update

# 这一步可能遇到一些包缺失, 逐个解决即可
./configure
# 编译
make
# 安装
make install

Libxdp

这个后面详细说, 这里暂且不表.

xdp-bench

基准性能测试工具, 这个工具涉及到了一些 bpf 底层内容, 现在暂时还不是太理解.

#待续

xdp-dump

类似 tcpdump, 捕获 接口 ingress 方向, 即使 xdp 程序返回是 drop 也能捕获;

  • 但是无法捕获 tx 到这个接口的; 例如 xks 的发包;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ xdpdump -h

Usage: xdpdump [options]

XDPDump tool to dump network traffic

Options:
--rx-capture <mode> Capture point for the rx direction (valid values: entry,exit)
-D, --list-interfaces Print the list of available interfaces
--load-xdp-mode <mode> Mode used for --load-xdp-mode, default native (valid values: native,skb,hw,unspecified)
--load-xdp-program Load XDP trace program if no XDP program is loaded
-i, --interface <ifname> Name of interface to capture on
--perf-wakeup <events> Wake up xdpdump every <events> packets
-p, --program-names <prog> Specific program to attach to
-P, --promiscuous-mode Open interface in promiscuous mode
-s, --snapshot-length <snaplen> Minimum bytes of packet to capture
--use-pcap Use legacy pcap format for XDP traces
-w, --write <file> Write raw packets to pcap file
-x, --hex Print the full packet in hex
-v, --verbose Enable verbose logging (-vv: more verbose)
--version Display version information
-h, --help Show this help

使用方式也是类似 tcpdump, 示例中给出了一些高频操作的 code;

1
2
3
4
5
6
# 列出所有接口已加载的 xdp 程序
# 如果没有 xdp 程序生效, +(--load-xdp-program) xdpdump 也能生效, 但是这样情况下直接 tcpdump 不好吗...
xdpdump -D

# 捕获 ens19 保存到 pcap 文件
sudo xdpdump -i ens19 -w ens19.pcap

指定捕获 pkt 的位置: 进入 xdp 程序前 (entry, 默认), 所有 xdp 程序后 (exit);

1
2
3
# 指定 ens19 挂载点 所有 xdp 程序后 (exit)
# 当 ens19 上已有 xdp 如果给出了 drop, exit 就捕获不到了
sudo xdpdump -i ens19 --rx-capture=exit,exit -p xdp_filter

指定 xdp 程序 一个 or 一组 (逗号隔开)

  • 程序入口函数名 或 fd
  • xdpdump -D 可查
1
2
# 
sudo xdpdump -i ens19 --rx-capture=entry,exit -p xdp_proc1,xdp_proc2 -x

更详细的参考 官方的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# xdpdump -D
Interface Prio Program name Mode ID Tag Chain actions
--------------------------------------------------------------------------------------
lo <No XDP program loaded!>
eth0 xdp_dispatcher skb 10558 d51e469e988d81da
=> 5 xdp_test_prog_w 10576 b5a46c6e9935298c XDP_PASS
=> 10 xdp_pass 10582 3b185187f1855c4c XDP_PASS
=> 10 xdp_pass 10587 3b185187f1855c4c XDP_PASS

# xdpdump -i eth0 --rx-capture=entry,exit -p xdp_dispatcher,xdp_pass@10587
# xdpdump -i eth0 --rx-capture=entry,exit -p 10558,10587
listening on eth0, ingress XDP program ID 10558 func xdp_dispatcher, ID 10587 func xdp_pass, capture mode entry/exit, capture size 262144 bytes
1607694215.501287259: xdp_dispatcher()@entry: packet size 102 bytes on if_index 2, rx queue 0, id 1
1607694215.501371504: xdp_pass()@entry: packet size 102 bytes on if_index 2, rx queue 0, id 1
1607694215.501383099: xdp_pass()@exit[PASS]: packet size 102 bytes on if_index 2, rx queue 0, id 1
1607694215.501394709: xdp_dispatcher()@exit[PASS]: packet size 102 bytes on if_index 2, rx queue 0, id 1
^C
4 packets captured
0 packets dropped by perf ring

xdp-filter

l2-l4 基于 xdp 的过滤器, 功能不足,但是性能 ++;

支持 l2-l4

  • mac 地址
  • ipv4 ipv6 地址
  • tcp / udp 端口

规则动态添加删除, 无需重启 xdp 程序; 详细配置参考 readme 描述非常清晰;

1
2
3
4
5
6
7
8
9
10
11
12
13
Usage: xdp-filter COMMAND [options]

COMMAND can be one of:
load - load xdp-filter on an interface
unload - unload xdp-filter from an interface
port - add a port to the filter list
ip - add an IP address to the filter list
ether - add an Ethernet MAC address to the filter list
status - show current xdp-filter status
poll - poll statistics output
help - show this help message

Use 'xdp-filter COMMAND --help' to see options for each command
1
2
3
4
5
6
7
8
9
10
11
# 加载到 eth0 ipv4 过滤
sudo xdp-filter load eth0 -f ipv4
# 源是 192.168.1.100
sudo xdp-filter ip 192.168.1.100 -m src
# 查看过滤状态
sudo xdp-filter status

# 官方例子: 只允许 fc00:dead:cafe::1 的 ipv6 包
# 小心把自己关在外面...
xdp-filter load eth0 -f ipv6 -p deny
xdp-filter ip fc00:dead:cafe::1 -m src

xdp-forward

在网卡直接转发 pkt, 在开发中使用很少, 暂时略过.

xdp-loader

这个敲黑板: 重点 与 xdp 程序加载/卸载有关的一切

1
2
3
4
5
6
7
8
9
10
11
Usage: xdp-loader COMMAND [options]

COMMAND can be one of:
load - load an XDP program on an interface
unload - unload an XDP program from an interface
status - show current XDP program status
clean - clean up detached program links in XDP bpffs directory
features - show XDP features supported by the NIC
help - show this help message

Use 'xdp-loader COMMAND --help' to see options for each command

可以看到细分下来 xdp-loader 包含了 load unload status clean features 几大部分, 下面分别展开.

Load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Usage: xdp-loader load [options] <ifname> <filenames>

Load an XDP program on an interface

Required parameters:
<ifname> Load on device <ifname>
<filenames> Load programs from <filenames>

Options:
-m, --mode <mode> Load XDP program in <mode>; default native (valid values: native,skb,hw,unspecified)
-p, --pin-path Path to pin maps under (must be in bpffs).
-s, --section <section> ELF section name of program to load (default: first in file).
-n, --prog-name <prog_name> BPF program name of program to load (default: first in file).
-P, --prio Set run priority of program
-A, --actions <actions> Chain call actions (default: XDP_PASS). e.g. XDP_PASS,XDP_DROP (valid values: XDP_ABORTED,XDP_DROP,XDP_PASS,XDP_TX,XDP_REDIRECT)
-v, --verbose Enable verbose logging (-vv: more verbose)
--version Display version information
-h, --help Show this help

主要选项:

  • m, --mode <mode>: 指定 XDP 程序加载模式 (native/skb/hw/unspecified)
  • p, --pin-path <path>: 指定 map 钉住的根路径
  • s, --section <section>: 指定要加载的 ELF section
  • n, --prog-name <prog_name>: 指定要加载的程序名称
  • P, --prio <priority>: 指定程序优先级
  • A, --actions <actions>: 指定链式调用动作
    • 这个特殊一点, 当前程序返回什么 xdp action 时候, 才继续执行下一个 xdp 程序.
    • 取值可以是 XDP_ABORTED,XDP_DROP,XDP_PASS,XDP_TX,XDP_REDIRECT, 默认情况下 当前 xdp 程序返回 XDP_PASS 时才继续执行后面的 xdp 程序
1
2
3
4
5
6
7
8
# eth0 native 模式加载 xdp_drop.o
sudo xdp-loader load -m native eth0 xdp_drop.o

# 加载多个程序,并指定优先级和钉住路径
sudo xdp-loader load -P 10 -p /sys/fs/bpf/myapp eth0 prog1.o prog2.o

# 加载 multi_prog.o 的 xdp_prog section
sudo xdp-loader load -s xdp_prog eth0 multi_prog.o

Unload

1
2
3
4
5
6
7
8
9
10
11
Usage: xdp-loader unload [options] <ifname>

Unload an XDP program from an interface

Options:
<ifname> Unload from device <ifname>
-i, --id <id> Unload program with id <id>
-a, --all Unload all programs from interface
-v, --verbose Enable verbose logging (-vv: more verbose)
--version Display version information
-h, --help Show this help

参数比较简单了

1
2
3
4
5
# 卸载 eth1 上所有 xdp 程序
sudo xdp-loader unload -a eth1

# 卸载 id 为 110 的 xdp 程序
sudo xdp-loader unload -i 110 eth1

Status

最常用命令…确认 xdp 程序 load/unload 是否正常

1
2
3
4
5
6
7
8
9
Usage: xdp-loader status [options] [ifname]

Show XDP program status

Options:
[ifname] Show status for device [ifname] (default all interfaces)
-v, --verbose Enable verbose logging (-vv: more verbose)
--version Display version information
-h, --help Show this help
1
2
3
4
5
6
7
8
9
10
11
12
13
sudo xdp-loader status
sudo xdp-loader status -vv

# 官方示例
# xdp-loader load eth0 xdp_drop.o
# xdp-loader status
CURRENT XDP PROGRAM STATUS:

Interface Prio Program name Mode ID Tag Chain actions
-------------------------------------------------------------------------------------
lo <no XDP program>
eth0 xdp_dispatcher native 50 d51e469e988d81da
=> 50 xdp_drop 55 57cd311f2e27366b XDP_PASS

Features

用于确认 interface 对 xdp 的支持程度.

  • 这里有个坑: rocky8 系列的 4.18 内核反向移植了 xdp, 能够正常支持 xdp 程序, xdp-loader features 无法正常运行.
1
2
3
4
5
6
7
8
9
10
11
12
13
# 输出更多信息
sudo xdp-loader features eth1
sudo xdp-loader features -v eth1
sudo xdp-loader features -vv eth1

❯ sudo xdp-loader features eth1
NETDEV_XDP_ACT_BASIC: yes
NETDEV_XDP_ACT_REDIRECT: yes
NETDEV_XDP_ACT_NDO_XMIT: yes
NETDEV_XDP_ACT_XSK_ZEROCOPY: no
NETDEV_XDP_ACT_HW_OFFLOAD: no
NETDEV_XDP_ACT_RX_SG: no
NETDEV_XDP_ACT_NDO_XMIT_SG: no

详细解释下每个 NETDEV_XDP_ACT_X

  • NETDEV_XDP_ACT_BASIC: 能运行 xdp 程序, 返回 XDP_ACT (XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX)
  • NETDEV_XDP_ACT_REDIRECT: 允许将 pkt 重定向到其他接口/CPU
  • NETDEV_XDP_ACT_NDO_XMIT: 可以接受 xdp 重定向的 pkt;
    • 实现了 ndo_xdp_xmit callback
  • NETDEV_XDP_ACT_XSK_ZEROCOPY: xks 收 pkt 时, 从网卡缓存区到用户应用的 xsk 0 copy;
  • NETDEV_XDP_ACT_HW_OFFLOAD: 支持硬件卸载模式, 这个非常非常少, 不用太关心;
  • NETDEV_XDP_ACT_RX_SG: 支持接收非线性 frame
    • 一般来说, xdp 处理的 frame 大小是固定的 (默认 4096), 但是 xdp 可以设置 frame 大小非固定, 配合超大 MTU 能够显著减少处理成本.
    • 但是相对的编程就会麻烦很多, 要额外处理不固定的 frame 大小.
  • NETDEV_XDP_ACT_NDO_XMIT_SG: 支持发送非线性 frame
    • frame 会在下一节解释;

通常前三个为 yes, 说明这个 inerface 可以正常运行 xdp 了;

1
2
3
NETDEV_XDP_ACT_BASIC:           yes
NETDEV_XDP_ACT_REDIRECT: yes
NETDEV_XDP_ACT_NDO_XMIT: yes

NETDEV_XDP_ACT_XSK_ZEROCOPY: yes 更好, 忘了在哪里看到的资料了, 0copy 与非 0copy 相差 30% 处理性能;

后面这 3 个不常用到.

1
2
3
NETDEV_XDP_ACT_HW_OFFLOAD:      no
NETDEV_XDP_ACT_RX_SG: no
NETDEV_XDP_ACT_NDO_XMIT_SG: no

wsl2 下各个以太网卡是前 3 个为 yes; tun 网卡也是前 3 个为 yes;

Clean

clean 是清理分离的 xdp 程序链接,例如 接口以及 down 了, 但是 xdp 程序的 map 还 pin 在文件系统;

1
2
3
4
5
6
7
8
# 清理所有接口的分离程序
sudo xdp-loader clean

# 只清理 eth0 的分离程序
sudo xdp-loader clean eth0

# 使用详细模式清理
sudo xdp-loader clean -v

xdp-monitor

实时监控 xdp 程序运行状态, 例如输出 redict 的 pps, drop 的 pps, drop 的原因等等;

  • 监控的是全部网卡不能指定 interface

使用非常简单, 没有太多参数.

  • i 指定刷新间隔
  • s 记录 redirection 成功的包
  • e 输出模式设置为拓展模式, 和捕获信息没有关系, 只是输出信息的两种模式, monitor 运行时候可以 Ctrl + \ 切换两种输出模式.
  • v or vv 打印来自 libxdp 和 libbbpf 的 log 输出
1
2
3
4
5
6
7
8
9
10
11
Usage: xdp-monitor [options]

XDP monitor tool, based on tracepoints

Options:
-i, --interval <seconds> Polling interval (default 2)
-s, --stats Enable statistics for transmitted packets (not just errors)
-e, --extended Start running in extended output mode (C^\ to toggle)
-v, --verbose Enable verbose logging (-vv: more verbose)
--version Display version information
-h, --help Show this help

monitor 的输出信息比较繁杂

1
2
3
4
5
6
# 跟踪 redirect 的 pkt
# 输出 1s 刷新一次
❯ sudo xdp-monitor -s -i 1 -v
Current rlimit 1567629312 already >= minimum 1048576
Kernel supports 5-arg xdp_cpumap_kthread tracepoint
Summary 0 redir/s 0 err,drop/s 0 xmit/s
  • rx/s Number of packets received per second
  • redir/s Number of packets successfully redirected per second
  • err,drop/s Aggregated count of errors per second (including dropped packets)
  • xmit/s Number of packets transmitted on the output device per second

现在摁下 ctrl + \, 切换输出信息到 extended 模式

1
2
3
4
5
6
Summary                         0 redir/s               0 err,drop/s            0 xmit/s
kthread 0 pkt/s 0 drop/s 0 sched
redirect 0 redir/s
redirect_err 0 error/s
xdp_exception 0 hit/s
devmap_xmit 0 xmit/s 0 drop/s 0 drv_err/s 0.00 bulk-avg

但是实际上 extended 模式下太细了, 还是搬上原文的翻译, 可以非常好的排除 xdp 程序问题;

  • 一般用不到这么详细的信息, 但是一旦一旦涉及到 xdp 程序丢包的排除, 全过程 tcpdump 也找不到具体是哪个程序问题时候…
  • xdp-monitor 我的神.
功能字段描述关键指标
接收receive显示接收的数据包数量和遇到的错误pkt/s, drop/s, error/s
redirect显示成功重定向的数据包数量redir/s
重定向redirect_err显示重定向失败的数据包数量及错误类型error/s (EINVAL, ENETDOWN, EMSGSIZE, EOPNOTSUPP, ENOSPC)
enqueue to cpu N显示入队到特定 CPU 的数据包数量pkt/s, drop/s, bulk-avg
处理kthread显示 CPUMAP 内核线程处理的数据包数量pkt/s, drop/s, sched, pass/s, drop/s, redir/s
异常xdp_exception显示 XDP 异常事件hit/s
传输devmap_xmit显示设备映射传输事件 (仅限非 SKB 模式)xmit/s, drop/s, drv_err/s, bulk-avg
  • receive(接收):
    • 显示接收的数据包数量和遇到的错误
    • 当发生错误或数据包丢弃时,每个 CPU 的错误和丢包统计将在简洁模式下内联展开
      • pkt/s: 每秒接收的数据包数
      • drop/s: 每秒丢弃的数据包数
      • error/s: 每秒遇到的错误数
    • redirect(重定向): 显示成功重定向的数据包数量
      • 重定向过程中遇到的错误会在 redirect_err 字段下展开
      • 注意: 使用 -s 选项启用此功能会带来每个数据包的开销
      • redir/s: 每秒成功重定向的数据包数
  • redirect_err(重定向错误):
    • 显示重定向失败的数据包数量
    • 错误代码 (errno) 会在此字段下按每个 CPU 的计数展开
    • 已识别的错误包括:
      • EINVAL: 无效的重定向
      • ENETDOWN: 重定向目标设备已关闭
      • EMSGSIZE: 数据包长度对设备来说太大
      • EOPNOTSUPP: 不支持的操作
      • ENOSPC: cpumap kthread 的 ptr_ring 中没有空间
    • error/s: 每秒重定向失败的数据包数
  • enqueue to cpu N(入队到 CPU N):
    • 显示入队到 CPU N 的批量队列的数据包数量
    • 展开为 cpu:FROM->N 以显示每个 CPU 入队到 CPU N 的统计信息
    • 接收的数据包可以与重定向程序将数据包入队的 CPU 相关联
      • pkt/s: 每秒从其他 CPU 入队到 CPU N 的数据包数
      • drop/s: 尝试入队到 CPU N 时丢弃的数据包数
      • bulk-avg: 每个事件处理的平均数据包数
  • kthread(内核线程):
    • 显示每个 CPU 的 CPUMAP 内核线程中处理的数据包数量
    • 展开从 ptr_ring 消耗的数据包数量,以及调用 CPUMAP BPF 程序后的 xdp_stats
    • xdp_stats 按总数和每个 CPU 展开,以关联到每个 CPU 的固定 CPUMAP 内核线程
      • pkt/s: 每秒从 ptr_ring 消耗的数据包数
      • drop/s: 内核线程中每秒丢弃的数据包数
      • sched: 内核线程调用 schedule() 的次数
    • xdp_stats(也展开到每个 CPU 的计数)
      • pass/s: CPUMAP 程序执行的 XDP_PASS 计数
      • drop/s: CPUMAP 程序执行的 XDP_DROP 计数
      • redir/s: CPUMAP 程序执行的 XDP_REDIRECT 计数
  • xdp_exception(XDP 异常):
    • 显示 xdp_exception 跟踪点事件
    • 可能由内部驱动程序错误、无法识别的 XDP 操作或用户明确触发 XDP_ABORTED 导致
    • 每个操作都在此字段下展开其计数
      • hit/s: 每秒触发跟踪点的次数
  • devmap_xmit(设备映射传输):
    • 显示 devmap_xmit 跟踪点事件
    • 此跟踪点用于输出设备上的成功传输,但这些统计信息在通用 XDP 模式下不可用
    • 使用 SKB 模式时,这些统计将从输出中省略
      • xmit/s: 每秒成功传输的数据包数
      • drop/s: 每秒传输失败的数据包数
      • drv_err/s: 每秒内部驱动程序错误数
      • bulk-avg: 每个事件处理的平均数据包数

xdp-trafficgen

生成 tcp udp 流量, 生成流量比较单一, 但是性能是相当高…

  • UDP : 固定或动态目标端口
  • TCP : 单一 TCP 流, 没有拥塞控制, 一旦有丢包就会有大量的重传.
  • 多线程支持
  • 目标网卡必须支持 XDP 重定向 (实现 ndo_xdp_xmit 驱动操作).

对 IP 版本限制:

  • udp 写明了只支持 IPv6
  • tcp 不明, 没有写不支持 ipv4 , tcp 的一个可选参数 “<hostname>” 只支持 ipv6,但这个参数本身是可选的

尾巴

以上大致是整个项目进行中用到的所有工具, 要是没有 xdp-monitor 还不知道能不能完成工作…. 感谢开源社区的工作…

下一篇是 xdp 和 af_xdp 的理论说明,非常粗略.