为什么 OpenConnect 页面要等到你调低隧道 MTU 才不再卡住
小响应能到,大响应就死。用 DF 探测测出路径上限,在隧道设备上设置尺寸,并让这个值在重新连接后依然有效。
登录成功,门户首页也画出来了,随后某个仪表盘小组件或报表就一直停在 waiting for host,直到浏览器放弃。公共站点全程正常加载,凭据也没有任何变化。小响应能到、大响应卡住,这是包大小上限的特征,而不是访问规则。在改动配置文件里其他任何东西之前,先确认这一判断。
把尺寸问题和策略问题分开
先排除被拒绝的可能。如果网关拒绝了你,连接会立刻失败,而不是把页面加载到一半。隧道能建立起来、之后只在较大的传输上卡住,说明外层路径载不动内层包。
机制是封装。每个内层包都会多出隧道头,于是原本塞得进物理链路的载荷,变成一个塞不进去的外层包,而任何带 do-not-fragment 位的包都会被丢弃,浏览器也拿不到能解释的响应。
用 do-not-fragment 探测测出上限
测出来,别猜。发送拒绝分片的探测,把载荷逐级下调,直到有响应回来,从类似这样的命令开始:ping -M do -s 1472 host。DF 已设置却抱怨需要分片的响应,说明这个值仍然太大。在 1462 上重试,然后是 1452,停在第一个能返回响应的尺寸上。把数字连同你探测的主机一起记下来,因为 这个答案属于那条路径,而不是协议本身。
要探测隧道实际发流量的那个地址。针对网关公网名的外层探测和针对内部服务器的内层探测量的是不同的跳数,任何一边的 ICMP 过滤都可能让一条健康的路径看起来是坏的。
把值应用到隧道设备上
把结果应用到客户端创建的那个接口,而不是 Wi-Fi 网卡。在 Linux 客户端上是 ip link set dev tun0 mtu 1300 之后再用 ip link show tun0 确认值已生效。客户端报什么设备名就用什么,因为不同实现、不同隧道搭建方式下名字不一样。
调低隧道 MTU 会让每个包承载的载荷变小,因此整体吞吐会略降,这是不把大响应丢进黑洞的代价。数字更小并不自动更安全。反过来改物理网卡,会把损害摊到那条链路上的每一个连接,而超大的内层包依旧超大。
先试客户端自带的 MTU 请求
当你控制调用方式时,可以让客户端直接申请尺寸,而不是事后打补丁。--mtu 向服务器请求隧道 MTU,--base-mtu 声明底层路径 MTU,让网关能推导出隧道数值。两者都只是请求,客户端的 手册 并没有承诺网关一定照办。
这个区别决定了你的工作顺序。先试请求,看看它生成的接口,如果网关选的值仍然卡住,再退回到本地设置设备。
用当初失败的那次传输来验证,然后把它固化下来
本地 ip link 的改动只维持到客户端重建隧道为止,而那发生在下次重连或恢复时。把值放到连接启动的地方:一个先启动客户端再调整设备的包装脚本、带 ExecStartPost 步骤的 systemd 单元,或者网关接受时的客户端选项。把它和配置文件一起放进版本控制,下一个人才能看到这个数字为什么在那里。
用失败的那个请求来测,别用首页。打开报表、拉取附件,或者跑一遍当初卡住的仓库拉取,如果仍然卡住,就记下它究竟停在哪里。改动之后能渲染的页面证明不了什么,因为改动之前它也能渲染。
如果同一次传输仍停在同一个位置,那 MTU 理论就不成立。先在隧道上抓包,看清是哪一侧停止发送,再决定要不要再改数字,并把上一个值记下来,让回滚始终是一条命令。