![如何防止 OpenVPN 网络上的 TCP 连接冻结?](https://linux22.com/image/608213/%E5%A6%82%E4%BD%95%E9%98%B2%E6%AD%A2%20OpenVPN%20%E7%BD%91%E7%BB%9C%E4%B8%8A%E7%9A%84%20TCP%20%E8%BF%9E%E6%8E%A5%E5%86%BB%E7%BB%93%EF%BC%9F.png)
该问题末尾添加了新细节;我可能正在集中精力寻找原因。
我在 UDP OpenVPN 模式下设置了一个 VPN tap
(我需要tap
这个 VPN 是因为我需要 VPN 来传递多播数据包,而网络似乎无法做到这一点tun
),并在互联网上与少数客户端建立连接。我在 VPN 上经常遇到 TCP 连接冻结的情况。也就是说,我将建立一个 TCP 连接(例如 SSH 连接,但其他协议也有类似的问题),并且在会话期间的某个时刻,似乎流量将停止通过该 TCP 会话传输。
这似乎与发生大量数据传输的点有关,例如,如果我ls
在 SSH 会话中执行命令,或者如果我有cat
一个很长的日志文件。一些 Google 搜索会找到许多答案就像之前在 Server Fault 上看到的这个一样,表明可能的罪魁祸首是 MTU 问题:在流量高峰期间,VPN 试图发送数据包,但这些数据包在 VPN 端点之间的管道中被丢弃。上述链接的答案建议使用以下 OpenVPN 配置设置来缓解此问题:
fragment 1400
mssfix
这应该会将 VPN 上使用的 MTU 限制为 1400 字节,并修复 TCP 最大段大小以防止生成任何大于该大小的数据包。这似乎可以稍微缓解问题,但我仍然经常看到冻结。我尝试了多种大小作为指令的参数fragment
:1200、1000、576,所有结果都相似。我想不出两端之间有任何奇怪的网络拓扑会触发这样的问题:VPN 服务器运行在普富思一台机器直接连接到互联网,并且我的客户端也在另一个位置直接连接到互联网。
另一个奇怪的谜题是:如果我运行该tracepath
实用程序,那么问题似乎就迎刃而解了。示例运行如下:
[~]$ tracepath -n 192.168.100.91
1: 192.168.100.90 0.039ms pmtu 1500
1: 192.168.100.91 40.823ms reached
1: 192.168.100.91 19.846ms reached
Resume: pmtu 1500 hops 1 back 64
192.168.100.90
上面的运行是在 VPN 上的两个客户端之间进行的:我启动了从到 目的地的跟踪192.168.100.91
。两个客户端都配置了 ,fragment 1200; mssfix;
以尝试限制链路上使用的 MTU。上述结果似乎表明tracepath
能够检测到两个客户端之间的路径 MTU 为 1500 字节。我认为由于 OpenVPN 配置中指定的碎片设置,它会稍微小一些。我发现这个结果有点奇怪。
然而,更奇怪的是:如果我的 TCP 连接处于停滞状态(例如,SSH 会话中的目录列表在中间冻结),那么执行tracepath
上述命令会导致连接重新启动!我无法找到任何合理的解释来解释为什么会出现这种情况,但我觉得这可能指向一个最终根除问题的解决方案。
有人推荐其他可以尝试的东西吗?
编辑:我回来进一步研究了这个问题,却发现了更多令人困惑的信息:
我将 OpenVPN 连接设置为以 1400 字节为单位进行分段,如上所示。然后,我从互联网连接到 VPN,并使用 Wireshark 查看发生停顿时发送到 VPN 服务器的 UDP 数据包。没有一个大于指定的 1400 字节数,因此分段似乎运行正常。
为了验证 1400 字节的 MTU 是否足够,我使用以下(Linux)命令 ping 了 VPN 服务器:
ping <host> -s 1450 -M do
我相信这会发送一个 1450 字节的数据包,并且禁用碎片处理(我至少验证过,如果我将其设置为明显过大的值(如 1600 字节),则不会起作用)。这些似乎工作正常;我毫无问题地收到了主机的回复。
所以,也许这根本不是 MTU 问题。我只是不明白它还有什么其他原因!
编辑2:兔子洞越来越深:我现在进一步隔离了这个问题。它似乎与 VPN 客户端使用的确切操作系统有关。我已经在至少三台 Ubuntu 机器(版本 12.04 到 13.04)上成功复制了这个问题。我只需对一个大型cat
日志文件执行 -ing 操作,就可以在一分钟左右的时间内可靠地复制 SSH 连接冻结。
然而,如果我使用 CentOS 6 机器作为客户端进行相同的测试,那么我看不到问题!我使用与在 Ubuntu 机器上使用的完全相同的 OpenVPN 客户端版本进行了测试。我可以cat
连续数小时记录文件而不会看到连接冻结。这似乎提供了一些关于最终原因的见解,但我只是不确定那个见解是什么。
我已经使用 Wireshark 检查了 VPN 上的流量。我不是 TCP 专家,所以我不确定如何理解这些可怕的细节,但要点是,在某个时候,由于 Internet 链路的带宽有限,UDP 数据包被丢弃,导致 VPN 隧道内的 TCP 重新传输。在 CentOS 客户端上,这些重新传输正常进行,一切顺利进行。但是,在 Ubuntu 客户端的某个时候,远程端开始一遍又一遍地重新传输相同的 TCP 段(每次重新传输之间的传输延迟都会增加)。客户端向每次重新传输发送看似有效的 TCP ACK,但远程端仍会定期继续传输相同的 TCP 段。这会无限延长,连接会停滞。我的问题是:
- 有人对如何排除故障和/或确定 TCP 问题的根本原因有什么建议吗?就好像远程端没有接受 VPN 客户端发送的 ACK 消息。
CentOS 节点和各种 Ubuntu 版本之间的一个常见区别是 Ubuntu 具有更新的 Linux 内核版本(从 Ubuntu 12.04 中的 3.2 到 13.04 中的 3.8)。也许是指向一些新的内核错误的指针?我假设如果是这样,那么我不是唯一遇到此问题的人;我不认为这似乎是一个特别奇特的设置。
答案1
这个命令帮我解决了这个问题:
$ sudo ip link set dev tun0 mtu 1350 && echo ":)"
您可以使用以下方式验证 tun0 设置
$ ip a s
干杯!
答案2
使用以下命令禁用 TCP 中的窗口缩放:
sysctl -w net.ipv4.tcp_window_scaling=0
完成后,通过 VPN 连接到 Debian/Ubuntu 系统的 SSH 对我来说就可以正常工作了。
答案3
在 Windows 上使用 Putty,您必须通过转到 vpn 连接的本地连接 -> 网络接口的详细信息(TAP windows 适配器或类似的东西)-> 高级 -> 属性 -> MTU(将其更改为低于 1500)来更改 MTU。您可能需要重新连接。它在 Windows 和 Putty 上对我有用
答案4
看起来这是一个缓冲问题。我遇到了同样的问题,我可以通过限制传输速度来避免它。这不是最好的方法,但它可能会帮助某人找到更好的解决方案。
请参阅此处的更新 1: 如何防止 OpenVPN 客户端到客户端连接时 SSH 冻结