我们遇到一个问题,许多客户端(所有 Linux Ubuntu)有时无法通过 SSH 连接到远程服务器。如果出现此问题,Windows 客户端不会出现此问题,并且可以正常连接。
我发现另一个问题也有类似的问题: 为什么服务器不会发送 SYN/ACK 数据包来响应 SYN 数据包
在服务器上禁用 TCP 时间戳确实可以解决问题,但我想知道真正的问题是什么。我真的不明白为什么这会造成任何问题,在建立连接时肯定不会。
使用 Wireshark 时,我发现 Windows 客户端使用的窗口大小为 8192,而 Linux 客户端使用的窗口大小为 29200。Windows 客户端收到 SYN_ACK,而 Linux 客户端没有收到。这个较高的初始窗口大小是否可能导致服务器不发送 SYN_ACK?我无法想出一个合理的解释来说明为什么它会导致给定的问题,但由于这是唯一(对我而言)的差异,所以看起来确实如此。我遗漏了什么吗?
*** 编辑 经过更多的搜索、思考和一些巫术,我想我可能想出了一个合理的解释。这确实需要一些假设和特定条件,但我相信这些在这种特殊情况下可能是可能的。
两个用户都位于同一个 NAT 设备(在我们的例子中是 Fortigate 防火墙)后面。该防火墙将在其外部接口/IP 上为每个 NAT 连接分配本地端口。如果该端口已被其他用户使用,则跳过该端口。如果连接已关闭,则释放该端口并返回到 NAT 池。如果该端口随后分配给其他用户,但服务器仍保留一些连接记录(TIME_WAIT,未收到最终 FIN/ACK)并且数据包的时间戳低于前一个连接的时间戳,则该数据包将被默默丢弃。
好吧,这里面有很多如果,但是…… - 这两个用户正在同一个网站上开发,因此他们将与同一个远程服务器建立大量连接 - 防火墙 (Fortigate) 似乎会按源 IP/目标 IP/目标端口保留 NAT 端口的顺序计数器。如果两个用户的计数器彼此接近,则在两个与该服务器的连接中发生这种“冲突”的可能性并不大,因为两个目标 IP 作为端口都相同。这可以解释为什么问题只是偶尔发生。
这个理论的唯一问题是,我找不到任何证据表明这种情况发生在服务器端。没有连接卡在 TIME_WAIT 或类似状态,我确实认为一旦它们从 netstat 输出中消失,服务器就会忘记它们。
我确实相信初始窗口大小在这方面不起作用,因此我将其列为嫌疑人名单之一。
答案1
因此,如果 Windows 客户端没有问题,我猜它们没有请求 TCP 时间戳,而 Linux 客户端请求了。您可以通过再次查看两个示例中的 Wireshark 捕获来验证这一点。
要开始排除时间戳问题的根本原因,首要任务是确保客户端和服务器与 NTP 服务器同步。如果它们只有一个自由运行的时钟,那么很可能就是问题的原因。例如:
# ntpq -p
remote refid st t when poll reach delay offset jitter
========================================================================
*utcnist2.colora .ACTS. 1 u 92 1024 377 50.242 2.041 1.847
+time-c.timefreq .ACTS. 1 u 623 1024 377 55.413 -1.781 0.418
确保至少有一个前面有星号。这意味着它是同步的。无论如何,一开始就看到 TCP 会话停滞是很奇怪的。人们会认为它在交换了几个带有时间戳值的数据包后就会停滞。更准确地说,当一个数据包的时间戳值似乎与前一个数据包的时间相反时。