- 网络安全
【免费下载链接】suricata
Suricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata community.
Suricata 通过 DPDK(Data Plane Development Kit)捕获模块实现绕过内核网络栈的高性能报文处理。本文以官方 DPDK 捕获硬件配置文档为主线,系统讲解 Suricata 启动时的大页(hugepage)用量分析、基于 Bond PMD 的多网卡聚合、节能型中断模式、接口属性自动配置、链路状态检测超时与 VLAN 硬件剥离等进阶用法,并结合仓库源码(如 src/runmode-dpdk.c、src/util-hugepages.c)与示例配置(suricata.yaml.in)给出可直接落地的配置示例与底层原理。读完本文,你将掌握在 TAP/IPS 场景下正确规划大页数量、组建链路聚合、按接口启用中断模式并精准调优 DPDK 队列参数的完整方法。
DPDK 捕获模块基础背景
DPDK 是一套面向数据面应用的高速报文处理库与驱动集合,其核心价值在于绕过内核网络协议栈,让用户态应用直接访问 NIC 存放报文的内存,从而获得显著的性能提升。在 DPDK 模式下,报文不是通过中断机制交付,而是由应用主动轮询(polling)NIC 获取新到达的报文;Suricata 直接基于报文描述符处理数据,无需额外拷贝。
要把 DPDK 捕获模块用起来,前提条件包括:
- 编译期启用 DPDK 支持:
./configure --enable-dpdk; - 安装好
pkg-config并能正确查询 DPDK 版本(pkg-config --modversion libdpdk),当前仓库要求最低 DPDK 版本为 19.11; - 分配足够数量的大页(hugepage),且大页必须位于 NIC 与 CPU 核心所在的 NUMA 节点上。
完整的 DPDK 基础搭建流程见 suricata-yaml 配置文档中的 DPDK 捕获模块章节,本文聚焦于该文档(capture-hardware/dpdk.rst)中讲解的进阶与边缘场景。
Hugepage 分析:在启动期诊断大页过量分配
Suricata 具备对系统已用大页的自动分析能力,在可能存在大页过量分配(overallocation)时尤为有用。分析的目的是评估当前使用中的大页数量,并给出合适的大页数量建议,使 Suricata 既能以最优状态运行,又为系统上其他应用保留足够内存。
工作原理:前后快照对比
分析机制本质上是对 Suricata 初始化之前与之后两份大页快照进行比较。由于 Suricata 初始化完成后不再分配大页,因此后置快照即可反映 Suricata 实际占用的大页规模。
在源码层面,这一逻辑实现在 src/util-hugepages.c 的SystemHugepageEvaluateHugepages()函数中:它逐个 NUMA 节点、逐种大页尺寸对比pre_s(初始化前快照)与post_s(初始化后快照),并输出多种建议:
- 若初始化前后 free 数量相同,说明这些大页未被使用,可考虑释放(
SCLogPerf输出"unused and can be deallocated"); - 若 2048kB 大页在初始化后 free 为 0,说明该 NUMA 节点大页被全部用尽,建议增加大页数量以避免跨 NUMA 分配内存;
- 若 free 大页占比仍高于 50%,还会基于实际用量乘以 1.15 的余量系数给出一个建议的大页总数。
查看方式与触发条件
- 分析结果在Perf 日志级别下输出,并仅在Suricata 启动过程中打印;
- 只有当 Suricata 检测到系统大页分配存在**异常(discrepancy)**时才会打印输出。
分析前的"干净状态"准备
官方建议从"干净状态"(即所有大页均空闲)开始执行分析,尤其当系统上没有其他依赖大页的应用运行时效果最佳。可用以下两种方式检查大页是否空闲:
# 全局检查 cat /proc/meminfo HugePages_Total: 1024 HugePages_Free: 1024 # 按 NUMA 节点检查(取决于 NUMA 节点 ID、大页尺寸以及 # nr_hugepages/free_hugepages 两个计数),例如: cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/free_hugepages清理未释放的大页
在 Suricata 及其他依赖大页的应用终止后,如果 free 大页数量不等于总大页数量,说明部分大页没有被完整释放。修复办法是从挂载大页的目录(文件系统)中移除 DPDK 相关文件。操作时务必谨慎——尤其当其他依赖大页的应用仍在运行时,删除操作会破坏这些应用的内存功能。
sudo rm -rf /dev/hugepages/rtemap_* # 查看大页挂载位置: dpdk-hugepages.py -s # 或 mount | grep huge注:
dpdk-hugepages.py是 DPDK 自带的大页管理脚本(在仓库中也用于--setup分配大页,见 suricata-yaml.rst)。rtemap_*是 DPDK 运行时在大页文件系统上创建的共享内存映射文件。
Bond interface:用 Bond PMD 聚合多个物理网口
Bond PMD(Link Bonding Poll Mode Driver)是 DPDK 提供的纯软件机制,用于把多块物理网卡聚合成单个逻辑接口。典型用途包括:
- 将被分光(TAP)接口的双向流量投递到同一个 worker(例如一条链路的两个方向分别进入两块物理网卡时);
- 通过监控多条链路实现冗余;
- 通过跨多条链路负载均衡提升网络吞吐。
Bond PMD 本质上是操纵多块物理网卡的虚拟驱动,支持多种工作模式(如 round-robin、active-backup 等),具体模式可查阅 DPDK 官方 Bond PMD 编程指南。使用时有两点约束需要注意:
- 被聚合的接口必须是相同设备类型——例如两块物理口都必须运行在 mlx5 PMD 上;
- Bond PMD 支持多队列,因此可以在 workers runmode 下工作;它对单个端口流量分布不应产生影响,流仍然按照各物理口独立的 RSS 配置进行分发。
双向 TAP 流量场景示例
假设 Suricata 监控 2 块接收光口 TAP 流量的接口:通信的一个方向到达接口 A,另一个方向到达接口 B。此时可在suricata.yaml的 DPDK 部分通过eal-params的vdev参数创建虚拟 Bond 设备:
... dpdk: eal-params: proc-type: primary vdev: 'net_bonding0,mode=0,slave=0000:04:00.0,slave=0000:04:00.1' # DPDK capture support # RX queues (and TX queues in IPS mode) are assigned to cores in 1:1 ratio interfaces: - interface: net_bonding0 # PCIe address of the NIC port # Threading: possible values are either "auto" or number of threads # - auto takes all cores # in IPS mode it is required to specify the number of cores and the # numbers on both interfaces must match threads: 4 ...配置项拆解
vdev是新增到dpdk.eal-params段落中的虚拟设备参数。DPDK EAL 会在初始化阶段创建指定的虚拟设备;- 本例中 EAL 创建了一个类型为
net_bonding的设备,后缀0表示接口名(即net_bonding0); - 设备名之后跟随额外参数:
mode=0即 Bond PMD 文档中的round-robin 模式; slave=0000:04:00.0、slave=0000:04:00.1依次追加成员(slave)物理口的 PCIe 地址。
从源码看,Suricata 在 src/runmode-dpdk.c 的InitEal()中把dpdk.eal-params下的每个键值对转换成--key value形式的 EAL 参数并交给rte_eal_init();src/util-dpdk-bonding.c 则负责 Bond 相关的运行期处理,例如通过BondingIsBond()依据驱动名net_bonding识别 Bond 端口,以及通过rte_eth_bond_members_get()/rte_eth_bond_slaves_get()获取聚合成员。此外在自动计算 mempool 大小时,源码会对net_bonding驱动走专门的BondingMempoolSizeCalculate()逻辑(见下文"自动接口配置")。
对应的 threading 配置
当设备在 EAL 参数中指定后,net_bonding0就能被interfaces列表引用。注意列表里填写的不是物理口的 PCIe 地址,而是net_bonding0这个虚拟接口名。threads的数量也需要与interfaces列表项对齐,方法是启用set-cpu-affinity并分别列出管理线程与 worker 线程使用的 CPU:
... threading: set-cpu-affinity: yes cpu-affinity: management-cpu-set: cpu: [ 0 ] # include only these CPUs in affinity settings receive-cpu-set: cpu: [ 0 ] # include only these CPUs in affinity settings worker-cpu-set: cpu: [ 2,4,6,8 ] ...这与 suricata.yaml.in 中 threading 段落的结构一致(management-cpu-set用于流超时处理与计数器线程,worker-cpu-set用于 worker 线程,还支持interface-specific-cpu-set为单个接口单独指定 CPU 集)。源码ConfigSetThreads()会强制校验:DPDK runmode 必须配置线程亲和(否则直接报错),且worker-cpu-set与management-cpu-set均为必填项,worker 与 management 线程不应重叠。
Interrupt(省电)模式:按接口混合轮询与中断
DPDK 传统上以轮询模式著称:CPU 核心持续不断地向 NIC 查询报文。轮询的优势是低延迟、高性能,但在零星或低流量场景下会造成不必要的 CPU 空转。为此 DPDK 提供了interrupt(中断)模式。
收益与注意事项
- 中断模式最直接的收益是功耗效率。根据官方测试,目前未观察到性能下降,Suricata 性能反而有轻微提升;
- 使用 IPS runmode 的用户需要注意:中断可能引入非确定性延迟;不过该延迟不会高于其他(如 AF_PACKET / AF_XDP 等)捕获方式。
按接口独立配置
中断模式支持按接口(per-interface)开启,因此可以搭建"混合"环境——部分 worker 保持轮询,部分 worker 使用中断。配置项位于suricata.yaml的 DPDK 段落:
... dpdk: eal-params: proc-type: primary interfaces: - interface: 0000:3b:00.0 interrupt-mode: true threads: 4源码实现上,src/runmode-dpdk.c 将interrupt-mode映射为接口配置属性(dpdk_yaml.irq_mode),默认值为false(宏DPDK_CONFIG_DEFAULT_INTERRUPT_MODE)。开启时设置DPDK_IRQ_MODE标志位,并在PortConfSetInterruptMode()中把端口配置的port_conf->intr_conf.rxq置 1,从而让 RX 队列以中断方式唤醒。
自动接口配置:让 Suricata 依据 NIC 能力自算参数
dpdk.interfaces下的大部分属性都可以手工指定,但 Suricata 也支持根据 NIC 能力自动配置接口属性。只需把以下四个接口属性设为auto:
mempool-sizemempool-cache-sizerx-descriptorstx-descriptors
auto模式下,Suricata 会基于 NIC 能力做"最佳努力"(best-effort)计算:
- RX 描述符:取小于或等于 NIC 支持描述符数量的最大 2 的幂。源码
ConfigSetRxDescriptors()中调用GreatestPowOf2UpTo(max_desc)实现; - TX 描述符:取决于
copy-mode的配置。IDS(none)模式下默认不创建 TX 队列、TX 描述符为 0;IPS 与 TAP 模式下 TX 描述符数量与 RX 描述符一致(源码ConfigSetTxDescriptors()依据iface_sends_pkts决定是否分配,ConfigIfaceSendsPkts()判断ips/tap两种模式); - mempool 大小与 cache 大小:在描述符数量确定后推导。源码
MempoolSizeCalculateAutosize()把nb_rx_desc + nb_tx_desc + 1向上对齐到 2 的幂,再减 1(保证形如2^q - 1,使全部描述符可被使用);MempoolCacheSizeCalculate()则要求 cache 不超过RTE_MEMPOOL_CACHE_MAX_SIZE(默认 512)与mempool-size / 1.5,同时满足"mempool-size 能被 cache 整除"的约束,最终取满足条件的最小最大公约数。
RX/TX 描述符被设置为可能的最大值,是为了在流量突发时提供更多缓冲空间,代价是占用更多内存。因此,如果默认值不符合实际资源约束,仍然可以手工指定这四个属性。
注意:Mellanox ConnectX-4 网卡可能不支持 RX/TX 描述符的自动配置,此时应改为固定值(例如 16384)。
关于手动指定mempool-size的分摊逻辑,可参考 suricata-yaml.rst 中的说明:手动设定值(如 65536)会按接口 worker 数均分(例如 4 个 worker 时每个 worker 分得 16383 个报文对象),接口 mempool 的字节内存约为mempool-size * mtu,据此可以估算所需大页数量。
Link State Change 超时:等待链路就绪再收包
linkup-timeoutYAML 配置项用于设置等待接口链路被检测到的时间上限,确保 Suricata 在链路真正 up 之前不开始处理报文。该选项对Intel E810(Ice)网卡尤其有用——这类网卡在接口启动后要过几秒才开始接收报文;若关闭此检查,Suricata 会报告"已启动"但实际上还要过几秒才开始处理报文。官方文档指出该问题未在其他网卡上观察到。
行为细节:
- 设为
0表示跳过链路检查; - 若超时后链路仍未 up,Suricata 会警告用户但继续引擎初始化。
源码ConfigSetLinkupTimeout()接受的取值范围为 0 到UINT16_MAX(65535)的正整数,默认值为 0(禁用,宏DPDK_CONFIG_DEFAULT_LINKUP_TIMEOUT)。在 suricata.yaml.in 的注释中也说明:linkup-timeout: 0表示"等待多少秒后放弃,0 表示禁用链路状态检查",可按需调整秒数。
Encapsulation stripping:硬件 VLAN 剥离
Suricata 支持在受支持的 NIC上启用硬件卸载的封装剥离(hardware-offloaded encapsulation stripping)。目前支持的是VLAN 封装剥离,通过接口属性vlan-strip-offload开启。
dpdk: interfaces: - interface: 0000:3b:00.0 vlan-strip-offload: true默认值为false(宏DPDK_CONFIG_DEFAULT_VLAN_STRIP)。源码PortConfSetVlanOffload()会先检查 NIC 的 RX offload 能力是否包含RTE_ETH_RX_OFFLOAD_VLAN_STRIP:支持则在端口配置的rxmode.offloads中置位并记录 "hardware VLAN stripping enabled";不支持则打印警告并自动禁用。Suricata 启动时默认关闭所有 offload 以防止任何报文被修改,只有显式配置的 offload(如校验和验证、VLAN 剥离)才会按需开启。
接口参数速查表
以下参数均可在dpdk.interfaces列表项中配置(也支持default项作为缺失配置的兜底值,完整示例见 suricata.yaml.in 的dpdk:段落):
| 参数 | 默认值 | 说明 |
|---|---|---|
threads | auto | 每个接口的 worker 线程数;auto按 affinity 核心自动分配,IPS 模式需显式指定且双接口数量必须一致 |
interrupt-mode | false | 按接口开启中断(省电)模式 |
promisc | true | 混杂模式,捕获所有报文 |
multicast | true | 同时检测组播报文 |
checksum-checks | true | 是否校验报文校验和 |
checksum-checks-offload | true | 尽可能把校验和验证卸载给 NIC |
mtu | 1500 | 设备 MTU(字节) |
vlan-strip-offload | false | 硬件 VLAN 剥离 |
rss-hash-functions | auto(RTE_ETH_RSS_IP) | RSS 哈希函数标志,可用-vvv启动观察DumpRssFlags输出后按 16 进制指定 |
linkup-timeout | 0 | 链路检测等待秒数,0 表示禁用 |
mempool-size | auto | 按 RX/TX 描述符与线程数自动计算 |
mempool-cache-size | auto | 依据 mempool 大小自动计算 |
rx-descriptors | auto | 最大 RX 描述符数 |
tx-descriptors | auto | 最大 TX 描述符数(IDS 模式自动为 0) |
copy-mode | none | none(IDS)/tap(转发并告警)/ips(转发并按规则丢弃) |
copy-iface | none | IPS/TAP 模式下的第二块接口 PCIe 地址 |
以上映射关系与取值约束均可在 src/runmode-dpdk.c 顶部的DPDKIfaceConfigAttributes dpdk_yaml结构及ConfigLoad()配置装载函数中逐一核对。
运行与验证
DPDK 捕获模块在 workers runmode 下工作,报文分发依赖 NIC 的 Receive Side Scaling(RSS)——Suricata 内部使用对称哈希(0x6d5a)把双向流定向到同一个 worker,保证流完整性。每个 worker 对应 1 个 RX 队列(IPS 模式下另有 1 个 TX 队列),RX 队列数恒等于线程数;TX 队列数要么等于 RX 队列数,要么在 IDS 模式下置 0(通过tx-descriptors: auto或 0 实现)。
仓库还提供了 DPDK 相关的活体测试脚本,可用于验证运行环境与配置正确性:qa/live/dpdk.sh、qa/live/dpdk/dpdk-testsuite.sh 与 qa/live/dpdk/dpdk-checklog.sh。如果追求更深入的理解,可继续阅读:
- DPDK 运行模式与接口配置装载:src/runmode-dpdk.c、src/runmode-dpdk.h
- DPDK 报文接收源:src/source-dpdk.c
- Bond 聚合与 RSS 辅助逻辑:src/util-dpdk-bonding.c、src/util-dpdk-rss.c
- 各 PMD 定制逻辑:src/util-dpdk-mlx5.c、src/util-dpdk-i40e.c、src/util-dpdk-ice.c、src/util-dpdk-ixgbe.c
- 大页快照与评估实现:src/util-hugepages.c
小结
围绕 DPDK 捕获这一主题,本文从启动期大页分析出发,依次覆盖了 Bond PMD 链路聚合、按接口中断模式、接口属性自动配置、链路超时检测与 VLAN 硬件剥离等进阶能力。无论是 TAP 双向流量汇聚、IPS 双口成对部署,还是低流量场景下的功耗优化,都可以在 suricata.yaml.in 的dpdk:与threading:段落中按上述示例落地,并结合启动时的 Perf 日志与-vvv详细输出持续调优。
- 网络安全
【免费下载链接】suricata
Suricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata community.
相关推荐
Suricata高性能数据包捕获配置指南
Suricata高性能数据包捕获配置指南 引言 Suricata作为一款开源的网络入侵检测和防御系统 NIDS/NIPS ,其性能表现很大程度上取决于数据包捕获
网络安全Suricata与DPDK高性能数据包捕获技术详解
Suricata与DPDK高性能数据包捕获技术详解 引言 在现代网络安全领域,高性能数据包处理能力至关重要。Suricata作为一款开源的网络威胁检测引擎,通过
网络安全MOOTDX 数据接口开发指南:从模块解析到高级配置
MOOTDX 数据接口开发指南:从模块解析到高级配置 一、核心功能模块全景解析 MOOTDX 作为通达信数据接口的 Python 封装,采用模块化设计实现行情数
金融科技数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考