如何防止 OpenVPN 网络上的 TCP 连接冻结?

Jas*_*n R 23 vpn openvpn tcp mtu

在这个问题的末尾添加了新的细节;我可能正在关注原因。

我有一个基于 UDP OpenVPN 的 VPN 设置tap模式(我需要,tap因为我需要 VPN 来传递多播数据包,这在tun网络中似乎是不可能的)与互联网上的少数客户端。我在 VPN 上经常遇到 TCP 连接冻结的情况。也就是说,我将建立一个 TCP 连接(例如一个 SSH 连接,但其他协议也有类似的问题),并且在会话期间的某个时刻,流量似乎将停止通过该 TCP 会话传输。

这似乎与发生大数据传输的点有关,例如我是否ls在 SSH 会话中执行命令,或者我是否cat是一个长日志文件。一些 Google 搜索在 Server Fault上找到了许多类似上一个这样的答案,表明可能的罪魁祸首是 MTU 问题:在高流量期间,VPN 试图发送数据包,这些数据包在两个管道之间的某处被丢弃VPN 端点。上面链接的答案建议使用以下 OpenVPN 配置设置来缓解问题:

fragment 1400
mssfix
Run Code Online (Sandbox Code Playgroud)

这应该将 VPN 上使用的 MTU 限制为 1400 字节,并修复 TCP 最大段大小以防止生成任何大于该值的数据包。这似乎稍微缓解了这个问题,但我仍然经常看到冻结。我尝试了许多大小作为fragment指令的参数:1200、1000、576,所有结果都相似。我想不出两端之间有什么奇怪的网络拓扑结构会引发这样的问题:VPN 服务器运行在直接连接到 Internet的pfSense机器上,而我的客户端也在另一个位置直接连接到 Internet。

另一个奇怪的难题:如果我运行该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 
Run Code Online (Sandbox Code Playgroud)

上述运行是在VPN两个客户端之间:我开始跟踪从192.168.100.90到的目的地192.168.100.91。两个客户端都配置fragment 1200; mssfix;为试图限制链路上使用的 MTU。上述结果似乎表明tracepath能够检测到两个客户端之间 1500 字节的路径 MTU。由于 OpenVPN 配置中指定的碎片设置,我认为它会稍微小一些。我发现这个结果有些奇怪。

然而,更奇怪的是:如果我有一个处于停止状态的 TCP 连接(例如,一个目录列表在中间冻结的 SSH 会话),那么执行tracepath上面显示的命令会导致连接再次启动!我无法弄清楚为什么会出现这种情况的任何合理解释,但我觉得这可能指向最终根除问题的解决方案。

有没有人有其他尝试的建议?

编辑:我回来并进一步研究了这一点,并且只发现了更多令人困惑的信息:

  • 我将 OpenVPN 连接设置为 1400 字节的片段,如上所示。然后,我通过 Internet 连接到 VPN,并使用 Wireshark 查看在发生停顿时发送到 VPN 服务器的 UDP 数据包。没有一个大于指定的 1400 字节计数,因此碎片似乎运行正常。

  • 为了验证 1400 字节的 MTU 是否足够,我使用以下 (Linux) 命令 ping 了 VPN 服务器:

    ping <host> -s 1450 -M do
    
    Run Code Online (Sandbox Code Playgroud)

    这(我相信)发送了一个禁用分段的 1450 字节数据包(我至少验证了如果我将它设置为一个明显太大的值,例如 1600 字节,它就不起作用)。这些似乎工作得很好;我收到了房东的回复,没有任何问题。

所以,也许这根本不是 MTU 问题。我只是对它可能是什么感到困惑!

编辑 2:兔子洞越来越深:我现在已经更多地隔离了问题。这似乎与 VPN 客户端使用的确切操作系统有关。我已经在至少三台 Ubuntu 机器(版本 12.04 到 13.04)上成功地复制了这个问题。我可以在一分钟左右的时间内可靠地复制 SSH 连接冻结,只需cat-ing 一个大型日志文件。

但是,如果我使用 CentOS 6 机器作为客户端进行相同的测试,那么我看不到问题!我已经使用与我在 Ubuntu 机器上使用的完全相同的 OpenVPN 客户端版本进行了测试。我可以cat记录文件数小时而不会看到连接冻结。这似乎提供了一些关于最终原因的见解,但我不确定这种见解是什么。

我已经使用 Wireshark 检查了 VPN 上的流量。我不是 TCP 专家,所以我不确定如何处理这些血腥细节,但要点是,在某些时候,由于 Internet 链接的带宽有限,UDP 数据包会被丢弃,导致内部 TCP 重新传输VPN 隧道。在 CentOS 客户端上,这些重传正确发生,事情进展顺利。但是,在 Ubuntu 客户端的某个时刻,远程端开始一遍又一遍地重新传输相同的 TCP 段(每次重新传输之间的传输延迟都会增加)。客户端向每次重传发送看似有效的 TCP ACK,但远程端仍会继续定期传输相同的 TCP 段。这无限扩展并且连接停止。我的问题是:

  • 有没有人对如何排除故障和/或确定 TCP 问题的根本原因有任何建议?就好像远程端不接受 VPN 客户端发送的 ACK 消息一样。

CentOS 节点和各种 Ubuntu 版本之间的一个常见区别是 Ubuntu 具有更新得多的 Linux 内核版本(从 Ubuntu 12.04 中的 3.2 到 13.04 中的 3.8)。可能是指向一些新内核错误的指针?我假设如果是这样,那么我不会是唯一遇到问题的人;我不认为这似乎是一个特别奇特的设置。

小智 11

这个命令为我解决了这个问题:

$ sudo ip link set dev tun0 mtu 1350 && echo ":)"
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法验证 tun0 设置

$ ip a s
Run Code Online (Sandbox Code Playgroud)

干杯!