我一直在解决严重的 WAN 速度问题。我修复了它,但为了其他人的利益:
通过 WireShark、日志记录和简化配置,我将其缩小到从对内部网络上的服务器执行 DNAT 的网关的一些奇怪行为。网关(一个 CentOS 机器)和服务器都在同一个 VMware ESXi 5 主机上运行(事实证明这很重要)。
这是发生的事件序列 - 非常一致 - 当我尝试使用直接连接到网关 WAN 端的测试客户端(绕过此处通常使用的实际互联网连接)从 DNAT 后面的 HTTP 服务器下载文件时:
通常的 TCP 连接建立(SYN、SYN ACK、ACK)正常进行;网关以两种方式正确地重新映射服务器的 IP。
客户端发送带有 HTTP GET 的单个 TCP 段,这也被正确地 DNAT 到目标服务器。
服务器通过网关发送一个 1460 字节的 TCP 段,其中包含 200 响应和文件的一部分。线路上帧的大小为 1514 字节 - 有效载荷为 1500。该段应该穿过网关但没有。
服务器通过网关发送第二个 1460 字节的 TCP 段,继续文件。同样,链接有效负载为 1500 字节。该段也不穿过网关,也从未被考虑在内。
网关将 ICMP 类型 3 代码 4(目标无法到达 - 需要分段)数据包发送回服务器,引用事件 3 中发送的数据包。ICMP 数据包指示下一跳 MTU 为 1500。 这似乎是荒谬的,因为网络是 1500 字节干净的,并且 3 和 4 中的链接有效负载已经在规定的 1500 字节限制内。服务器可以理解地忽略此响应。(最初,ICMP 已被过分热心的防火墙丢弃,但已修复。)
经过相当长的延迟(在某些配置中,来自服务器的重复 ACK),服务器决定重新发送事件 3 中的数据段,这次是单独发送。除了 IP 标识字段和校验和之外,该帧与事件 3 中的帧相同。它们的长度相同,并且新帧仍然设置了 Don't Fragment 标志。 然而,这一次,网关愉快地将段传递给客户端 - 整体 - 而不是发送 ICMP 拒绝。
客户端对此进行确认,并且传输继续进行,尽管速度非常慢,因为后续段经历大致相同的被拒绝、超时、重新发送然后通过的模式。
如果客户端移动到局域网直接访问服务器,则客户端和服务器可以正常工作。
这种奇怪的行为会根据目标服务器看似无关的细节发生不可预测的变化。
例如,在 Server 2003 R2 上,如果启用了 Windows 防火墙(即使它允许 HTTP 和所有 ICMP),7MB 的测试文件将花费 7 小时来传输,而问题根本不会出现,而且矛盾的是永远不会被拒绝如果禁用了 Windows 防火墙,则首先由网关发送。另一方面,在 Server 2008 R2 上,禁用 Windows 防火墙没有任何效果,但传输虽然仍然受到影响,但比在启用防火墙的 Server 2003 R2 上快得多。(我认为这是因为 2008 R2 使用了更智能的超时启发式算法和 TCP 快速重传。)
更奇怪的是,如果目标服务器上安装了 WireShark,问题就会消失。因此,为了诊断问题,我必须在单独的 VM 上安装 WireShark 以观察 LAN 端网络流量(出于其他原因,这可能是一个更好的主意。)
ESXi 主机版本为 5.0 U2。
您不能丢弃需要 ICMP 分段的消息。它们是 pMTU 发现所必需的,而 pMTU 发现是 TCP 正常工作所必需的。请 LART 防火墙管理员。
十多年来,这种配置已被公认为根本上已被打破。ICMP 不是可选的。
我终于弄清楚了。结果证明是 VMware 在目标服务器的虚拟 NIC 中实现 TCP 分段卸载的问题。
服务器的 TCP/IP 堆栈将向 NIC 发送一个大块,并期望 NIC 将其分解为限制为链路 MTU 的 TCP 段。然而,VMware 决定将其留在一个大的部分中,直到——好吧,我不确定什么时候。
当它到达网关 VM 的 TCP/IP 堆栈时,它似乎实际上停留了一大段,这引发了拒绝。
在生成的 ICMP 数据包中隐藏了一条重要线索:被拒绝数据包的 IP 标头指示大小为 2960 字节 - 比它似乎拒绝的实际数据包大得多。如果 TCP 段合并了迄今为止发送的两个段的数据,那么这也正是 TCP 段在网络上的大小。
使问题很难诊断的一件事是,传输的数据实际上被分成 1500 字节的帧,只要运行在另一个 VM 上的 WireShark(连接到一个单独的混杂端口组上的同一 vSwitch)可以看到。我真的不确定为什么网关 VM 看到一个数据包而 WireShark VM 看到两个。FWIW,网关没有启用大量接收卸载 - 如果启用了我能理解。WireShark 虚拟机运行的是 Windows 7。
我认为 VMware 延迟分段的逻辑是,如果数据要传出物理 NIC,则可以利用 NIC 的实际硬件卸载。然而,它确实看起来有问题,它在发送到另一个 VM 之前无法分段,并且就此而言不一致。我在其他地方看到这种行为是作为 VMware 错误提到的。
解决方案只是关闭目标服务器中的 TCP 分段卸载。该过程因操作系统而异,但 fwiw:
在 Windows 中,在连接属性的“常规”选项卡或“网络”选项卡上,单击适配器旁边的“配置...”,然后查看“高级”选项卡。对于 Server 2003 R2,它被指定为“IPv4 TCP 分段卸载”。对于 Server 2008 R2,它是“大型发送卸载 (IPv4)”。
这个解决方案有点麻烦,可以想象在某些环境中会影响性能,所以我仍然会接受任何更好的答案。
我有同样的症状,问题原来是这个内核错误:https ://bugs.debian.org/cgi-bin/bugreport.cgi?bug=754294
我在 Linux 主机上看到了同样的问题。
解决方案是在网关计算机的网络驱动程序 (vmxnet) 上停用大型接收卸载 (LRO)。
引用 VMware 知识库:
请参阅http://kb.vmware.com/kb/2055140
因此,到达网关机器的数据包被网络驱动程序合并并发送到网络堆栈,将它们丢弃为大于 MTU ...