失序的FIN数据包和覆盖?

5

当我浏览一个开源代码库时,想到了一个有趣的场景。假设在成功建立TCP连接后,TCP客户端必须发送一个序列号为101的数据包。但它却发送了一个序列号为201的FIN。现在TCP服务器认为FIN是乱序的,并将其排队等待数据包到达。 我的问题是,如果服务器接收到序列号为101且长度为150的数据包,根据RFC,TCP终点应该采取什么行为?它会覆盖先前发送的FIN吗?还是将数据包裁剪到FIN序列号?或者它取决于TCP实现?


1
非常有趣的问题! - jweyrich
1个回答

4
根据RFC 793中的一些段落:
“3.如果连接处于同步状态(已建立、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK或TIME-WAIT),任何不可接受的报文段(窗口外序列号或不可接受的确认号)必须仅引发一个空的确认报文段,其中包含当前发送序列号和指示下一个应该接收的序列号的确认,并且连接保持在相同的状态。”
...
“处理传入报文段的一种自然方法是将其想象为首先测试是否具有正确的序列号(即它们的内容位于序列号空间中预期的“接收窗口”的范围内),然后按序列号顺序排队并处理。 当一个报文段与已经接收到的其他报文段重叠时,我们重构该报文段以仅包含新数据,并调整标头字段以保持一致。”
... 我的回复: 请记住,如果发生这种情况,那是因为客户端的TCP行为不良。不是由于乱序而是由于带有FIN标志的片段中出现了错误的序列。或者可能是攻击。 当TCP在服务器端接收到SEQ = 201的片段时,它将为其存储限定时间,并发送一个ACK,因为它正在等待该SEQ号码。然后,当SEQ = 101的片段到达时,接收端中的TCP在接收到SEQ = 101的片段后将具有新的接收窗口。从SEQ = 201的第一个到达的片段开始,它应该只获取超过251字节的数据(在我的测试中,而不是这样做,它从SEQ = 101的片段中删除了重叠的字节-这可能取决于实现),如果有的话,并接受FIN。接收端TCP将发送一个ACK。当服务器端关闭套接字时,接收端TCP将发送一个[FIN,ACK]片段。 为了测试它,我有一个客户端恰好做了您描述的事情(这是使用用户空间中的原始套接字完成的,无法使用TCP套接字模拟它。服务器是一个简单的nodejs服务器),发送了一个FIN片段,15秒钟后发送了先前的片段。服务器读取接收到的数据,并在10秒后关闭套接字。 这是tcpdump,您可以看到TCP服务器端的响应:
    [rodolk@localhost ~]$ sudo tcpdump -i p2p1 -vv tcp
tcpdump: listening on p2p1, link-type EN10MB (Ethernet), capture size 65535 bytes
19:33:03.648216 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.101.16345 > 192.168.56.1.webcache: Flags [S], cksum 0x5f49 (correct), seq 523645, win 500, length 0
19:33:03.649826 IP (tos 0x0, ttl 128, id 26590, offset 0, flags [DF], proto TCP (6), length 44)
    192.168.56.1.webcache > 192.168.56.101.16345: Flags [S.], cksum 0x1ac8 (correct), seq 1576251572, ack 523646, win 8192, options [mss 1460], length 0
19:33:03.651208 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.101.16345 > 192.168.56.1.webcache: Flags [.], cksum 0x5091 (correct), seq 1, ack 1, win 500, length 0
19:33:03.651567 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 74)
    192.168.56.101.16345 > 192.168.56.1.webcache: Flags [F.], cksum 0x8121 (correct), seq 122:156, ack 1, win 500, length 34
19:33:03.651891 IP (tos 0x0, ttl 128, id 26591, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.1.webcache > 192.168.56.101.16345: Flags [.], cksum 0x5314 (correct), seq 1, ack 1, win 65392, length 0
19:33:18.652083 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 171)
    192.168.56.101.16345 > 192.168.56.1.webcache: Flags [P.], cksum 0xf973 (correct), seq 1:132, ack 1, win 500, length 131
19:33:18.652834 IP (tos 0x0, ttl 128, id 26593, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.1.webcache > 192.168.56.101.16345: Flags [.], cksum 0x5313 (correct), seq 1, ack 157, win 65237, length 0
19:33:28.661041 IP (tos 0x0, ttl 128, id 26594, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.1.webcache > 192.168.56.101.16345: Flags [F.], cksum 0x5312 (correct), seq 1, ack 157, win 65237, length 0
19:33:28.961756 IP (tos 0x0, ttl 128, id 26595, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.56.1.webcache > 192.168.56.101.16345: Flags [F.], cksum 0x5312 (correct), seq 1, ack 157, win 65237, length 0

2
接收端TCP会在最后发送一个ACK。出站FIN是由应用程序引起的,而不是来自对入站FIN的确认。 - user207421
@EJP,我想你说得没错。我稍后再检查一下以确认,并编辑我的回复。 - rodolk
@EJP,我已按照您的评论进行了修改。谢谢。 - rodolk
当服务器端关闭套接字时,它会发送一个FIN而不是[FIN,ACK],除非有其他内容需要ACK。每一侧的关闭都是完全独立的。 - user207421
理论上,在TCP状态机中,应该只有一个FIN。同意。我想这取决于实现方式。如果你看到tcpdump,它总是发送ACK。我用nodejs TCP套接字服务器和http服务器进行了测试,总是看到[FIN,ACK]。当你嗅探任何流量时,即使没有额外的数据要ACK(ack号与先前的ACK相同),你也可以看到相同的情况。无论如何,这并没有任何区别。 - rodolk
@rodolk "...如果有的话,并接受FIN。" 这在OP提供的特定示例中是不正确的。到达较晚的序列号为101,长度为150的数据包将使接收方的窗口前进到RCV.UNA=261。由于FIN位于序列号201处,对于接收方来说现在已经太“旧”,因此将被丢弃。 - Phil Kang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接