最近在家里经常遇到 ssh 超时的问题,一开始也没太当回事,感觉是网络不稳定导致的,但是后来慢慢的发现这种超时问题只会出现在跟 ssh 相关的程序中,例如 git、ssh。这成功的引起了我的注意,于是我开始尝试着去排查。
首先我测试了一下 ping 远程服务器,发现结果还是比较正常的,所以在 icmp 协议上,连接应该是没有问题的。然后安装 tcping
软件包,测试 tcp 连接,结果显示也是正常的,那么看起来就是 ssh 协议的问题了。于是接着尝试让 ssh 输出调试信息:
$ ssh -v -o ConnectTimeout=10 test-server
输出结果如下:
# .....
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
# 一直等待,直到超时
可以看到,ssh 连接的建立一直阻塞在了 expecting
这个阶段,用这行命令的输出在网络上查找,发现了是 MTU 过大引起的问题: mtu missmatch。
这片文章给出的解决方案是将系统的 MTU 的设置成一个较小的值(例如,576)。使用 ip
命令简单的执行了一下,发现确实可以解决 ssh 超时的问题:
查看当前的 MTU 设置
$ ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: wlp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DORMANT group default qlen 1000
link/ether ac:bc:32:d4:cd:af brd ff:ff:ff:ff:ff:ff
接着修改无线网卡的 MTU 设置:
$ sudo ip link set dev wlp4s0 mtu 576
但是我的潜意识告诉我这么干并不优雅,于是我在 Arch Wiki 上面找到了这个内容。这里面提到一个内核参数 —— tcp_mtu_probing
,启用这项内核功能可以让操作系统根据网络状况自动的调节 MTU 大小,从而在性能与稳定性之间找到一个微妙的平衡,具体的介绍可以看 Cloudflare 这篇介绍
只要先在 /etc/sysctl.d/99-sysctl.conf
加上下面的内容:
net.ipv4.tcp_mtu_probing = 1
然后使用 sysctl
加载配置就好了:
$ sudo sysctl --system