eth0 上有流量不代表 VPN 隧道在泄漏
物理网卡上的公网地址通常是隧道自己的外层封装。先比较各接口,再问内核它会选哪个设备。
隧道已建立时在 eth0 上看到公网地址,感觉像是证据表明有东西漏了出去。通常恰恰相反:隧道自身的报文必须经过那块网卡才能到达服务器,它们看起来就像普通的 UDP 或 TCP 流。内层报文只会出现在隧道设备上。区分这两者靠的是读懂外层封装,而不是相信字节计数。
每个隧道报文仍然要经过物理线路
隧道是一种接口,而不是一条物理线路。客户端取来内层报文,把它封装起来,再把结果发往配置里的服务器地址和端口。在物理网卡上你看到的是封装;在隧道设备上你看到的是被封装的内容。
在 UDP 模式下,外层流在每个方向上只有一对地址和端口,所以健康的隧道在 eth0 上显得几乎乏味。首先要找的正是那一对。
一次只抓一个接口
打算比较计数时,请一次只抓一个接口。any 这个伪接口会把所有设备一次性收集起来,因此被转发的报文会在入口和出口各出现一次,而本地生成的报文会以两种不同形态出现两次。报文数相等而字节数不等是正常的,因为封装比它承载的内容更大。ARP、DHCP 和 IPv6 路由器通告属于本地链路,与你的应用无关。
sudo tcpdump -i eth0 -n host 203.0.113.10 and udp port 1194把地址和端口换成配置里的实际值。如果这条流是稳定的,说明隧道在承载东西。
问内核它会选哪个设备
先问内核它会选哪个设备,再到线路上确认。路由查询是决策,抓包是决策被执行的证据。它们回答的是不同的问题,两者都需要。
ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111常见的结果是分流的:IPv4 走隧道,而 IPv6 走物理网卡,因为配置只下发了 IPv4 路由。这是真正的泄漏,而且在只抓 IPv4 时完全看不到。过滤 IPv6,留意既不是服务器、也不是 link-local 的地址。
在连接前后各跑一次这两种查询,这样你才能看到客户端实际改了什么。两次运行之间的差异才是客户端的真实作用。
当抓包结果确实是泄漏
有时抓包说的是实话。一条更窄的路由把某个前缀送到隧道之外,客户端被要求排除的某个子网,或者在隧道策略之前就生效的防火墙放行规则,都可能把流量明文放到线路上。匹配某个进程组的规则也可能是有意留下的例外。在把它判定为故障之前,先读清它的接口和目的范围。
该修的是路由或排除项,而不是防火墙。然后从新连接重新抓包,并分别检查 IPv4 和 IPv6,因为修好其中一个并不能说明另一个。tcpdump 手册介绍了上文用到的接口与方向选择符。