服务器不发送 SYN/ACK 数据包以响应 SYN 数据包

jef*_*eff 6 linux web-server tcp

使用 iptraf、tcpdump 和 wireshark 我可以看到一个 SYN 数据包进入,但只有 ACK FLAG 设置在回复数据包中。

我正在使用内核 2.6.36 运行 Debian 5

我已经关闭了 window_scaling 和 tcp_timestamps、tcp_tw_recycle 和 tcp_tw_reuse:

cat /etc/sysctl.conf 



net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_timestamps = 0
Run Code Online (Sandbox Code Playgroud)

我附上了wireshark输出的图像。

http://imgur.com/pECG0.png

输出到 netstat

netstat -natu | grep '72.23.130.104'

tcp        0      0 97.107.134.212:18000    72.23.130.104:42905     SYN_RECV
Run Code Online (Sandbox Code Playgroud)

我一直在尽一切可能找到解决方案,但尚未找出问题所在,因此非常感谢任何帮助/建议。

更新 1:我已设置 tcp_syncookies = 0 并注意到我现在每 50 个 SYN 请求回复 1 个 SYN+ACK。尝试连接的主机大约每秒发送一次 SYN 请求。

PCAP文件

rad*_*ius 6

遇到同样的问题后,我终于找到了根本原因。

在 Linux 上,当套接字处于 TIME_WAIT 和新的 SYN 附加(对于同一对 ip/port src、ip/port dest)时,内核检查 SYN 的 SEQ 编号是否<或>比收到的最后一个 SEQ这个插座。

(PS:在此问题附带的wireshark 输出图像中,序列号显示为相对,如果您不将它们设置为绝对值,您将看不到问题。捕获也必须显示旧会话以能够比较 SEQ 编号)

  • 如果 SYN 的 SEQ 编号大于前一个数据包的 SEQ 编号,则创建一个新连接,一切正常
  • 如果 SYN 的 SEQ 编号小于前一个数据包的 SEQ 编号,内核将发送与前一个套接字相关的 ACK,因为内核认为收到的 SYN 是前一个套接字的延迟数据包。

行为是这样的,因为在 TCP 开始时,计算机生成的 SEQ 编号是增量的,几乎不可能收到一个 SEQ 编号 < 仍然在 TIME_WAIT 中的前一个套接字的 SEQ 编号。

计算机带宽的增加使这从几乎不可能变为罕见。但这里最重要的是,现在大多数系统使用随机 ISN(初始序列号)来提高安全性。因此,没有什么可以阻止新套接字的 SEQ 编号大于前一个套接字的 SEQ 编号。

每个操作系统都使用或多或少安全的不同算法来避免这个特定问题 http://www.bsdcan.org/2006/papers/ImprovingTCPIP.pdf很好地介绍了这个问题。

还有最后一件棘手的事情......所以内核将发送与旧会话相关的ACK,然后呢?客户端操作系统应该收到(前一个会话的)ACK,不理解它,因为对于客户端会话已关闭,发送 RST。当服务器收到此 RST 时,它将立即清除套接字(因此它不再处于 TIME_WAIT 中)。在他这边,客户端正在等待 SYN/ACK,因为它没有得到它,它将发送一个新的 SYN 。在此期间,RST 已经发送并且会话在服务器上被清除,所以这个辅助 SYN 将工作并且服务器将回复 SYN/ACK 等等。

所以正常的行为是连接应该可以工作,但会延迟一秒钟(直到发送辅助 SYN)。在 Jeff 的案例中,他在评论中说他使用 Fortinet 防火墙,这些防火墙(默认情况下)会丢弃与旧会话相关的 ACK(因为防火墙看不到与 ACK 相关的打开会话),因此客户端不会发送任何 RST 并且服务器无法从 TIME_WAIT 状态清除会话(当然在 TIME_WAIT 计时器结束时除外)。fortinet 上的“set anti-replay宽松”命令可以允许转发这个ACK包而不是丢弃。


sys*_*138 1

我之前第一次看到这种情况是因为出站和入站数据包在网络上采用不同的路由,并且入站线路上有一个状态连接跟踪设备。由于该设备(在我的例子中是负载平衡器,但它也可能很容易成为防火墙)从未看到初始 SYN,因此 SYN-ACK 被当作虚假的丢弃在地板上。