使用 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
我已附加 wireshark 输出的图像。
https://i.stack.imgur.com/QIikJ.jpg.png
输出到 netstat
netstat -natu | grep '72.23.130.104'
tcp 0 0 97.107.134.212:18000 72.23.130.104:42905 SYN_RECV
我一直在尽一切努力寻找解决方案,但尚未解决问题,因此非常感谢任何帮助/建议。
更新 1:我已设置 tcp_syncookies = 0,并注意到我现在每 50 个 SYN 请求回复 1 个 SYN+ACK。尝试连接的主机大约每秒发送一次 SYN 请求。
答案1
遇到同样的问题后,我终于找到了根本原因。
在 Linux 上,当套接字处于 TIME_WAIT 状态并且附加新的 SYN(对于同一对 ip/port src、ip/port dest)时,内核会检查 SYN 的 SEQ 号是否小于或大于此套接字收到的最后一个 SEQ 号。
(PS:在本问题附带的 wireshark 输出图像中,序列号显示为相对的,如果您不将它们设置为绝对的,您将无法看到该问题。捕获还必须显示旧会话才能比较序列号)
- 如果 SYN 的 SEQ 号大于前一个数据包的 SEQ 号,则建立新连接并且一切正常
- 如果 SYN 的 SEQ 号小于前一个数据包的 SEQ 号,则内核将发送与前一个套接字相关的 ACK,因为内核认为收到的 SYN 是前一个套接字的延迟数据包。
行为如此是因为在 TCP 开始时计算机生成的 SEQ 号是递增的,几乎不可能接收到小于仍处于 TIME_WAIT 状态的前一个套接字的 SEQ 号的 SEQ 号。
计算机带宽的增加使这种情况从几乎不可能变为罕见。但这里最重要的是,现在大多数系统使用随机 ISN(初始 SEQ 号)来提高安全性。因此没有什么可以阻止新套接字的 SEQ 号 a > 前一个套接字的 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 loose”命令可以允许转发此 ACK 数据包而不是丢弃它。
答案2
看来 97.107.134.212 已经相信存在连接(72.23.130.104:42905, 97.107.134.212:18000)
。
当 72.23.130.104:42905 发送其 SYN 数据包时,其序列号为 246811966。接下来应该是一个具有其自己的 SEQ 号且 ACK 值为 246811967 的 SYN/ACK 数据包。
但它发送了带有 SEQ=1736793629 和 ACK=172352206 的 ACK。这些值可能是来自较早连接的值。
任何新的连接尝试都应来自不同的端口号... 发生这种情况了吗?Wireshark 在 pkt#11 中指出了这一点:“TCP 端口号重用”。
看起来问题出在发送者身上。
FWIW,我可以连接只是美好的:
1 0.000000 192.168.0.135 97.107.134.212 TCP 45883 > biimenu [SYN] Seq=809402803 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSV=2319725 TSER=0 WS=7
2 0.022525 97.107.134.212 192.168.0.135 TCP biimenu > 45883 [SYN, ACK] Seq=4293896301 Ack=809402804 Win=14600 Len=0 MSS=1360 SACK_PERM=1
3 0.022553 192.168.0.135 97.107.134.212 TCP 45883 > biimenu [ACK] Seq=809402804 Ack=4293896302 Win=14600 Len=0
答案3
我以前见过这种情况,是因为出站和入站数据包在网络上采用不同的路由,而入站线路上有一个状态连接跟踪设备。由于该设备(在我的情况下是负载平衡器,但它也可能是防火墙)从未看到初始 SYN,因此 SYN-ACK 被当作伪造的数据包丢弃。
答案4
我会检查几件事:
您的主机是否是多宿主的(例如,您是否有多个以太网接口?) - 如果是,您的路由可能会混乱。测试此问题的最简单方法是禁用您的辅助接口,然后查看问题是否消失。
要检查的另一件事是 iptables(或其他防火墙)是否已启用。service iptables stop 将关闭它直到下次重新启动 - 如果这解决了问题,那么您需要调整 iptables 设置。
此外,如果您在接口上启用了 IPv6,有时会有通过 ipv4 而不是 ipv6 的路由。当发生这种情况时,如果 ipv6 路由是“默认”,您的数据包可能会通过错误的地址(即使在正确的接口上)。尝试禁用 ipv6 以查看是否是这个问题。