网络包迷踪:用 eBPF 工具 pwru 追踪 Linux 内核网络包
网络包迷踪:用 eBPF 工具 pwru 追踪 Linux 内核网络包
引言:当 tcpdump 不够用的时候
想象一下这个场景:你通过 tcpdump 确认数据包已经到达了网卡,但应用就是收不到;你怀疑是 iptables 规则或内核网络栈的某个环节把包丢了,却不知道具体丢在了哪里。Linux 内核网络栈的代码路径极其复杂,从网卡驱动、Netfilter 钩子、路由表、到 TCP/IP 协议栈,中间有数十上百个函数调用。传统工具很难精确定位问题出在哪个环节。
这时候,你就需要一个能"看见"数据包在内核中每一个足迹的工具——pwru。
pwru 是 Packet, Where Are You? 的缩写,顾名思义,它的使命就是帮你回答"我的网络包到底去哪儿了?"这个问题。
pwru 是什么?
pwru 是一个基于 eBPF(Extended Berkeley Packet Filter)技术的 Linux 内核网络调试工具。它能够追踪网络包在 Linux 内核网络栈中的完整路径,并提供强大的过滤能力,让开发者和管理员能够精细地观察内核状态,从而高效诊断网络连通性问题。
pwru 由 Cilium 团队开发,在 FOSDEM 2024 等技术大会上都有过专题分享。最令人惊叹的是,它被打包成一个小于 10MB 的静态链接二进制文件,没有任何外部依赖——下载即用,部署极其简单。
工作原理:eBPF 探针如何追踪网络包?
pwru 的核心工作原理其实很优雅:
-
利用 BTF 信息:pwru 依赖内核的 BTF(BPF Type Format)信息,自动识别内核网络栈中所有接收
sk_buff(socket buffer,即网络包的内核数据结构)作为参数的函数。 -
注入 eBPF 探针:pwru 通过 kprobe(内核动态探针)或 fentry(更高效的函数入口跟踪)将这些函数挂载上 eBPF 程序。
-
实时输出:当网络包经过这些函数时,eBPF 程序会记录信息并上报给用户空间,让你看到包在内核中的完整"旅行轨迹"。
pwru 支持多种后端:传统的 kprobe 和性能更好的 kprobe-multi(需要内核 >= 5.18)。
核心功能
pwru 之所以强大,离不开它丰富的功能矩阵:
1. 灵活的包过滤
pwru 支持标准的 pcap 过滤语法(就是 tcpdump 用的那一套),同时提供了大量专门的过滤选项:
- 按 IP 地址过滤(源/目的)
- 按端口过滤(源/目的)
- 按协议过滤(TCP、UDP、ICMP)
- 按网络命名空间(netns)过滤
- 按 skb mark 过滤
- 按内核函数名过滤(支持正则表达式)
多个过滤器可以组合使用,只有全部匹配的包才会被追踪。
2. 丰富的输出信息
pwru 可以输出多种维度的信息:
- L4 元组(五元组信息)
- 调用栈(call stack)——帮你看到包是被哪个函数处理的
- skb 元数据和完整内容
- 时间戳(支持相对时间、绝对时间等)
- JSON 格式输出,方便后续分析
3. 容器和云原生环境支持
pwru 天然支持容器环境,可以通过 netns 过滤追踪特定容器内的网络包,也可以与 Cilium 生态集成。
安装与快速上手
环境要求
pwru 需要 Linux 内核 >= 5.3。部分高级功能(如 --output-skb)需要 >= 5.9,kprobe-multi 后端需要 >= 5.18。
需要确保以下内核配置已启用:
CONFIG_DEBUG_INFO_BTF=yCONFIG_KPROBES=yCONFIG_BPF=yCONFIG_BPF_SYSCALL=y
可以用 zgrep CONFIG_XXX /proc/config.gz 来检查。
另外需要挂载 debugfs:
1 | mount -t debugfs none /sys/kernel/debug |
安装
直接从 GitHub Release 页面下载静态编译的可执行文件即可,支持 x86_64 和 arm64 架构:
1 | # 下载最新版本(示例) |
一个完整的调试示例
假设你想追踪一个访问 1.1.1.1:80 的 TCP 请求,看看包在内核中的完整路径:
1 | sudo ./pwru --filter-dst-ip=1.1.1.1 --filter-dst-port=80 --filter-proto=tcp --output-tuple |
然后在另一个终端执行 curl 1.1.1.1,你就能看到这个包经过的每一个内核函数。
接着添加一条 iptables 规则把包丢掉:
1 | sudo iptables -t filter -I OUTPUT 1 -m tcp --proto tcp --dst 1.1.1.1/32 -j DROP |
再执行一次 curl,pwru 会清楚地显示出包是在 nf_hook_slow 等 Netfilter 相关函数处被丢弃的。这就是 pwru 最典型的应用场景——快速定位丢包点。
高级用法
追踪特定内核函数
如果怀疑问题出在某个特定的内核函数上,可以用 --filter-func 精确追踪:
1 | sudo ./pwru --filter-func='nf_hook_slow|ip_rcv' --output-stack |
支持 RE2 正则表达式。
容器网络调试
在 Kubernetes 集群中调试特定 Pod 的网络问题:
1 | # 先获取 Pod 的 netns inode |
追踪被释放的包
有些场景下,包在被 kfree(释放)之后你可能还想继续追踪(比如桥接流量),可以用 --filter-track-skb-by-stackid。
JSON 输出
将追踪结果输出为 JSON 格式,方便导入到其他分析工具:
1 | sudo ./pwru --output-json --output-limit-lines=1000 > trace.json |
适用场景
pwru 特别适合以下场景:
-
网络丢包问题:包到了网卡但应用收不到,快速定位是在 Netfilter、路由、还是协议栈哪一层丢了。
-
iptables/nftables 规则调试:验证规则是否按预期生效,包到底匹配了哪条规则。
-
容器网络问题:在复杂的容器网络环境下(如 Calico、Cilium 等 CNI),追踪包在宿主机内核和容器 netns 之间的流转。
-
内核网络开发:理解网络包在内核中的完整处理流程,学习或调试内核网络模块。
-
性能问题排查:配合
--output-stack观察包处理的热点路径。
与同类工具的对比
| 工具 | 定位 | 优势 | 局限 |
|---|---|---|---|
| tcpdump | 抓包分析 | 通用、轻量 | 只能看到进出网卡的包,看不到内核内部处理 |
| bpftrace | 动态追踪 | 灵活强大 | 需要编写脚本,学习曲线陡峭 |
| pwru | 内核网络包追踪 | 开箱即用、自动探测所有网络函数、过滤强大 | 专注于网络包追踪 |
pwru 填补了 tcpdump 和 bpftrace 之间的空白——它比 tcpdump 看得更深(能进内核),又比 bpftrace 更易用(无需写脚本)。
总结
pwru 是一个强大而优雅的 Linux 网络调试工具。它利用 eBPF 技术,以极低的部署成本(一个 <10MB 的静态二进制文件)提供了对内核网络包处理的深度洞察。
无论你是:
- 被复杂的网络问题困扰的系统管理员
- 需要调试 CNI 或网络策略的 Kubernetes 运维
- 研究 Linux 内核网络栈的开发者
pwru 都能成为你工具箱里一件得力的"透视镜",帮你在纷繁复杂的内核代码路径中,精准定位那个"迷路"的网络包。
Packet, where are you? —— 有了 pwru,你总能找到答案。