.. _ebpf-xdp:

23.4. eBPF 与 XDP

23.4.1. 简介

eBPF 全称为扩展版 BPF。这是 Berkeley 数据包过滤器在近期 Linux 内核版本中的扩展实现。

它通过 C 语言开发的 eBPF 程序提供更高级功能,并支持内核与用户空间之间共享结构化数据。

在 Suricata 中,eBPF 用于以下三个方面:

  • eBPF 过滤器:可开发任何类 BPF 过滤器。示例提供了仅接受特定 VLAN 数据包的过滤器,并包含旁路实现。

  • eBPF 负载均衡:提供可编程负载均衡。示例实现了简单的 IP 对负载均衡。

  • XDP 程序:Suricata 可加载 XDP 程序。示例提供了旁路程序。

旁路功能可通过 eBPF 和 XDP 实现。XDP 的优势在于能在最早阶段丢弃数据包,因此性能更优。但被旁路的流量不会进入网络栈,故仅适用于镜像/嗅探流量,常规流量无法使用此功能。

旁路实现依赖于 eBPF 的核心概念:映射(map)。映射是用户空间与内核空间/硬件共享的数据结构,支持两者间的交互和信息传递。映射通常实现为可存储任意键值对的数组或哈希表。

23.4.1.1. XDP

XDP 为 Linux 原生提供了另一种优化 Suricata 在高速网络嗅探性能的方案:

XDP(eXpress Data Path)作为 IO Visor 项目的一部分,在 Linux 内核中提供了高性能、可编程的网络数据路径。XDP 在软件栈最底层实现裸金属级数据包处理,兼具高速与可编程性。此外,无需修改内核即可通过集成快路径动态实现新功能。

更多 XDP 信息:

23.4.2. 系统要求

需使用支持 XDP 的内核版本,为获得最佳性能建议网卡驱动支持 XDP。

Suricata XDP 代码已在 4.13.10 内核测试,但需 4.15 或更新版本才能使用 CPU 重定向映射等全部功能。

若使用 Intel 网卡,需使用内核树内驱动的 NIC 驱动,树外驱动不包含 XDP 支持。

建议网卡支持 RSS 对称哈希,否则需使用 XDP CPU 重定向映射功能。

23.4.3. 环境准备

本指南已在 Debian/Ubuntu "LTS" Linux 验证。

23.4.3.1. 禁用 irqbalance

多数场景下 irqbalance 可能导致问题,建议停用

systemctl stop irqbalance
systemctl disable irqbalance

23.4.3.2. 内核版本

需运行 4.13 或更新版本内核。

23.4.3.3. Clang 及依赖

确保系统已安装 clang (>=3.9)

sudo apt install clang

23.4.3.4. libbpf

Suricata 使用 libbpf 与 eBPF/XDP 交互

sudo apt install libbpf-dev

若无法获取 libbpf 包,可从仓库克隆

git clone https://github.com/libbpf/libbpf.git

编译安装库

cd libbpf/src/
make && sudo make install

sudo make install_headers
sudo ldconfig

部分系统可能无法找到安装在 /usr/lib64 下的 libbpf 库,此时需调整 ldconfig 配置。

23.4.4. 编译安装 Suricata

获取 Suricata 源码

git clone https://github.com/OISF/suricata.git
cd suricata && ./scripts/bundle.sh

./autogen.sh

配置时添加 eBPF 标志并指定 Clang 编译器(用于构建包括 eBPF 程序在内的所有 C 源码):

CC=clang ./configure --prefix=/usr/ --sysconfdir=/etc/ --localstatedir=/var/ \
--enable-ebpf --enable-ebpf-build

make clean && make
sudo make install-full
sudo ldconfig
sudo mkdir /usr/libexec/suricata/ebpf/

构建 eBPF 文件需使用 clang 编译器,因其独有的 eBPF 后端仅存在于 llvm/clang 工具链。若不想用 Clang 编译 Suricata 本体,可通过 --with-clang 单独指定

./configure --prefix=/usr/ --sysconfdir=/etc/ --localstatedir=/var/ \
--enable-ebpf --enable-ebpf-build --with-clang=/usr/bin/clang

23.4.5. 配置旁路

若计划使用 eBPF 或 XDP 实现内核/硬件级旁路,需启用以下功能:

首先在 suricata.yamlstream 部分启用 bypass

stream:
  bypass: true

这将使流在达到深度阈值时立即被旁路。

如需旁路加密流量,可在应用层 TLS 部分设置 encryption-handlingbypass

app-layer:
  protocols:
    tls:
      enabled: yes
      detection-ports:
        dp: 443

      encryption-handling: bypass

另一种方案是使用含 bypass 关键字的签名规则实现选择性旁路。Suricata 流量识别定义的 flowbits 可用于其他签名,例如

alert any any -> any any (msg:"bypass video"; flowbits:isset,traffic/label/video; noalert; bypass; sid:1000000; rev:1;)
alert any any -> any any (msg:"bypass Skype"; flowbits:isset,traffic/id/skype; noalert; bypass; sid:1000001; rev:1;)

23.4.6. 配置 eBPF 过滤器

修改 ebpf/vlan_filter.c 中的 VLAN ID 列表以适配您的网络。ebpf/filter.c 还提供了基于 IPv4 地址集的过滤示例。详见 固定映射用法

Suricata 可加载任何暴露 filter 段的 eBPF 代码作为过滤器。

修改并 make 编译后,复制生成的 eBPF 过滤器

cp ebpf/vlan_filter.bpf /usr/libexec/suricata/ebpf/

suricata.yaml 的 af-packet 部分设置 ebpf-filter-file 变量

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_flow # 选择合适类型
  defrag: yes
  # 包含'filter'函数的eBPF文件将插入内核用作负载均衡函数
  ebpf-filter-file:  /usr/libexec/suricata/ebpf/vlan_filter.bpf
  ring-size: 200000

正常启动 Suricata

/usr/bin/suricata --pidfile /var/run/suricata.pid  --af-packet=eth3 -vvv

23.4.7. 配置 eBPF 旁路

也可使用 eBPF 旁路。加载 bypass_filter.bpf 文件并在 suricata.yaml 的 af-packet 配置中设置 bypassyes

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_qm # 必须启用对称RSS哈希
  # 包含'filter'函数的eBPF文件将插入内核用作包过滤函数
  ebpf-filter-file:  /usr/libexec/suricata/ebpf/bypass_filter.bpf
  bypass: yes
  ring-size: 200000

旁路兼容代码的约束比常规过滤器更严格。过滤器必须暴露 flow_table_v4flow_table_v6 这两个每 CPU 数组映射,其定义需与 bypass_filter.c 中的一致。Suricata 将通过这两个映射维护旁路流列表。

若未启用 VLAN 跟踪(suricata.yamlvlan.use-for-tracking 设为 false),还需在 bypass_filter.c 中将 VLAN_TRACKING 宏设为 0

23.4.8. 配置 eBPF 负载均衡

eBPF 负载均衡允许通过监听套接字按 eBPF 过滤器实现的逻辑分配流量。标记为 loadbalancer 段的函数返回值将按 CPU 数取模决定数据包发送的目标套接字。

lb.bpf 提供了简单的对称 IP 对哈希实现。

复制生成的 eBPF 过滤器

cp ebpf/lb.bpf /usr/libexec/suricata/ebpf/

在 af-packet 接口配置中使用 cluster_ebpf 作为负载均衡方法,并指向 lb.bpf 文件

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_ebpf
  defrag: yes
  # 包含'loadbalancer'函数的eBPF文件将插入内核用作负载均衡函数
  ebpf-lb-file:  /usr/libexec/suricata/ebpf/lb.bpf
  ring-size: 200000

23.4.9. 配置 XDP 旁路

XDP 旁路允许 Suricata 通过 XDP 机制通知内核丢弃特定流的数据包。此操作在数据包到达 Linux 内核网络栈前完成,属于早期丢弃。

推荐使用 Linux 4.15 或更新版本。旧版内核需在 ebpf/xdp_filter.c 中设置 BUILD_CPUMAP0

复制生成的 XDP 过滤器

cp ebpf/xdp_filter.bpf /usr/libexec/suricata/ebpf/

配置 suricata.yaml 的 af-packet 接口部分。

示例使用 cluster_qm``(需 NIC 支持对称哈希)、``xdp-mode: driver 及 TCP 卸载/旁路

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_qm # 必须启用对称哈希!
  defrag: yes
  # XDP模式:"soft"为基于skb,"driver"为基于网卡,"hw"为支持eBPF的硬件
  xdp-mode: driver
  xdp-filter-file:  /usr/libexec/suricata/ebpf/xdp_filter.bpf
  # 若eBPF过滤器实现旁路功能,可设为yes启用
  bypass: yes
  ring-size: 200000
  # 使用Netronome等硬件XDP网卡时取消注释(默认值为yes)
  # use-percpu-hash: no

XDP 旁路兼容 AF_PACKET IPS 模式。旁路流的数据包将直接从一张网卡发送至另一张,不经内核网络栈。

使用硬件 XDP 卸载时,可能需设置 use-percpu-hash 为 false,并在编译安装 XDP 过滤器前将 USE_PERCPU_HASH 设为 0。

XDP 过滤器文件中,设置 ENCRYPTED_TLS_BYPASS 为 1 可旁路加密的 TLS 1.2 流量。注意这将使 Suricata 无法检测 443 端口的匹配流量。

若未启用 VLAN 跟踪(vlan.use-for-tracking 设为 false),还需在 xdp_filter.c 中将 VLAN_TRACKING 设为 0。

23.4.9.1. Intel 网卡配置

Intel 网卡不支持对称哈希,但可通过特定哈希函数模拟。

严格按以下步骤操作

ifconfig eth3 down

使用内核树内驱动:Intel 官网提供的驱动不支持 XDP。

启用对称哈希

ifconfig eth3 down
ethtool -L eth3 combined 16 # 至少16核
ethtool -K eth3 rxhash on
ethtool -K eth3 ntuple on
ifconfig eth3 up
./set_irq_affinity 0-15 eth3
ethtool -X eth3 hkey 6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A:6D:5A equal 16
ethtool -x eth3
ethtool -n eth3

set_irq_affinity 脚本可从任何 Intel x520/710 网卡驱动下载包获取。

注意: 此处使用低熵密钥实现对称哈希。对称哈希设置研究详情

23.4.9.2. 禁用网卡卸载功能

运行以下命令禁用卸载

for i in rx tx tso ufo gso gro lro tx nocache copy sg txvlan rxvlan; do
       /sbin/ethtool -K eth3 $i off 2>&1 > /dev/null;
done

23.4.9.3. 最大化负载均衡

尽量利用网卡的流均衡功能

for proto in tcp4 udp4 ah4 esp4 sctp4 tcp6 udp6 ah6 esp6 sctp6; do
   /sbin/ethtool -N eth3 rx-flow-hash $proto sd
done

此命令仅基于源/目的 IP 进行负载均衡。虽在均衡公平性上非最优,但确保分片流的所有数据包均到达同一线程(某些分片包可能无法获取端口信息)。

23.4.9.4. XDP CPU 重定向场景

若硬件不支持对称负载均衡但支持驱动模式 XDP,可使用 xdp_filter.bpfxdp_lb.bpf 中的 CPU 重定向映射功能。此模式下,XDP 过滤器负责负载均衡,各 CPU 处理包括内核 skb 结构创建在内的完整数据包处理流程。

需 Linux 4.15 或更新版本支持。

在 af-packet 接口配置中设置 xdp-cpu-redirect 为目标 CPU 集,并使用 cluster_cpu 作为负载均衡方法。同时需设置 CPU 亲和性确保 Suricata 使用分配了 skb 的核。

为避免乱序包,需将 RSS 队列数设为 1。以 eth3 为例

/sbin/ethtool -L eth3 combined 1

系统超过 64 核时,需在 xdp_lb.cxdp_filter.c 中将 CPUMAP_MAX_CPUS 设为更大值。

纯 XDP 负载均衡配置示例

- interface: eth3
  threads: 16
  cluster-id: 97
  cluster-type: cluster_cpu
  xdp-mode: driver
  xdp-filter-file:  /usr/libexec/suricata/ebpf/xdp_lb.bpf
  xdp-cpu-redirect: ["1-17"] # 或 ["all"] 在所有CPU上均衡
  ring-size: 200000

可通过 xdp_monitor 监控 CPU 重定向行为。该程序位于 Linux 源码树的 samples/bpf 目录,由 make 命令构建。示例输出

sudo ./xdp_monitor --stats
XDP-event       CPU:to  pps          drop-pps     extra-info
XDP_REDIRECT    11      2,880,212    0            Success
XDP_REDIRECT    total   2,880,212    0            Success
XDP_REDIRECT    total   0            0            Error
cpumap-enqueue   11:0   575,954      0            5.27       bulk-average
cpumap-enqueue  sum:0   575,954      0            5.27       bulk-average
cpumap-kthread  0       575,990      0            56,409     sched
cpumap-kthread  1       576,090      0            54,897     sched

23.4.9.5. 启动带 XDP 的 Suricata

启动启用 XDP 旁路的 Suricata

/usr/bin/suricata -c /etc/suricata/xdp-suricata.yaml --pidfile /var/run/suricata.pid  --af-packet=eth3 -vvv

确认输出中包含 XDP 过滤器启用信息(示例):

...
...
(runmode-af-packet.c:220) <Config> (ParseAFPConfig) -- 为接口eth3启用mmap内存锁定
(runmode-af-packet.c:231) <Config> (ParseAFPConfig) -- 在接口eth3启用tpacket v3捕获
(runmode-af-packet.c:326) <Config> (ParseAFPConfig) -- 为AF_PACKET(接口eth3)使用基于队列的集群模式
(runmode-af-packet.c:424) <Info> (ParseAFPConfig) -- af-packet将使用'/usr/libexec/suricata/ebpf/xdp_filter.bpf'作为XDP过滤器文件
(runmode-af-packet.c:429) <Config> (ParseAFPConfig) -- 为AF_PACKET(接口eth3)启用内核旁路功能
(runmode-af-packet.c:609) <Config> (ParseAFPConfig) -- eth3: 通过数据释放调用启用零拷贝模式
(util-runmodes.c:296) <Info> (RunModeSetLiveCaptureWorkersForDevice) -- 将使用8个线程
...
...

23.4.10. 固定映射用法

固定映射在创建进程退出后仍保持附着状态,且可被外部工具访问。在 Suricata 旁路场景中,这可用于保持旁路流表活跃,避免重启时已旁路流量冲击 Suricata。在套接字过滤器场景中,可用于由外部工具维护映射。

使用固定映射前需挂载 bpf 伪文件系统

sudo mount -t bpf none /sys/fs/bpf

或在 /etc/fstab 添加

bpffs                      /sys/fs/bpf             bpf     defaults 0 0

后执行 sudo mount -a

固定映射将以文件形式存在于 /sys/fs/bpf 目录。Suricata