# netmaster **Repository Path**: openkylin/netmaster ## Basic Information - **Project Name**: netmaster - **Description**: No description available - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-10-16 - **Last Updated**: 2026-10-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # netmaster [![license: GPL-2.0 OR BSD-3-Clause](https://img.shields.io/badge/license-GPL--2.0%20OR%20BSD--3--Clause-blue.svg)](LICENSE) [![kernels: BTF | 4.19+ COMPAT](https://img.shields.io/badge/kernels-BTF%20%7C%204.19%2B%20COMPAT-informational.svg)](#环境要求) [English](README.md) | 中文 netmaster 是一个 eBPF 数据包生命周期追踪器。它把单个 skb 经过的 钩子事件(驱动、netfilter、TCP/UDP、qdisc)缝合成按包的一条链, 标注丢包原因、调用栈与时延。在故障主机上用 `--save` 采集,之后用 `--load` 离线回放,不挂探针、无内核副作用。 ## 输出 ![netmaster 回放输出](assets/trace-demo.svg) 一次回放运行(`--load fixture.bin --mode=chain`)。下文记录压缩到 本页宽度;实际运行时分隔线随终端宽度调整。左列是距第一个钩子的 时间,`*...*` 携带每个节点的上下文,行尾 `+ ...` 是旁注。完整输出 位于 `tests/golden/expected/`。 接收路径,以丢包判定收尾: ```text [1725000001.485249] [host] SKB:0xffff8de07cadf700,CPU:6,len=576 | UDP: 0.0.0.0:68->255.255.255.255:67 ------------------------------------------------------------------------------ [0.000000] napi_gro_receive_entry | [0.000001] dev_gro_receive | [0.000012] __netif_receive_skb_core | [0.000001] netif_receive_skb | [0.000002] ip_rcv_core | [0.000002] nf_hook_slow | *ipv4 in chain: PRE_ROUTING* [0.000002] ip_route_input_slow | [0.000002] ip_local_deliver | [0.000001] nf_hook_slow | *ipv4 in chain: INPUT* [0.000001] ip_local_deliver_finish | [0.000001] udp_rcv | [0.000002] kfree_skb | + drop at kfree_skb: reason 0, location 0xffffffffb45a9da4 ``` 发送路径,出口关联到对应 socket: ```text [1725000002.045338] [host] SKB:0xffff8de1efa9d700,CPU:3,pid=823,len=59 | UDP: 192.168.0.107:55330->192.168.0.1:53 ------------------------------------------------------------------------------ [0.000000] ip_make_skb | [0.000015] nf_hook_slow | *ipv4 in chain: OUTPUT* [0.000004] nft_do_chain | [0.000006] ip_output | [0.000004] nf_hook_slow | *ipv4 in chain: POST_ROUTING* [0.000004] ip_finish_output | [0.000005] ip_finish_output2 | [0.000006] __dev_queue_xmit | [0.000006] sch_direct_xmit | *queue state: 0, flags: 54* [0.000006] dev_hard_start_xmit | [0.000057] cpu6 consume_skb | + sock 0xffff8de0f94a76c0: v4 55330->53 ``` 丢包原因、netfilter 链、队列状态、克隆、cork 等待、报文头改写、 ICMP 错误可达性判定、TCP 状态与 SACK 块,都会作为进一步的标注出现。 一次实时 `--save` 采集会把终端变成 top 式的汇总页。窗口每 `--save-interval` 秒(默认 5)滚动一次:累计记录数与丢包数、时延 趋势、系统与 BPF 负载、ksoftirqd 调度延迟、窗口内时延最高的记录, 以及最近丢包及其丢包位置。 ![netmaster --save 实时汇总页](assets/summary-demo.svg) 一次 `--save` 运行(`sudo netmaster --func skbtrace -p tcp --save out.bin`),终端宽 136 列。EXTRA 列承载排队等待与丢包位置;超长行 以 `..` 结尾。退出时,会话报告打印在最终帧下方。 一次采集还会把同一批记录汇总成会话报告(`--load out.bin --report`);`--queue-top` 追加每流的时延排名: ```text =============== session report =============== node: host records: 222 drops: 160 -- drop reasons -- 160 reason 0 NOT_SPECIFIED -- latency -- max=2.2s avg=635943us p99~2.1s p50~2.0ms ``` ## 与其他工具对比 这些工具有所重叠,但回答的问题不同: | 问题 | netmaster | tcpdump / Wireshark | bpftrace / perf | ftrace | | --- | --- | --- | --- | --- | | 这个包在内核里经历了什么? | 它经过的钩子,按包一条链 | 只有线上的字节 | 你自行编写的孤立探针事件 | 函数耗时,无法与具体数据包关联 | | 为什么被丢弃? | 丢包点的原因、位置与调用栈 | 从缺失的数据包反推 | 自己配对探针 | 很难关联回单个数据包 | | 回放与分享 | `--save` / `--load` 可在任意主机回放;`--to-pcap` 交给 Wireshark | pcap 文件 | — | — | | 两个节点,一条时间线 | `--xmatch` 对齐两份采集 | 手工对齐时间戳 | — | — | | 生产环境的成本控制 | 有界表;内核侧 `--sample-flow` / `--latency-over` | 全量抓包 | 每个脚本各自的额外开销 | 全局开销 | ## 功能 - **按包调用链(`skbtrace`)** —— 把单个 skb 的 kprobe/fentry/ tracepoint 节点缝合成一条生命周期链,标注 netfilter/iptables/qdisc/conntrack 上下文、跨 CPU 延迟释放、克隆 关联、TCP cork 等待、报文头改写、ICMP 错误判定(PMTU/不可达/ 超时)、VXLAN/IPv6 分片、TCP 状态快照与 SACK 块(真实/伪重传 划分)。 - **丢包监控(`skbdrop`)** —— 挂载 `kfree_skb` 探测点系列,报告 任意协议的丢包,附丢包原因、原始报文头与调用栈。 - **时延统计(`rtt` / `irq_delay`)** —— TCP RTT 直方图与 softirq/hardirq 接收时延分解。 - **诊断面板(`netdiag`)** —— 聚合 TUI 视图;一切正常时无事件 开销。 - **离线回放与分析(`--save` / `--load`)** —— 采集时写出压缩、带 版本号的文件,并离线重建;按墙钟顺序合并多个文件、跨节点对齐 (`--xmatch`)、会话报告(`--report`),以及 `--to-pcap` 导出 (仅报头帧,供 Wireshark 读取)。 - **实时汇总页(`--save`)** —— 采集期间的 top 式页面:累计记录数 与丢包数、时延趋势、系统与 BPF 负载、窗口内时延最高的记录, 以及最近丢包。 - **黑匣子** —— `--stop-on` 在首个错误/丢包/超阈值时延处停止并 保留现场;`--blackbox`(仅 BTF 构建)在每个 CPU 的有界内核环中 保留最近 N 条链,收到 SIGUSR1 时转储。 - **生产控制** —— `--sample-flow`(内核侧流采样)、 `--latency-over`(内核侧时延预筛)、`--save-rotate` / `--save-keep`(长时间运行的有界采集)。 - **JSON 输出(`--format=json`)** —— NDJSON,每行一个链对象, 供程序消费。 ## 环境要求 | 项目 | 要求 | | --- | --- | | 内核 | 原生 ≥ 5.8(BTF/CO-RE、fentry/fexit、ringbuf);4.19 系列使用 COMPAT 构建(kprobe + perfbuf) | | 架构 | x86、arm、arm64、riscv、loongarch(CO-RE 头文件随 `vmlinux//` 提供) | | 权限 | root:netmaster 在启动时检查 uid 0;此版本中仅授予 capability 并不足够 | | BTF | 建议 `CONFIG_DEBUG_INFO_BTF=y`(存在 `/sys/kernel/btf/vmlinux`);无 BTF 时使用 COMPAT 构建 | | 内核配置 | `CONFIG_BPF_SYSCALL`、`CONFIG_KPROBES`、`CONFIG_FUNCTION_TRACER`、`CONFIG_NET` | `vmlinux//vmlinux.h` 是 bpftool 生成的 CO-RE 头文件 (`bpftool btf dump file /sys/kernel/btf/vmlinux`),取自各 COMPAT 构建的目标内核(x86 6.1、arm 6.2、arm64 5.16/6.1、riscv/loongarch 6.2),作为构建输入提交到仓库。作为内核类型信息的 衍生物,适用双许可证中的 GPL-2.0 选项。 ## 快速开始 ### 构建 ```bash # 依赖(openEuler/CentOS/Fedora) # ncurses(-devel) 仅用于 netdiag TUI;-DWITH_TUI=OFF 可去掉 yum install cmake clang llvm elfutils-libelf-devel zlib-devel \ ncurses-devel gcc make # 依赖(Debian/Ubuntu) apt install cmake clang llvm libelf-dev zlib1g-dev libncurses-dev gcc make # 首次克隆后初始化 libbpf/bpftool 子模块 #(v1.4.0 / v7.4.0;直接 "git submodule update --init" 也可以, # 但重跑本脚本可重建 bpftool/libbpf 符号链接) ./tools/init_third_party.sh mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. # 首次构建也会一并编译 libbpf/bpftool 子模块 cmake --build . -- -j"$(nproc)" ``` ### 首次追踪 ```bash # 症状预设把常用开关打包好(见下文) # 为什么慢 sudo ./src/netmaster --func skbtrace -p tcp --profile latency # 是不是在丢包 sudo ./src/netmaster --func skbtrace -p tcp --profile drop # 重传 / 乱序 sudo ./src/netmaster --func skbtrace -p tcp --profile retrans # 应用读取是否太晚 sudo ./src/netmaster --func skbtrace -p tcp --profile app # 每个预设都会打印等价的完整命令行,之后可微调各个开关; # cookbook 见 doc/netmaster.1 # 追踪一条 TCP 收发链(在另一个终端运行 curl 或 ping) sudo ./src/netmaster --func skbtrace -p tcp -d eth0 --mode=chain # 列出支持的子命令 sudo ./src/netmaster --show ``` Ctrl+C(SIGINT/SIGTERM)会自动清理 BPF 对象和遗留的旧式 kprobe 条目。 ## 症状预设 `--profile` 打包一个场景所需的选项,启动时打印等价的完整命令行; 可像其他运行一样配合常规过滤器(`-p`、`-d`、`--dport`)使用: | 预设 | 问题 | 打包内容 | | --- | --- | --- | | `--profile drop` | 是不是在丢包? | 丢包原因、调用栈、诊断规则解释(`--explain`) | | `--profile latency` | 为什么慢? | 带 TCP 状态快照的链、零窗口与读端归因、内核侧时延预筛 | | `--profile retrans` | 重传 / 乱序? | 重传计数与 SACK 真伪拆分 | | `--profile app` | 应用读取是否太晚? | 读端归因、零窗口证据、TCP 状态 | | `--profile blackbox` | 罕见错误丢失现场 | `--stop-on=error`:遇到首个错误即停止追踪 | | `--profile full` | 广谱采集 | 丢包解释、重传/SACK、TCP 状态、零窗口与应用 hold-over | `--show-config` 只打印预设展开及推导出的配置,不进行追踪; `doc/netmaster.1` 里的症状 cookbook 把每个症状映射到对应预设与 后续开关。 ## COMPAT 构建(无 BTF 的内核) 适用于**没有 BTF** 的旧内核(如麒麟 V10 / 4.19 系列)。如果 `/sys/kernel/btf/vmlinux` 存在,请使用上面的默认构建: ```bash # COMPAT 模式(kprobe + perfbuf + 内核头文件;无 BTF) mkdir build && cd build cmake -DCOMPAT=1 -DBPF_NO_GLOBAL_DATA=1 -DCMAKE_BUILD_TYPE=Release .. cmake --build . -- -j"$(nproc)" ``` COMPAT 需要目标内核头文件(`/lib/modules/$(uname -r)/build`,可用 `-DKBUILD_DIR=` 覆盖);没有 BTF 时,头文件必须与所用的 clang 匹配 (上面的依赖列表已包含 clang)。`-DCOMPAT=1` 已默认打开 `BPF_NO_GLOBAL_DATA`,因此不带该参数的写法也能生成可加载的对象; 在支持 BTF 的内核上请求 COMPAT 时,构建会给出警告。 ### COMPAT 支持边界 `--capabilities` 打印构建提供的能力;做验收的 4.19 arm64 COMPAT 构建 输出如下: | 键 | 值 | 对 4.19 的影响 | | --- | --- | --- | | `event_mode` | 0 | 启动时拒绝 `--mode=event` | | `blackbox` | 0 | 启动时拒绝 `--blackbox FILE` | | `sack` | 0 | 启动时拒绝 `--sack` | | `ringbuf` | 0 | 采集走 perfbuf 后端 | | `icmp_quote` | 0 | 不解析 ICMP 差错引用(拿不到被引用的元组) | | `tunnel_unwrap` | 0 | 不剥离 VXLAN/GRE/IPIP | | `ipv6_parse` | 0 | IPv6 只解析到以太网头 | | `perfbuf_kb` | 256 | 每 CPU 的默认事件缓冲,见 `-s` | | `tcp_probe`、`retrans_tp` | 运行时探测 | 重传统计依赖这两个 tracepoint | 管线被拒绝的场景报告跳过(ctest 退出码 77),不再判失败: `scenario_blackbox`、`scenario_sack`、`accuracy_ACC_ICMP`。 perfbuf 默认每 CPU 256 KiB,它就是高负载下的瓶颈:在 4.19 arm64 构建 实测,100K pps 档约 31K records/s,每个 5 秒窗口丢 6K-19K 个事件, 50K 档处于临界(丢 0-475);`-s 5`(每 CPU 8 MiB)下该阶梯与多流压测 均实测零丢失,高速用例已显式传 `-s 5`。 ## 常用命令 ```bash # 全链路追踪 sudo netmaster --func skbtrace -p tcp --dport 443 --mode=chain # 黑匣子:遇到首个丢包即停止 sudo netmaster --func skbtrace -p tcp --stop-on=drop # 丢包监控(原始报文头) sudo netmaster --func skbdrop -H # RTT 直方图 sudo netmaster --func rtt -i 5 # 诊断面板(TUI) sudo netmaster --func netdiag -p tcp -i 1 # 一次性文本快照(cron/CI) sudo netmaster --func netdiag --once # 采集到磁盘 sudo netmaster --func skbtrace -p tcp --save out.bin # 离线回放(不挂探针) sudo netmaster --func skbtrace --load out.bin --report # 交给 Wireshark(仅报头帧) sudo netmaster --func skbtrace --load out.bin --to-pcap out.pcap ``` ## 构建选项 | 选项 | 说明 | | --- | --- | | `-DCMAKE_BUILD_TYPE=Release\|Debug` | Debug 会启用 ASan | | `-DCOMPAT=1` | 旧内核(4.19 系列)兼容模式:kprobe + perfbuf + 内核头文件;`-DKBUILD_DIR=` 选择内核构建目录 | | `-DSTATIC=1` | 链接时追加 `-lzstd --static`(面向旧环境的静态打包) | | `-DBPF_NO_GLOBAL_DATA=1` | 面向不支持 global data 段的旧内核/clang | | `-DWITH_TUI=ON\|OFF` | netdiag 的 ncurses TUI(默认 ON);OFF 时 netdiag 只提供文本快照,且不再需要 ncurses | | `-DTARGET_PKG_TYPE=rpm\|deb` | CPack 包类型,`make package` | | `-DLIBBPF_INCLUDE_DIRS=... -DLIBBPF_LIBRARIES=...` | 使用系统 libbpf 而非子模块 | `make distclean` 也会清除 libbpf/bpftool 子模块的构建产物 (普通 `make clean` 不会)。 ## 测试 所有测试层级都在 `tests/` 下(目录 = ctest 标签 = 层级);完整的 测试金字塔与各门禁钉住的契约见 `ARCHITECTURE.md`。每次 push/PR, CI 会运行 clang-format 门禁、Release 构建与 `ctest -LE scenario -E bpf_load_smoke`;需要 root 的层级只在本地运行。 ```bash # 全量:单元 + 契约 + golden + fuzz 冒烟 + BPF 冒烟 + 场景 + accuracy ctest # 快速底线:单元/契约/golden/fuzz,不含 root 场景 ctest -LE scenario # CLI 组合契约(root) ctest -R cli_matrix # 仅 BPF 加载冒烟测试(需要 root) make bpf-smoke ``` 分层: - **单元**(`tests/unit/`)—— 28 个 `test_*` 二进制,覆盖存储、帧化、 分发与诊断模块,包括容量/淘汰与所有权契约。 - **契约**(`tests/contract/`)—— `cli_matrix`(参数组合与退出码 契约)、`layering_check`(依赖方向规则)、`judge_sync`(判定族与 代码集)、`corpus_regression`(实时采集回归,红/绿双向断言)。 - **Golden**(`tests/golden/`)—— 字节级精确的回放/JSON 基线,外加 一个 20 万条记录的回放体量用例。 - **Fuzz**(`tests/fuzz/`)—— `fuzz_smoke`:针对回放解析器的 libFuzzer + ASan + UBSan,使用随仓库提交的种子语料(长时间会话: `tests/fuzz/run.sh`)。 - **场景**(`tests/scenario/`)—— 60+ 个用例跨网络命名空间注入真实 流量,验证端到端行为;见 [tests/scenario/README.md](tests/scenario/README.md)。 - **Accuracy**(`tests/accuracy/`)—— 注入已知参数故障的闭环数值 断言(`-L accuracy`,需要 root + netns)。 ## 文档 - [`doc/netmaster.1`](doc/netmaster.1) —— 完整选项参考与症状 cookbook(`man ./doc/netmaster.1`)。 - [ARCHITECTURE.md](ARCHITECTURE.md) —— 模块图、数据流、所有权 契约与测试金字塔。 - [CHANGELOG.md](CHANGELOG.md) —— 版本历史。 - [tests/scenario/README.md](tests/scenario/README.md) —— 场景 工具包:拓扑、判定、如何新增用例。 - [tests/golden/README.md](tests/golden/README.md) —— 字节级回放基线与更新流程。 ## 已知限制 - 除 `kfree_skb`/重传 tracepoint 外,部分 tracepoint 的字段偏移 面向主流内核在编译期固定;字段顺序被调整的冷门内核版本可能读取 错误(`kfree_skb` 的读取器在运行时自适应)。 - ringbuf 模式下 libbpf 不提供丢事件回调;丢事件计数只在 perfbuf(<5.8)路径上可见。 - 完整的修复与注意事项历史见 [CHANGELOG.md](CHANGELOG.md)。 ## 常见问题 **问:需要 root 吗?** 答:需要。netmaster 在启动时检查 uid 0(此版本中仅授予 capability 并不足够);离线 `--load` 回放会跳过探针,但不跳过启动检查。 **问:与抓包工具(tcpdump/Wireshark)或 bpftrace 一行命令有何不同?** 答:抓包展示线路上有什么;netmaster 展示内核对每个包做了什么: 经过的钩子、排队时间、改写点与丢包判定,按包保存为可回放的产物。 bpftrace 一行命令打印钩子事件;netmaster 把它们关联成每个 skb 一条 链。`--to-pcap` 可以把回放转换成给 Wireshark 的仅报头帧。 **问:加载时报 BTF 相关错误?** 答:内核没有 BTF;使用 COMPAT 构建: `cmake -DCOMPAT=1 -DBPF_NO_GLOBAL_DATA=1 ..`。 **问:4.19(或其他 4.x)内核带 BTF——可以用默认构建吗?** 答:只能部分使用:skbdrop、rtt 与 netdiag 可以加载并工作,但 skbtrace 与 irq_delay 会失败:4.x 校验器把调用链栈计入 512 字节 总量,它们的链在默认构建的代码生成下超过了该值。4.x 内核无论是否 带 BTF 都请使用 COMPAT 构建;在该环境加载失败时,工具会打印相应 提示。 **问:`--func` 接受哪些值?** 答:`netmaster --show`;当前有 `skbtrace`、`skbdrop`、`rtt`、 `irq_delay`、`netdiag`。 **问:退出码?** 答:`0` 成功(包括 `--help/--version/--show`),`1` 运行时失败, `2` 用法错误。 ## 参与贡献 欢迎 issue 和 PR。开发环境搭建、测试先行的工作流以及提交前必须 通过的内容见 [CONTRIBUTING.md](CONTRIBUTING.md)(底线是 `ctest -LE scenario`)。安全漏洞请勿提交公开 issue,请见 [SECURITY.md](SECURITY.md)。 ## 许可证 双许可证:GPL-2.0 OR BSD-3-Clause,见 [LICENSE](LICENSE)。 `third_party/` 下的第三方库(libbpf、bpftool)以 git 子模块引入, 保留各自原有许可证。 *本文是 [README.md](README.md) 的中文翻译;如与英文版有出入, 以英文版为准。*