网络包迷踪:用 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 的核心工作原理其实很优雅:

  1. 利用 BTF 信息:pwru 依赖内核的 BTF(BPF Type Format)信息,自动识别内核网络栈中所有接收 sk_buff(socket buffer,即网络包的内核数据结构)作为参数的函数。

  2. 注入 eBPF 探针:pwru 通过 kprobe(内核动态探针)或 fentry(更高效的函数入口跟踪)将这些函数挂载上 eBPF 程序。

  3. 实时输出:当网络包经过这些函数时,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=y
  • CONFIG_KPROBES=y
  • CONFIG_BPF=y
  • CONFIG_BPF_SYSCALL=y

可以用 zgrep CONFIG_XXX /proc/config.gz 来检查。

另外需要挂载 debugfs:

1
mount -t debugfs none /sys/kernel/debug

安装

直接从 GitHub Release 页面下载静态编译的可执行文件即可,支持 x86_64 和 arm64 架构:

1
2
3
4
# 下载最新版本(示例)
wget https://github.com/cilium/pwru/releases/download/v1.0.12/pwru-linux-amd64
chmod +x pwru-linux-amd64
sudo ./pwru-linux-amd64 --help

一个完整的调试示例

假设你想追踪一个访问 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
2
3
4
# 先获取 Pod 的 netns inode
docker inspect <container-id> | grep net
# 然后用 netns 过滤
sudo ./pwru --filter-netns="inode:123456789" --output-tuple

追踪被释放的包

有些场景下,包在被 kfree(释放)之后你可能还想继续追踪(比如桥接流量),可以用 --filter-track-skb-by-stackid

JSON 输出

将追踪结果输出为 JSON 格式,方便导入到其他分析工具:

1
sudo ./pwru --output-json --output-limit-lines=1000 > trace.json

适用场景

pwru 特别适合以下场景:

  1. 网络丢包问题:包到了网卡但应用收不到,快速定位是在 Netfilter、路由、还是协议栈哪一层丢了。

  2. iptables/nftables 规则调试:验证规则是否按预期生效,包到底匹配了哪条规则。

  3. 容器网络问题:在复杂的容器网络环境下(如 Calico、Cilium 等 CNI),追踪包在宿主机内核和容器 netns 之间的流转。

  4. 内核网络开发:理解网络包在内核中的完整处理流程,学习或调试内核网络模块。

  5. 性能问题排查:配合 --output-stack 观察包处理的热点路径。

与同类工具的对比

工具 定位 优势 局限
tcpdump 抓包分析 通用、轻量 只能看到进出网卡的包,看不到内核内部处理
bpftrace 动态追踪 灵活强大 需要编写脚本,学习曲线陡峭
pwru 内核网络包追踪 开箱即用、自动探测所有网络函数、过滤强大 专注于网络包追踪

pwru 填补了 tcpdump 和 bpftrace 之间的空白——它比 tcpdump 看得更深(能进内核),又比 bpftrace 更易用(无需写脚本)。

总结

pwru 是一个强大而优雅的 Linux 网络调试工具。它利用 eBPF 技术,以极低的部署成本(一个 <10MB 的静态二进制文件)提供了对内核网络包处理的深度洞察。

无论你是:

  • 被复杂的网络问题困扰的系统管理员
  • 需要调试 CNI 或网络策略的 Kubernetes 运维
  • 研究 Linux 内核网络栈的开发者

pwru 都能成为你工具箱里一件得力的"透视镜",帮你在纷繁复杂的内核代码路径中,精准定位那个"迷路"的网络包。

Packet, where are you? —— 有了 pwru,你总能找到答案。


项目地址:https://github.com/cilium/pwru