解析网络入侵检测系统的构造
分层协议解析、受 Snort 启发的规则和有状态威胁检测;单线程解析合成 SYN 包时达到 500K+ 包/秒。
IDS 处理链路
Snort 和 Suricata 等商用入侵检测系统遵循共同架构:捕获 → 解析 → 检测 → 告警。SentinelFlow 使用 C++17 和 libpcap 实现完整链路,单线程解析速度为 500K+ 包/秒。
捕获:BPF 过滤器在内核空间运行
SentinelFlow 基于 libpcap 和多态 PacketCapture 接口,用 LiveCapture 处理实时流量,用 PcapFileReader 离线分析。关键优化是 BPF:tcp port 80 这样的过滤表达式通过 pcap_compile() 编译,在内核中运行。不匹配的包不会复制到用户空间,比应用层过滤高效得多。
分层协议解析
网络帧是嵌套结构,不能跳到固定字节偏移,因为每一层都可能变长:
- Ethernet(14 字节),对 EtherType 使用
ntohs后分派到 IPv4(0x0800)或 ARP(0x0806) - IPv4,IHL 字段指示 L4 起点,IP 选项会改变长度。
- TCP,
data_offset指示载荷起点,TCP 选项会改变长度。 - UDP(8 字节),首部固定,但当端口为 53 时会触发 DNS 解析
- DNS,遍历带长度前缀的标签编码查询名
- ICMP / ARP,类型代码和硬件地址
每个解析器都是直接操作原始 const uint8_t* 的无状态自由函数,无复制、无分配。ParsedPacket 为每层使用 std::optional<T>:解析失败或协议不存在时保持空值,并跳过下游层。
受 Snort 启发的规则引擎
SentinelFlow 实现了 Snort 规则语言的子集;该语言是基于签名检测的通行标准:
alert tcp any any -> any 22 (msg:"SSH brute force"; flags:S; threshold:10,60; sid:2001;)规则采用声明式表达。规则头快速预筛协议、IP 和端口;选项进一步细化匹配,包括标志位掩码、载荷字符串和 DNS 查询长度阈值。规则解析器将这些元素提取为结构化对象,由签名匹配器逐包判断。
有状态与无状态检测
签名匹配能在单个包中发现已知恶意模式,但很多真实攻击分布在多个包之间:
- 端口扫描,需要持续跟踪每个源 IP 访问的目标端口集合。SentinelFlow 为每对 IP 维护
std::set<uint16_t>,在 60 秒内访问 15+ 个不同端口时告警。 - SYN 洪泛,通过半开连接速率检测。每个目标 IP 使用
std::deque保存 SYN 时间戳,访问时延迟清理;10 秒内出现 100+ SYN 时触发。 - DNS 隧道,通过编码的 DNS 查询外传数据会产生异常长的查询名。检测器跟踪超过 50 个字符的查询,并在数量超过阈值时告警。
全部有状态检测器使用滑动时间窗口与延迟清理:旧记录在下次访问时删除,不依赖后台线程,从而避免同步开销。
零复制解析性能
解析器直接操作 libpcap 提供的原始缓冲区,不做中间复制,也不为每个包动态分配。memcpy + ntohs/ntohl 是高性能包处理的常见方式,DPDK、PF_RING 和生产 NIDS 均采用它。基准证明单线程达到 500K+ 包/秒;这里仅衡量合成 TCP SYN 包的解析吞吐量。
告警输出
告警系统采用策略模式,AlertManager 将每条告警分派到全部已注册输出:
- 控制台:以 ANSI 颜色表示严重程度(绿 → 黄 → 红 → 加粗红)
- CSV:符合 RFC 4180,正确转义,可供 SIEM 摄取
添加 syslog、webhook 或 Kafka 等新输出,只需实现一个 emit() 方法。