.. _ebpf-xdp: eBPF 与 XDP ============ 简介 ------------ eBPF 全称为扩展版 BPF。这是 Berkeley 数据包过滤器在近期 Linux 内核版本中的扩展实现。 它通过 C 语言开发的 eBPF 程序提供更高级功能,并支持内核与用户空间之间共享结构化数据。 在 Suricata 中,eBPF 用于以下三个方面: - eBPF 过滤器:可开发任何类 BPF 过滤器。示例提供了仅接受特定 VLAN 数据包的过滤器,并包含旁路实现。 - eBPF 负载均衡:提供可编程负载均衡。示例实现了简单的 IP 对负载均衡。 - XDP 程序:Suricata 可加载 XDP 程序。示例提供了旁路程序。 旁路功能可通过 eBPF 和 XDP 实现。XDP 的优势在于能在最早阶段丢弃数据包,因此性能更优。但被旁路的流量不会进入网络栈,故仅适用于镜像/嗅探流量,常规流量无法使用此功能。 旁路实现依赖于 eBPF 的核心概念:映射(map)。映射是用户空间与内核空间/硬件共享的数据结构,支持两者间的交互和信息传递。映射通常实现为可存储任意键值对的数组或哈希表。 XDP ~~~ XDP 为 Linux 原生提供了另一种优化 Suricata 在高速网络嗅探性能的方案: XDP(eXpress Data Path)作为 IO Visor 项目的一部分,在 Linux 内核中提供了高性能、可编程的网络数据路径。XDP 在软件栈最底层实现裸金属级数据包处理,兼具高速与可编程性。此外,无需修改内核即可通过集成快路径动态实现新功能。 更多 XDP 信息: - `IOVisor XDP 页面 `__ - `Cilium BPF 与 XDP 参考指南 `__ 系统要求 ------------ 需使用支持 XDP 的内核版本,为获得最佳性能建议网卡驱动支持 XDP。 Suricata XDP 代码已在 4.13.10 内核测试,但需 4.15 或更新版本才能使用 CPU 重定向映射等全部功能。 若使用 Intel 网卡,需使用内核树内驱动的 NIC 驱动,树外驱动不包含 XDP 支持。 建议网卡支持 RSS 对称哈希,否则需使用 XDP CPU 重定向映射功能。 环境准备 ------------- 本指南已在 Debian/Ubuntu "LTS" Linux 验证。 禁用 irqbalance ~~~~~~~~~~~~~~~~~~ 多数场景下 ``irqbalance`` 可能导致问题,建议停用 :: systemctl stop irqbalance systemctl disable irqbalance 内核版本 ~~~~~~ 需运行 4.13 或更新版本内核。 Clang 及依赖 ~~~~~~~~~~~~~~~~~~~~~~ 确保系统已安装 ``clang`` (>=3.9) :: sudo apt install clang 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 配置。 编译安装 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 配置旁路 ------------ 若计划使用 eBPF 或 XDP 实现内核/硬件级旁路,需启用以下功能: 首先在 ``suricata.yaml`` 的 `stream` 部分启用 `bypass` :: stream: bypass: true 这将使流在达到深度阈值时立即被旁路。 如需旁路加密流量,可在应用层 TLS 部分设置 `encryption-handling` 为 `bypass` :: 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;) 配置 eBPF 过滤器 ----------------- 修改 `ebpf/vlan_filter.c` 中的 VLAN ID 列表以适配您的网络。`ebpf/filter.c` 还提供了基于 IPv4 地址集的过滤示例。详见 :ref:`ebpf-pinned-maps`。 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 配置 eBPF 旁路 ----------------- 也可使用 eBPF 旁路。加载 `bypass_filter.bpf` 文件并在 ``suricata.yaml`` 的 af-packet 配置中设置 `bypass` 为 `yes` :: - 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_v4` 和 `flow_table_v6` 这两个每 CPU 数组映射,其定义需与 `bypass_filter.c` 中的一致。Suricata 将通过这两个映射维护旁路流列表。 若未启用 VLAN 跟踪(``suricata.yaml`` 中 `vlan.use-for-tracking` 设为 `false`),还需在 ``bypass_filter.c`` 中将 ``VLAN_TRACKING`` 宏设为 `0`。 配置 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 配置 XDP 旁路 ---------------- XDP 旁路允许 Suricata 通过 XDP 机制通知内核丢弃特定流的数据包。此操作在数据包到达 Linux 内核网络栈前完成,属于早期丢弃。 推荐使用 Linux 4.15 或更新版本。旧版内核需在 ``ebpf/xdp_filter.c`` 中设置 ``BUILD_CPUMAP`` 为 `0`。 复制生成的 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。 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 网卡驱动下载包获取。 **注意:** 此处使用低熵密钥实现对称哈希。`对称哈希设置研究详情 `_ 禁用网卡卸载功能 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 运行以下命令禁用卸载 :: 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 最大化负载均衡 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 尽量利用网卡的流均衡功能 :: 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 进行负载均衡。虽在均衡公平性上非最优,但确保分片流的所有数据包均到达同一线程(某些分片包可能无法获取端口信息)。 XDP CPU 重定向场景 ~~~~~~~~~~~~~~~~~~~~~~~~~ 若硬件不支持对称负载均衡但支持驱动模式 XDP,可使用 `xdp_filter.bpf` 和 `xdp_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.c` 和 `xdp_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 启动带 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) (ParseAFPConfig) -- 为接口eth3启用mmap内存锁定 (runmode-af-packet.c:231) (ParseAFPConfig) -- 在接口eth3启用tpacket v3捕获 (runmode-af-packet.c:326) (ParseAFPConfig) -- 为AF_PACKET(接口eth3)使用基于队列的集群模式 (runmode-af-packet.c:424) (ParseAFPConfig) -- af-packet将使用'/usr/libexec/suricata/ebpf/xdp_filter.bpf'作为XDP过滤器文件 (runmode-af-packet.c:429) (ParseAFPConfig) -- 为AF_PACKET(接口eth3)启用内核旁路功能 (runmode-af-packet.c:609) (ParseAFPConfig) -- eth3: 通过数据释放调用启用零拷贝模式 (util-runmodes.c:296) (RunModeSetLiveCaptureWorkersForDevice) -- 将使用8个线程 ... ... .. _ebpf-pinned-maps: 固定映射用法 ----------------- 固定映射在创建进程退出后仍保持附着状态,且可被外部工具访问。在 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