news 2026/9/28 8:47:25

Zeek Pcap 模块内置函数详解:动态管理 PCAP/BPF 抓包过滤器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Zeek Pcap 模块内置函数详解:动态管理 PCAP/BPF 抓包过滤器
  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载

导读

本文以 Zeek 仓库中 doc/scripts/base/bif/pcap.bif.zeek.rst 为骨架,系统讲解Pcap命名空间下五个与抓包过滤相关的内置函数:Pcap::precompile_pcap_filter、Pcap::install_pcap_filter、Pcap::error、Pcap::get_filter_state、Pcap::get_filter_state_string,以及枚举设备接口的Pcap::findalldevs。读完本文,你将掌握如何在 Zeek 脚本中动态编译、安装、查询 BPF 过滤器,如何读懂过滤器的编译状态与错误信息,并理解这些函数在 Zeek 底层iosource与packet-filter框架中的真实调用链。所有结论均以仓库内 src/iosource/pcap/pcap.bif、scripts/base/frameworks/packet-filter/main.zeek 与测试用例为准。


一、Pcap 模块概览:从 BIF 到抓包内核

在 Zeek 中,Pcap命名空间封装了与 libpcap 交互的内置函数,它们通过.bif(Built-In Function)机制生成 Zeek 脚本可直接调用的函数。对应的 BIF 源文件位于 src/iosource/pcap/pcap.bif,文档中给出的函数签名与行为均由该文件中的%%{ ... }%%C++ 代码实现。

该模块同时声明了抓包相关的常量,位于 scripts/base/init-bare.zeek:

常量类型默认值含义
snaplencount—抓包快照长度
bufsizecount—在线抓包缓冲区大小
bufsize_offline_bytescount—离线(读 pcap 文件)缓冲区字节数
non_fd_timeoutinterval20usec非文件描述符型包源的轮询超时

这些常量说明Pcap模块不仅提供过滤函数,也承载着 libpcap 底层抓包参数的配置入口。

Pcap模块涉及的两个核心数据类型同样定义在 scripts/base/init-bare.zeek 中:

  • PcapFilterID(init-bare.zeek#L1533-L1535):枚举类型,初始只有None,用于标识不同的 BPF 过滤器;
  • Pcap::filter_state(init-bare.zeek#L5975-L5980):枚举{ ok, fatal, warning },描述过滤器编译/初始化的状态;
  • Pcap::Interface/Pcap::Interfaces(init-bare.zeek#L5955-L5973):描述抓包接口的记录类型与接口集合。

二、五个核心函数一览

原文档 pcap.bif.zeek.rst 以 Summary 表格的形式列出全部函数,整理如下:

函数签名作用
Pcap::precompile_pcap_filter(id: PcapFilterID, s: string): bool预编译 PCAP 过滤器并绑定到给定标识符
Pcap::install_pcap_filter(id: PcapFilterID): bool安装已预编译的过滤器
Pcap::error(): string返回最后一次 PCAP 错误的字符串表示
Pcap::get_filter_state(id: PcapFilterID): Pcap::filter_state返回过滤器初始化状态
Pcap::get_filter_state_string(id: PcapFilterID): string返回过滤器初始化产生的错误信息字符串
Pcap::findalldevs(): Pcap::Interfaces枚举本机所有可用的抓包设备

其中前五个构成"编译 → 安装 → 查询 → 排错"的完整动态过滤工作流;findalldevs则用于设备发现。


三、预编译过滤器:Pcap::precompile_pcap_filter

3.1 文档定义的接口

function Pcap::precompile_pcap_filter(id: PcapFilterID, s: string): bool
  • 参数id:用于后续引用过滤器s的 PCAP 标识符;
  • 参数s:PCAP 过滤表达式,合法语法参见man tcpdump;
  • 返回值:s合法且预编译成功时返回T,否则返回F。

3.2 源码实现与限制

src/iosource/pcap/pcap.bif#L36-L60 中的实现揭示了两个关键细节:

  1. ID 空间上限为 100:id->AsEnum() >= 100时直接报错并返回false。原因是底层用 vector 存储过滤器以支持快速查找,限制 ID 空间防止其无限增长。错误信息为"PCAP filter ids must remain below 100 (is N)"。
  2. 编译状态联动:实现通过zeek::iosource_mgr->GetPktSrc()获取当前活动的包源(PktSrc),调用其PrecompileFilter(id, s)(虚方法,默认实现见 src/iosource/PktSrc.h#L188),随后取得对应的BPF_Program并检查其状态:只有编译成功且过滤器状态为FilterState::OK时,函数才返回T。

BPF_Program是对 libpcapstruct bpf_program的封装,定义于 src/iosource/BPF_Program.h,其Compile()方法内部对应pcap_compile()。对 libpcap 源而言,编译发生在 src/iosource/pcap/Source.cc#L266-L279 的PcapSource::CompileFilter()中,失败时错误信息形如cannot compile BPF filter "..." : <详情>。

3.3 何时使用

预编译的目的是把语法检查与真正的安装分离:脚本可以先在任何时刻(例如zeek_init)验证过滤器表达式是否合法,再在合适的时机(如某个事件触发时)将其安装生效,避免在流量处理的关键路径上执行昂贵的编译。


四、安装过滤器:Pcap::install_pcap_filter

4.1 文档定义的接口

function Pcap::install_pcap_filter(id: PcapFilterID): bool
  • 参数id:已通过Pcap::precompile_pcap_filter预编译过的过滤器标识符;
  • 返回值:与id关联的过滤器安装成功时返回T。

4.2 源码实现

src/iosource/pcap/pcap.bif#L80-L89 中,安装动作最终落到包源的SetFilter(id)(纯虚方法,见 src/iosource/PktSrc.h#L202):

zeek::iosource::PktSrc* ps = zeek::iosource_mgr->GetPktSrc(); if ( ps && ! ps->SetFilter(id->AsInt()) ) success = false;

不同的包源实现各不相同:

  • libpcap 源PcapSource:在 src/iosource/pcap/Source.cc#L282 实现,将预编译的 BPF 程序绑定到 pcap 句柄;
  • AF_Packet 源AF_PacketSource:在 src/iosource/af_packet/AF_Packet.cc#L318-L322 实现PrecompileFilter与SetFilter;
  • pcapng 源:SetFilter直接返回true(src/iosource/pcapng/Source.h#L36),即对 pcapng 输入不做过滤。

这说明Pcap模块的过滤器机制是包源无关的抽象,同一套脚本接口可驱动不同底层抓包引擎。

4.3 典型用法:先预编译,再安装

仓库测试 testing/btest/core/pcap/dynamic-filter.zeek 展示了标准的两阶段用法:

redef enum PcapFilterID += { A, B }; event zeek_init() { if ( ! Pcap::precompile_pcap_filter(A, "port 80") ) print "error 1"; if ( ! Pcap::precompile_pcap_filter(B, "port 53") ) print "error 2"; } event new_packet(c: connection, p: pkt_hdr) { ++cnt; if ( cnt == 1 ) if ( ! Pcap::install_pcap_filter(A) ) print "error 3"; if ( cnt == 2 ) if ( ! Pcap::install_pcap_filter(B) ) print "error 4"; }

该测试使用wikipedia.pcap离线流量:zeek_init阶段预编译port 80与port 53两个过滤器,然后在new_packet事件中先后安装,验证"先编译后安装"的完整链路。


五、读取过滤状态:get_filter_state与get_filter_state_string

5.1Pcap::get_filter_state

function Pcap::get_filter_state(id: PcapFilterID): Pcap::filter_state

返回过滤器初始化状态;若当前没有活动包源,或过滤器 ID 不存在,则返回ok。

实现见 src/iosource/pcap/pcap.bif#L128-L139:通过ps->GetBPFFilter(id)获取BPF_Program后,将其GetState()映射为Pcap::filter_state枚举值返回。FilterState的 C++ 定义在 src/iosource/BPF_Program.h#L16-L20:

enum class FilterState : uint8_t { OK, FATAL, // results in Reporter::Error WARNING // results in Reporter::Warning };

与 Zeek 侧的枚举{ ok, fatal, warning }(init-bare.zeek#L5976-L5980)一一对应:fatal表示会阻止 Zeek 继续运行的问题,warning表示非致命、仅需记录的告警。

5.2Pcap::get_filter_state_string

function Pcap::get_filter_state_string(id: PcapFilterID): string

返回过滤器初始化过程中报告的警告/错误信息:

  • 有错误信息时返回对应字符串;
  • 未遇到任何问题时返回空字符串;
  • 若没有活动包源或过滤器 ID 不存在,返回"<unknown>"。

实现(src/iosource/pcap/pcap.bif#L152-L162)直接返回BPF_Program::GetStateMessage()(src/iosource/BPF_Program.h#L76),这是 libpcap 编译过程中产生的原始错误文本。

5.3 在 PacketFilter 框架中的组合使用

这两个函数被 scripts/base/frameworks/packet-filter/main.zeek 中的install()函数组合使用,构成完整的失败处理流程(main.zeek#L300-L326):

if ( ! Pcap::precompile_pcap_filter(DefaultPcapFilter, tmp_filter) ) { local state = Pcap::get_filter_state(DefaultPcapFilter); if ( state == Pcap::fatal ) { NOTICE(Notice::Info($note=Compile_Failure, $msg=fmt("Compiling packet filter failed"), $sub=tmp_filter)); error_string = fmt("Bad pcap filter '%s': %s", tmp_filter, Pcap::get_filter_state_string(DefaultPcapFilter)); if ( network_time() == 0.0 ) Reporter::fatal(error_string); else Reporter::warning(error_string); } else if ( state == Pcap::warning ) Reporter::warning(fmt("Warning while compiling pcap filter '%s': %s", tmp_filter, Pcap::get_filter_state_string(DefaultPcapFilter))); }

安装失败时同样使用状态字符串作为日志与 Notice 的原因(main.zeek#L347-L356)。由此可见,get_filter_state决定错误等级(fatal/warning),get_filter_state_string提供可读的排错细节,二者配合支撑了Compile_Failure、Install_Failure、Too_Long_To_Compile_Filter三类 Notice(main.zeek#L21-L30)。


六、获取错误信息:Pcap::error

function Pcap::error(): string

返回最后一次 PCAP 函数失败的描述性错误信息;若没有活动包源或没有错误,则返回"no error"。实现见 src/iosource/pcap/pcap.bif#L105-L116,直接返回包源的ErrorMsg()。该函数通常与下面第七节的地址/子网过滤函数配合使用,作为失败诊断的补充。


七、相关联的包过滤函数族

Pcap模块的函数在文档中多次以zeek:see::交叉引用一组全局过滤函数,它们定义在 src/zeek.bif 中,用于按概率丢弃特定方向的报文:

函数签名作用
install_src_addr_filter(ip: addr, tcp_flags: count, prob: double): bool安装按源地址丢弃报文的过滤器
install_src_net_filter(snet: subnet, tcp_flags: count, prob: double): bool安装按源子网丢弃报文的过滤器
uninstall_src_addr_filter(ip: addr): bool移除源地址过滤器
uninstall_src_net_filter(snet: subnet): bool移除源子网过滤器
install_dst_addr_filter(ip: addr, tcp_flags: count, prob: double): bool安装按目的地址丢弃报文的过滤器
install_dst_net_filter(snet: subnet, tcp_flags: count, prob: double): bool安装按目的子网丢弃报文的过滤器
uninstall_dst_addr_filter(ip: addr): bool移除目的地址过滤器
uninstall_dst_net_filter(snet: subnet): bool移除目的子网过滤器

以install_src_addr_filter为例(src/zeek.bif#L5333-L5337),其实现委托给packet_mgr->GetPacketFilter()->AddSrc(...),tcp_flags表示"若这些 TCP 标志位均未设置,则以prob概率丢弃来自该地址/子网的报文",prob取值区间为[0.0, 1.0]。这类过滤器用于丢包采样与缓解而非 BPF 语法过滤,与Pcap::precompile_pcap_filter的机制互补。


八、枚举抓包设备:Pcap::findalldevs

function Pcap::findalldevs(): Pcap::Interfaces

返回本机全部可用抓包接口的集合,类型为Pcap::Interfaces,即set[Pcap::Interface]。实现(src/iosource/pcap/pcap.bif#L164-L216)直接调用 libpcap 的pcap_findalldevs(),失败时通过emit_builtin_error报告错误并返回空集合。

返回的Pcap::Interface记录(scripts/base/init-bare.zeek#L5955-L5971)字段如下:

字段类型说明
namestring接口/设备名
descriptionstring &optional设备的人类可读描述
addrsset[addr]设备关联的网络地址
is_loopbackbool是否为回环接口(如127.0.0.1、[::1])
is_upbool &optional设备是否处于 up 状态(libpcap ≥ 1.6.1 可用)
is_runningbool &optional设备是否处于运行状态(libpcap ≥ 1.6.1 可用)

值得注意的实现细节:is_up与is_running字段被#ifdef PCAP_IF_UP保护(pcap.bif#L205-L209),因为这两个标志直到 libpcap 1.6.1 才出现——这体现了 Zeek 对旧版 libpcap 的兼容性处理。IPv4 与 IPv6 地址会分别从sockaddr_in/sockaddr_in6转换为 Zeek 的addr类型后以set[addr]返回。


九、深入实践:packet-filter框架中的动态过滤机制

9.1 框架如何扩展PcapFilterID

PcapFilterID初始只有None(init-bare.zeek#L1533-L1535),而 packet-filter/main.zeek#L150-L153 通过redef enum PcapFilterID += { DefaultPcapFilter, FilterTester }增加两个内置 ID:

  • DefaultPcapFilter:当前生效的抓包过滤器;
  • FilterTester:用于验证过滤器表达式合法性的临时 ID。

test_filter()函数(main.zeek#L155-L164)演示了FilterTester的用途:先用Pcap::precompile_pcap_filter(FilterTester, filter)验证表达式,失败即返回F。Zeek 启动时用它对capture_filters与restrict_filters中的每个过滤器做预验证,非法表达式直接触发Reporter::fatal(main.zeek#L180-L192)。

9.2 过滤器的组装优先级

框架的build()函数(main.zeek#L242-L285)揭示了 Zeek 中各类过滤器的最终组合逻辑:

  1. 命令行过滤器优先:若通过-f等命令行参数指定了cmd_line_bpf_filter,直接采用,覆盖脚本定义的所有过滤器;
  2. 否则按capture_filters表以or合并捕获过滤器;未定义时使用default_capture_filter(默认"ip or not ip",即捕获所有流量);
  3. 若启用enable_auto_protocol_capture_filters,再与Analyzer::get_bpf()按协议自动生成的过滤器做or合并;
  4. 所有restrict_filters与动态dynamic_restrict_filters(即exclude添加的"排除"过滤器,自动包上not (...))以and合并为限制过滤器;
  5. 最后将捕获过滤器与限制过滤器以and组合,并叠加unrestricted_filter(or)与restricted_filter(and)两个全局常量(默认均为空串)。

9.3 动态过滤的完整生命周期

脚本可通过exclude(filter_id, filter)、exclude_for(filter_id, filter, span)、remove_exclude(filter_id)动态增删排除过滤器(main.zeek#L206-L240)。每次变更都会触发install()重新构建并安装过滤器:

  • install()先比较新过滤器与current_filter,未变化则直接返回(避免无谓重装,见 main.zeek#L294-L296);
  • 接着调用Pcap::precompile_pcap_filter(DefaultPcapFilter, tmp_filter);
  • 预编译通过后,调用Pcap::install_pcap_filter(DefaultPcapFilter)真正生效;
  • 若编译耗时超过max_filter_compile_time(默认100msec),触发Too_Long_To_Compile_FilterNotice(main.zeek#L327-L330);
  • 每次安装尝试都会写入packet_filter日志流(Info记录含ts、node、filter、init、success、failure_reason等字段,见 main.zeek#L34-L54),便于审计与排障。

此外,filter_change_tracking事件每 5 分钟检查一次filter_changed标志(main.zeek#L168-L174),确保兼容"先注册变更、后统一安装"的插件式过滤机制。

9.4 更多测试用例佐证

仓库中另有两条针对性测试: testing/btest/core/pcap/filter-error.zeek 与 testing/btest/core/pcap/input-error.zeek 覆盖了非法过滤器、读取输入失败等错误路径;testing/btest/opt/ZAM-bif-tracking.zeek 则验证了Pcap模块函数在 ZAM 优化下的行为。另外 src/script_opt/FuncInfo.cc#L133-L134 将Pcap::get_filter_state与Pcap::get_filter_state_string标记为ATTR_NO_ZEEK_SIDE_EFFECTS(无 Zeek 副作用),意味着编译器可以对它们进行更积极的优化——这是从源码结构可以推断的实现细节。


十、总结与最佳实践

围绕 pcap.bif.zeek.rst 文档所定义的函数族,Zeek 的Pcap模块提供了一套完整、包源无关的动态过滤能力:

  1. 两阶段过滤:precompile_pcap_filter负责语法校验与编译(注意 ID 必须小于 100),install_pcap_filter负责生效,二者分离使运行时切换过滤器成为可能;
  2. 状态可观测:get_filter_state给出ok/fatal/warning三态判定,get_filter_state_string给出可读错误文本,配合Pcap::error可快速定位"编译失败"还是"安装失败";
  3. 框架级集成:packet-filter框架将上述函数封装为default_capture_filter、restrict_filters、capture_filters、exclude等高层配置,普通用户通常无需直接调用Pcap函数,但理解其原理有助于排查packet_filter日志与相关 Notice;
  4. 设备发现:findalldevs在需要枚举本机网卡的脚本场景(如自动选择抓包接口)中可直接使用。

实践建议:当你在脚本中自定义动态过滤器时,优先复用packet-filter框架的exclude/exclude_for接口;只有需要精细控制多个独立过滤器的编译与安装时机时,才直接使用Pcap::precompile_pcap_filter与Pcap::install_pcap_filter,并记得为每个过滤器分配独立的PcapFilterID枚举值,同时检查返回值与get_filter_state_string的输出。

  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载
上一篇:3分钟实现完全离线OCR:天若OCR本地版让你的文字识别更安全高效
下一篇:3分钟构建企业级HTTP文件服务器:chfsgui图形化文件共享工具深度解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 8:46:38

相机与PC通讯的TCP协议设计与C++实现:从心跳到断线重连

简介&#xff1a;一份面向工业自动化、图像处理及网络编程初学者的相机与PC通讯示例程序包&#xff0c;基于C#实现TCP/IP通信&#xff0c;演示了建立连接、发送与接收图像数据的完整流程&#xff0c;并体现了“无协议通讯”的简化思路&#xff0c;即利用TCP可靠传输直接交换数据…

作者头像 李华
网站建设 2026/9/28 8:45:47

Javaweb项目完整案例:图书管理系统大二期末大作业实战指南

简介&#xff1a;这是一份面向Java Web初学者与在校学生的图书管理系统实战项目&#xff0c;源自大二上学期期末大作业&#xff0c;适合作为SSM/MyBatis入门练手与课程设计参考。项目围绕书籍、学生、管理员、留言四类实体展开&#xff0c;涵盖借还书日志记录、书籍状态流转&am…

作者头像 李华
网站建设 2026/9/28 8:45:38

数据驱动微电网鲁棒优化调度:Python实战与避坑指南

简介&#xff1a;这份资源是面向微电网优化调度方向的研究生、科研人员与电力工程师的MATLAB项目包&#xff0c;聚焦数据驱动与鲁棒优化相结合的调度策略&#xff0c;可用于课题复现、算法对比与论文实验验证。压缩包共5个文件&#xff0c;约4.68MB&#xff0c;其中3个m文件承担…

作者头像 李华
网站建设 2026/9/28 8:44:50

多卡GPU训练为何达不到线性加速?通信与计算重叠是关键

1. 为什么“两张GPU两倍速度”是个危险的幻觉刚入行做大模型训练的朋友&#xff0c;常会盯着服务器机柜里那两块亮闪闪的RTX 4090或A100&#xff0c;心里盘算&#xff1a;“既然单卡跑完一个epoch要8小时&#xff0c;双卡不就只要4小时&#xff1f;省一半时间&#xff0c;稳赚不…

作者头像 李华
网站建设 2026/9/28 8:44:29

Java+SSM+MySQL+微信小程序答题系统源码部署与联调实战

简介&#xff1a;本资源是一套基于Java、SSM框架、MySQL数据库与微信小程序开发的答题小程序完整项目包&#xff0c;面向计算机相关专业学生、毕业设计选题者及课程设计需求者&#xff0c;可直接用于毕设、期末大作业或在线答题场景的二次开发。压缩包共1095个文件&#xff0c;…

作者头像 李华