☰
Zeek SumStats 框架 MAX 插件源码与实战解析:利用 SumStats::MAX 追踪观测流最大值
2026/10/8 1:36:30 网站建设 项目流程
  • 网络安全
  • 网络
  • 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 网络分析框架中 SumStats 摘要统计框架的MAX计算插件(位于 scripts/base/frameworks/sumstats/plugins/max.zeek)。文章将以该插件的接口定义、底层实现与官方测试用例为主线,说明如何通过SumStats::MAX在自定义观测流上持续追踪最大值,并讲解其与 SumStats 框架核心调度、结果合并机制之间的协作方式。读完本文,你将掌握MAX插件的注册原理、max结果字段的语义,以及如何在真实 Zeek 脚本中组合使用它。

1. 插件定位:SumStats 框架中的最大值计算器

SumStats(summary statistics)是 Zeek 内置的统计框架,用于把大规模数据流压缩为简单的摘要测量值。它本身并不内置任何具体算法,而是把每种统计量都实现为可插拔的“计算插件”(plugin),MAX就是其中之一,其功能正如文档所述:“Find the maximum value.”(求最大值)。

在插件目录 scripts/base/frameworks/sumstats/plugins/ 下,与max.zeek并列的还有average.zeek、min.zeek、sum.zeek、std-dev.zeek、variance.zeek、unique.zeek、hll_unique.zeek、last.zeek、sample.zeek、topk.zeek等,它们共同通过 plugins/load.zeek 中的@load指令批量装载。当你在脚本中@load base/frameworks/sumstats时,MAX插件即随框架一起就绪。

插件所属命名空间为SumStats,其文档位于 doc/scripts/base/frameworks/sumstats/plugins/max.zeek.rst。从文档的:Imports:一节可以看到它依赖框架核心 scripts/base/frameworks/sumstats/main.zeek,也就是说它只负责在核心框架之上“追加”一种计算能力,而不重复实现框架逻辑。

2. 接口定义:向框架注册枚举值与结果字段

MAX插件通过 Zeek 脚本的redef机制向 SumStats 框架的两个基础类型追加内容,这一部分对应文档 “Summary / Redefinitions” 一节:

  • 向枚举类型SumStats::Calculation追加新成员SumStats::MAX,用于在 Reducer 的$apply集合中声明“本 reducer 需要计算最大值”;
  • 向记录类型SumStats::ResultVal追加可选字段max: double &optional,用于存放计算结果——“For numeric data, this tracks the maximum value.”(对数值型数据,该字段记录最大值)。

对应的源码如下:

redef enum Calculation += { ## Find the maximum value. MAX }; redef record ResultVal += { ## For numeric data, this tracks the maximum value. max: double &optional; };

注意max字段被声明为&optional:当某个 reducer 未申请MAX计算时,结果中不会出现该字段,脚本侧访问前需用result?$max判断。从 main.zeek 可见,ResultVal基类自身只有三个字段——begin(首个观测进入时间)、end(最后一个观测进入时间)、num(观测计数),其余统计字段全部由各插件通过redef record ResultVal +=追加,这正是框架“插件化”设计的体现。

3. 实现原理:观察期更新与结果合并两段逻辑

MAX插件的核心实现在max.zeek中由两个 hook 组成,分别对应两种数据流场景。

3.1 单点观测:register_observe_plugins 中的增量更新

hook register_observe_plugins() { register_observe_plugin(MAX, function(r: Reducer, val: double, obs: Observation, rv: ResultVal) { if ( ! rv?$max ) rv$max = val; else if ( val > rv$max ) rv$max = val; }); }

这个 hook 在框架初始化时(main.zeek 中的event zeek_init() &priority=100000)被触发,框架遍历所有插件调用各自的register_observe_plugins,并把计算函数存入calc_store表。之后每次脚本调用SumStats::observe(id, key, obs)时,框架 observe() 会为每个挂接在对应 stream 上的 reducer 取出其calc_funcs列表,逐一执行对应的计算函数。

MAX的计算逻辑非常直观:若结果值尚未初始化(! rv?$max),直接把当前观测值赋给rv$max;否则仅当新观测值更大时才更新。因此它维护的是一个“单调不减”的最大值,符合求最大值语义。

关于val的来源有一个值得注意的细节:在 observe() 中,如果观测值obs提供的是$num(count)或$dbl(double),则val取对应数值;如果提供的是$str字符串,则回退为1.0。也就是说,MAX插件只对数值型观测有意义(文档中的max: double字段也印证了这一点),字符串观测会被当作数值 1.0 参与比较。

3.2 跨结果合并:compose_resultvals_hook

SumStats 结果不仅会在单个 reducer 内随观测增量更新,还会在 epoch 结束或集群场景中把两个ResultVal合并成一个(例如单机模式 epoch 结束时、集群中节点间结果汇总时)。框架的 compose_resultvals() 负责合并begin、end、num等公共字段,随后通过compose_resultvals_hook让各插件补上自己的合并逻辑:

hook compose_resultvals_hook(result: ResultVal, rv1: ResultVal, rv2: ResultVal) { if ( rv1?$max && rv2?$max ) result$max = (rv1$max > rv2$max) ? rv1$max : rv2$max; else if ( rv1?$max ) result$max = rv1$max; else if ( rv2?$max ) result$max = rv2$max; }

其语义为:两个结果都有max时取较大者;只有一个有max时直接继承;两者都没有则结果中不设置max字段。这保证了多 epoch 汇总或集群合并后的最大值仍然正确,不会因合并操作被高估或低估。

4. 实战用法:在 Reducer 中启用 MAX 并读取结果

MAX的使用方式与 SumStats 其他插件完全一致:在构建SumStats::Reducer时把SumStats::MAX加入$apply集合,框架即自动维护结果字段,用户只需在回调中读取result["<stream>"]["$max"]。

官方测试 testing/btest/scripts/base/frameworks/sumstats/basic.zeek 展示了标准用法:

@load base/frameworks/sumstats redef exit_only_after_terminate=T; event zeek_init() &priority=5 { local r1: SumStats::Reducer = [$stream="test.metric", $apply=set(SumStats::SUM, SumStats::VARIANCE, SumStats::AVERAGE, SumStats::MAX, SumStats::MIN, SumStats::STD_DEV, SumStats::UNIQUE, SumStats::HLL_UNIQUE)]; SumStats::create([$name="test", $epoch=3secs, $reducers=set(r1), $epoch_result(ts: time, key: SumStats::Key, result: SumStats::Result) = { local r = result["test.metric"]; print fmt("Host: %s - ... - max:%.1f - ...", key$host, ..., r$max, ...); terminate(); }]); SumStats::observe("test.metric", [$host=1.2.3.4], [$num=5]); SumStats::observe("test.metric", [$host=1.2.3.4], [$num=22]); SumStats::observe("test.metric", [$host=1.2.3.4], [$num=94]); SumStats::observe("test.metric", [$host=1.2.3.4], [$num=50]); SumStats::observe("test.metric", [$host=1.2.3.4], [$num=50]); ... }

对该测试中的1.2.3.4主机而言,依次喂入5、22、94、50、50五个观测,r$max最终应为94.0。可以推断,若将相同逻辑推广到网络场景,一个典型应用是把连接持续时间、请求字节数、响应延迟等指标写入观测流,再用MAX追踪某段时间内每个主机(Key中的$host)或每个逻辑分组(Key中的$str)的峰值。

关于回调的执行时机,main.zeek 中SumStats::SumStat记录给出了完整上下文:$epoch决定聚合周期,周期结束触发epoch_result(每个 key 调用一次)与epoch_finished;$threshold/$threshold_series配合$threshold_val可在MAX值越过阈值时触发threshold_crossed。例如把threshold_val写成返回result["test.metric"]$max,即可实现“某 key 的峰值超过 N 时告警”。若将$epoch设为0 secs,则进入手动 epoch 模式,需通过SumStats::next_epoch()主动结束周期。

5. 工程细节与注意事项

结合 main.zeek 的框架实现,使用MAX时还需注意以下几点:

  • 结果字段可选性:max是&optional字段,仅当 reducer 的$apply中包含SumStats::MAX时才存在。读取前建议用result?$max做存在性判断,或使用result$max?的取值安全模式,避免脚本因字段缺失报错。
  • 数值类型语义:max: double意味着 count 类型的观测也会被自动转换为 double 后比较;字符串观测会被框架回退为数值1.0,因此字符串流上的MAX结果恒为1.0,没有实际统计意义,应避免混用。
  • 集群一致性:MAX的合并逻辑(取两值较大者)保证在集群环境中,无论结果来自哪个 worker,汇总后的最大值都与“全局最大值”一致。合并公共字段(begin、end、num)由框架的compose_resultvals()统一完成,插件只负责自身字段。
  • 依赖与装载:MAX无额外计算依赖(对比variance等可能需要依赖其他计算的插件),且已包含在 plugins/load.zeek 的默认装载清单中,正常@load base/frameworks/sumstats即可使用,无需单独@load。
  • 阈值联动:若使用MAX结果做阈值检测,需把阈值设为本 epoch 内预期的峰值量级,因为 epoch 结束后reset()会清空结果与阈值跟踪状态(main.zeek),新一轮统计从零开始。

6. 小结

SumStats::MAX是 Zeek SumStats 框架中最简单也最常用的计算插件之一:它通过redef enum Calculation += { MAX }注册计算类型、通过redef record ResultVal += { max: double &optional }提供结果字段,并在register_observe_plugins与compose_resultvals_hook两个 hook 中分别实现观测期增量更新与结果合并语义。无论是单机脚本内的峰值追踪,还是集群环境下的跨节点汇总,MAX都能以框架统一的方式工作,是构建基于峰值的告警、基线画像等检测逻辑的可靠基础组件。更多框架级接口(create、observe、threshold_crossed、next_epoch等)可进一步查阅 scripts/base/frameworks/sumstats/main.zeek,配套测试可参考 testing/btest/scripts/base/frameworks/sumstats/ 目录下的basic.zeek、thresholding.zeek、manual-epoch.zeek等用例。

  • 网络安全
  • 网络
  • 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
点击查看免费下载
上一篇:dlssg-to-fsr3高级功能:HDR支持、运动向量处理与调试工具详解
下一篇:pyre2源码解析:_re2.cc C++扩展如何桥接RE2与CPython API

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询