Skip to content

Latest commit

 

History

History
42 lines (25 loc) · 5.09 KB

TCP序列号回绕与解决.md

File metadata and controls

42 lines (25 loc) · 5.09 KB

在说明TCP头部中的时间戳选项与防回绕有什么关系之前,我们先来研究一下什么叫做回绕序列号。

在TCP中,两端建立连接之前,会分别各自选择一个初始序列号。初始序列号会随着时间而改变,因此每一个连接都拥有不同的初始序列号。[RFC0793]指出,初始序列号可以视为一个32位的计数器,这个计数器的数值每4微妙加一。

这其中就会存在回绕的问题。想象一下,如果序列号每次交换都会自增1,而连接又长时间存在且高速交换报文时,总有一个时刻序列号的长度会超过32位(2^32 - 1),此时序列号就会重置为0,此时的序列号就会从0重新开始自增1。这就是TCP序列号回绕问题。

当然,序列号回绕真正导致bug的问题概率很低。但是一旦因为序列号回绕出现问题,那么这样的问题几乎是所有网络测试工程师的噩梦:偶发、无规律并且无法稳定重现。

下面来研究一下序列号回绕可能会导致什么样的问题。

发送时间 发送序列号 接收方
A N 报文完好
B N+1 报文完好
。。。 。。。 报文完好
。。。 。。。 报文完好
C N 报文完好
D N+1 报文完好

假设上表是两个主机之前通讯的可能数据流。可以看到在B时刻和C时刻之间,序列号发生了回绕。

如果数据包全部完好无损的顺序到达,那么这样的数据流将没有任何问题,可以正常维持服务。

但是我们来做一个假设,假设在B时刻,有一个报文段延迟。这时接收方由于迟迟收不到所需的报文段,要求发送方重传。这一次的重传反而没有问题,延迟的报文段被重新发送了一份,正常由接收方接收。如果接下来没有什么问题,比如这个延迟的报文段自此丢失,那整个数据流也不会受到影响。

但是我们再做一个假设,如果这个B时刻延迟的报文段,在D时刻到达了。如果这个报文段丢失与重新出现的时间(也就是在网络中跋涉的时间)还很幸运的小于一个报文段在网络中存在的最大时间(MSL)的限制,那么服务端就能正常接收到这个报文段。

这下接收端懵逼了,幸福来得太突然,一下子来了两个报文段,还都拿着一样的序列号,不知道要选谁好了。这就是回绕序列号结合报文段延迟导致的重复报文段问题。这样的问题只会发生在相对高速的连接中。

问题发生了就要解决,那么如果解决这个问题呢?这就要用到TCP头部中的额外选项——时间戳选项了。当使用时间戳选项时,发送方将一个32位的数值填充到时间戳数值字段(称作TSV或TSval)作为时间戳选项的第一个部分;而接收方则将收到的时间戳数值原封不动的填充至第二部分的时间戳回显重试字段(称作TSER或TSecr),如果这个报文属于客户端SYN握手包,那么由于不知道服务端时间,第二部分的时间戳回显重试字段将被置为0。由于包含了时间戳选项,TCP头部的长度将会增长10字节(8个字节用于保存2个时间戳数值,而另两个字节用于指明选项的数值与长度)。

[RFC1323]推荐发送方如果启用时间戳选项,那么每秒钟至少将时间戳选项加1。

刚刚我们说到的报文段延迟并且恰好碰上序列号回绕导致接收端同时收到两个序列号一致的报文段的问题,就可以通过时间戳选项来解决。如果真的出现这样序列号相同的报文段,那么直接丢弃掉与最近接收到的报文段时间戳相差最远的那个报文段即可。

时间戳选项广义上可以解决所有由于序列号一致而造成的报文段重复问题,比如比回绕序列号更容易出现的重置序列号问题。由于TCP实质上是通过一对端点,其中包括2个IP地址与2个端口号构成的四元组所唯一标识,因此即使是同一个四元组连接也会出现不同的实例。而不同的实例之间有更大的可能出现序列号重复的问题。

比如一个短连接总是由客户端的80端口链接至服务端的443端口,这样的短连接可能在一天之内断开又重连上千次。那么极有可能出现某个连接由于某个报文段的长时间延迟而被关闭,然后又以相同的四元组被重新打开,延迟的报文段又会被视为有效数据重新进入新连接的数据流中。如果恰好此时报文段的序列号符合预期,那么接收端又会陷入迷惑之中。开启时间戳选项之后,这样的问题将会得到避免。

当然,时间戳选项的真正作用并不是为了解决序列号重复问题而产生的,避免接收旧报文段与判断报文段正确性只不过是时间戳选项带来的额外好处。借助时间戳选项,我们能够获得报文往返时间大量样本,从而的相对精确的估算连接中报文的往返时间。

这,与重传计时器的设置紧密相关。