为什么 VPN 看起来已连接却什么都用不了
「已连接」可能描述的是进程、接口或关联,而不是一条可用的通路。发一个新请求,看哪个证据能证明流量真的走了。
客户端报告已连接,接口处于开启状态,浏览器却超时,而这三句话没有一句是假的。 「已连接」描述的是与你所请求的页面毫无关系的一层。 要判断是哪一层在报告,只需发一个新请求,再看一眼能证明流量真的走了的计数器。 重启会把这些证据丢掉,并重建你原本想观察的条件。
三种不同的「已连接」
最底层,已连接可能指客户端进程正在运行,并持有一个指向对端的套接字。 再往上一层,它可能指存在一个带地址的接口,而常驻设备或恢复例程可以在其背后没有活动会话的情况下把它建出来。 在控制层,它可能指协商出了一个关联,而这并不说明有没有哪怕一个数据包从它下面经过。 三者各自能扛住别的层扛不住的故障,而客户端通常显示它能主张的最强的那一种。
因此绿色图标和死掉的隧道是相容的,而不是矛盾的;它们报告的是不同的对象。 这正是这个故障总被误诊为客户端 bug 的全部原因。
在碰守护进程之前,先向你真正需要的目标发一个无害请求,并在操作期间保持必需的直接流量拦截。 重启会抹掉事件顺序,再把条件重建得足够接近以掩盖原因,所以同一张工单一周后又会回来,里面没有任何新东西。 一个请求、一次计数器读数,以及引发故障的触发条件,就是最低限度的证据。
哪个计数器能证明数据包动了
在 IPsec 中,IKE 关联和 CHILD 关联是两个独立对象,只有 child 才带有能回答这个问题的计数器。 列出 child,观察两个方向的数据包是否都在增加,再与应用想做的事情对照。 装好了却停在零的关联,和一边增长一边应用仍然失败,是两种不同的结论;前者说明流量没有被选中,后者说明流量在走,而故障在更后面。 计数器要读两次,间隔几秒,因为一次采样看不出趋势。
swanctl --list-sas ip xfrm state list ip -s link show dev tun0
零计数器指向选择环节:路由、策略、数据包离开的命名空间,或者客户端从未绑定过的接口。 增长的计数器说明问题不在隧道,于是按顺序继续查远端的转发、域名解析、回程路由和目标服务。 没有独立 child 对象的协议也会用自己的说法给出同样的证据,所以要用它报告的邻居状态和包计数,而不是照搬 IPsec 的术语。 读数的形状比产生它的工具更重要。
计数器动了之后,故障落在哪里
网关能在自己的管理地址上应答,只能证明网关在运行,不能证明它后面有什么在服务你的请求,所以要测远端,而不是中间那个盒子。 域名解析最会藏故障,因为一个通过隧道到不了的解析器,超时起来和死掉的连接一模一样。 两次查询就能分开这两种情况,而且都很便宜。 先问隧道推送的解析器,再用同一个名字问一个公共解析器。
dig +short host.example dig +short @10.20.0.1 host.example ping -c 2 10.20.0.25
一个名字能通过公共解析器解析、却不能通过隧道解析器解析,说明 DNS 通路断了;而两边都能解析、浏览器仍然超时,则指向回程路由或服务本身。 如果按地址 ping 能通,应用仍然拒绝,那就是网络把请求送到了、服务把它拒了,这是完全更靠后的阶段。
记下触发条件,然后只改一处
记下故障之前发生了什么:一段空闲、一次网络切换、一次密钥更新、一台唤醒的笔记本,或者一次系统还原。 这些会产生不同的机制,而通常只有系统还原会自己修好,恰恰是那种什么都教不了你的情况。 重启会抹掉事件顺序,再把条件重建得足够接近以掩盖原因。 趁触发条件还能复现,把它写下来。
然后做一个有依据的修正,并带着引发故障的触发条件重跑原来的请求。 空闲是最容易复现的触发条件,所以它也是最快的确认:修的是机制,而不是症状。
你要报告的是证据:你打算发送的流量,到达了你打算到达的服务。 变绿的图标、能加载的缓存页面、会动的计数器,加起来都不如一个打向目标地址并完成的请求。 把请求、计数器读数和触发条件放在一起,下一个人才能分清这是修好了还是碰巧。