pwru1 是排查 Linux 网络问题最好的工具,全称是 Packet where are you?
它是怎么工作的呢?
eBFP 可以让我们往 kernel 的函数上添加 hook,当这个 kernel 函数执行的时候,我们可以通过 eBPF 定义一些额外的动作,比如把这个函数和它的参数记录下来然后打印出来。
Linux 启动的时候,会生成 /proc/kallsyms
,pwru 通过读取这个文件,找到所有和 skb (网络包在内核中的数据结构)相关的函数,然后 hook 这些函数。这样,在这个包在内核栈经过的路径,就可以用 eBPF 追踪到了2 3。
通过这种方法,pwru 几乎可以解决在 Linux 上遇到的任何网络不通的问题,因为通过函数路径可以很快确定这个包经过了哪些函数处理,没有走到哪些函数。对照函数查看源代码,就可以知道原因了。
它的安装方法很简单:apt install pwru
即可4。
然后通过 pwru icmp and dest host 1.1.1.1
就可以开始追踪了。包的过滤语法和 tcpdump
一样。
案例分享
今天遇到的问题,还是网络在 Linux 上网络不通了,网络结构很简单,就是默认路由到一个 vxlan driver 的 interface,经过这个 interface 封装,然后通过物理 interface 发送出去。这种网络不通的问题最适合用 pwru 定位了,这个工具可以直接告诉我们包在 Linux 网络栈经过的代码路径。
我们用以下命令来抓这个包,pwru --filter-track-skb --all-kmods dst 10.1.1.100
,得到的输出如下:

可以看到数据包的确被 Drop 了,虽然原因是 SKB_DROP_REASON_NOT_SPECIFIED
,但是不要紧,我们可以看到 drop 之前的函数是 vxlan_get_route
。然后就可以去查看对应的 Linux 源代码5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if (!IS_ERR(rt)) { if (rt->dst.dev == dev) { netdev_dbg(dev, "circular route to %pI4\n", &daddr); ip_rt_put(rt); return ERR_PTR(-ELOOP); } *saddr = fl4.saddr; if (use_cache) dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); } else { netdev_dbg(dev, "no route to %pI4\n", &daddr); return ERR_PTR(-ENETUNREACH); } |
从源码中可以确认,这个 vxlan_get_route
函数返回错误的原因有 2 个:
- 一个是 vxlan 封包之后使用的 dev 和现在一样,那就是出现环路了,会导致一直封包一直循环;
- 另一个就是没有路由;
按照包的封装过程查看 ip route
,发现确认有一条多加的路由导致环路了,删除即可恢复。
如果没有 pwru 的话,就得依靠网络的经验逐个地方检查,然后判断出来是路由表配置问题。但是有了 pwru,就可以顺藤摸瓜得排查到根因。
- https://github.com/cilium/pwru ↩︎
- https://cleveruptime.com/docs/files/proc-kallsyms ↩︎
- https://github.com/cilium/pwru/blob/db786876d10bc104be1a7908e13902c890e548d0/internal/pwru/ksym.go#L45 ↩︎
- 这个是我司在内网打包的,公网不能这么安装,用户需要按照 Github 的文档来安装。 ↩︎
- 相应的代码地址:https://elixir.bootlin.com/linux/v5.15.178/source/drivers/net/vxlan/vxlan_core.c#L2428 ↩︎