~$ lambsouls ▌
~/posts/mtu-1480

断网、丢包、网盘读不出来,折腾了几个月发现是同一个问题

· 网络排障 · #MTU #移动宽带 #丢包 #排障

搬到新地方以后,家里用的是移动宽带,网络就一直没消停过,这个问题困扰了我很久。

具体是好几件事放在一起:PC 上偶尔丢包,NAS 连网盘经常读不出文件,目录能看见,点进去就转到超时,飞牛(fnOS)的面板有时候卡死打不开,应用商店下东西老是失败。麻烦的是它有时候又自己好了,过一阵再犯,没什么规律,我一开始还以为是那段时间网络本身不稳。

中间能换的我都换了。网线换了,没用。光猫的桥接方式改了,还是没用。后来干脆把 NAS 换了一台新的,想着新旧硬件不一样,总能排除掉是不是机器的问题,结果新机上一模一样的毛病。这期间也换过 DNS、关过防火墙、查过网盘账号是不是被限速,一样没用。

后来一直查不出来,我就想着干脆抓数据看看。让 dsh 帮我在 PC 和 NAS 上各挂了一些探针,把连接的真实计数记下来。挂了几天,从数据里才看出来:有些大小的包发出去以后,对面从来没回过,本地一直在重传,rto 一直往上涨,bytes_acked 远远小于 bytes_sent,重传数几乎等于发送数。就是包发走了,对面一个都没收到。

这基本就是 MTU 黑洞了。量了一下,PC 上 ping -f -l 1452 能通,1453 就不行,提示需要拆分数据包但设置了不分片标记。1452 加 28 字节包头正好是 1480,说明这条线路实际只能过 1480 的包,而我们两台机器都在按 1500 往外发。

ping -f -l 1452 <目标地址>
# linux 上写法不一样
ping -M do -s 1452 <目标地址>

一直到这时候才明白,为什么前面看着像好几个不相干的毛病:不管是在 PC 上丢包,还是 NAS 读网盘,只要那个操作要发一个超过 1480 的包,这个包就会被静默扔掉,本该回来的"包太大了"那条 ICMP 也被中间屏蔽了,两边都不知道。小包照常能过,所以表现出来就是时好时坏。

还有一件事我一直没想通,同一台机器同一个时刻,用 curl 下 31MB 的文件能下完,走代理连一个字节都收不到。后来才明白区别只在单个包有没有踩到 1500。

改起来不复杂。NAS 那边在网络设置里把 MTU 改成 1480,重启也保持得住。PC 这边踩了个坑:netsh interface ipv4 set subinterface 30 mtu=1480 store=persistent 会回一句"确定。",看着像成了,去注册表里查 MTU 这个值根本没有,重启就变回去。要直接写注册表:

reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{网卡GUID}" /v MTU /t REG_DWORD /d 1480 /f

写完再读一遍确认。别信回显。

改完发现 docker0、veth、容器里的 eth0 还是 1500,我以为还得重建容器。试了一下不用,宿主机现在自己知道出不去,会替容器分片或者回 ICMP,容器那边取 1MB 照样能下来,206、1048576 字节,两秒左右。

两边都改成 1480 之后,之前那些毛病基本没了。探针我没撤,留在 NAS 上定时跑,一个每分钟一个每 5 分钟,都走 HTTP 接口,不敢走挂载,挂载一旦挂死进程会进 D 状态,kill -9 都杀不掉,还会把 nginx 一起拖住,面板就整个打不开了。

ss -tni

这命令是当时看连接状态用的,重点看三个数:bytes_acked 远小于 bytes_sent、bytes_retrans 接近 bytes_sent、rto 一直指数退避。现在留着当个习惯。

这次能找到原因,主要还是靠探针抓下来的那些数据。之前用的是 deepseek-4.1-flash,问它基本就是让我查 DNS、查防火墙、换驱动、换网线这一套,来回折腾很久没什么进展。换成 deepseek-4.1-flash-1017 之后,它让我先别急着下结论,先把 bytes_acked、bytes_retrans、rto 这些抓下来看,我才第一次拿到能定位问题的东西。中间我连着猜错四次,USB 网卡、DNS、账号风控、服务状态,它每次都把我错在哪指出来,让我回去补数据,没有顺着我的猜测往下编。

要是早点想到量一下两边能过多大的包,就不用折腾这么久。不过我感觉这确实不是人类能第一时间想到的。