问题一般是由于您到 freeshell 链路上的网络设备(包括路由器、NAT 等)没有正确转发 ICMP Error (Packet Too Big) 包。要验证这个问题,请用下列 ssh -v 命令验证,如果最后两行输出与下面的相同,并且卡住了,则一般是这种问题。还可以用 wireshark 抓包,如果没有收到 ICMP Destination Unreachable 包,则确定是网络问题。


$ ssh -v -p 30xxx [email protected]
OpenSSH_6.6.1, OpenSSL 1.0.1h 5 Jun 2014
debug1: Connecting to ssh.freeshell.ustc.edu.cn [202.141.176.99] port 30xxx.
debug1: Connection established.
...
debug1: sending SSH2_MSG_KEX_ECDH_INIT
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY

问题原因是 ssh.freeshell.ustc.edu.cn 与 freeshell 计算节点之间使用了 GRE tunnel,载荷 MTU 为 1476 字节,略小于以太网通常的 MTU 1500。ssh 密钥交换阶段一次发送的数据一般大于 1500 字节,因此至少填满了一个 MTU,这个长为 1500 字节的包发到 ssh.freeshell.ustc.edu.cn 会被丢弃,并返回一个 ICMP Error (Packet Too Big) 消息,其中包含了可用 MTU 1476。SSH 客户端所在主机收到 ICMP 消息后,会重新对数据拆分为 1476 字节大小的分片并发送。这个 MTU 会在路由表中缓存一段时间,因此短时间内第二次连接 freeshell IPv4 会使用正确的分片大小,不会收到 ICMP Error 消息。

遇有此种问题,请联系您的运营商解决。NAT 设备转发 ICMP Error 包是 RFC 规范的要求

如果想临时解决此问题,可以修改网卡的链路 MTU 为 1476,例如 ifconfig eth0 mtu 1476。请注意网络重新连接后 MTU 设置会恢复原状。

Update (2014-07-31): 此问题已解决。我们在 blog 主机上添加了 iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 以修改用户所发起连接的 MSS(Max Segment Size),这样 freeshell 返回的 SYN+ACK 包就会包含用户支持的 MSS 与 blog-freeshell 间的 tunnel 所支持 MSS 的较小者,用户随后发送 SSH 认证数据包时,就会分片成合适大小,从而不会产生 ICMP unreachable 消息。