tcp序列号回绕与解决

问题描述

tcp协议头中有seq和ack_seq两个字段,分别代表序列号和确认号。tcp协议通过序列号标识发送的报文段。seq的类型是__u32,当超过__u32的最大值时,会回绕到0。

一个tcp流的初始序列号(ISN)并不是从0开始的,而是采用一定的随机算法产生的,因此ISN可能很大(比如(2^32-10)),因此同一个tcp流的seq号可能会回绕到0。而我们tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕(sequence wraparound)问题。

内核解决办法

内核中给出的序列号(解决序列号回绕问题)判断解决方案十分简洁:

点击(此处)折叠或打开

  1. /*
  2. * The next routines deal with comparing 32 bit unsigned ints
  3. * and worry about wraparound (automatic with unsigned arithmetic).
  4. */
  5.  
  6. static inline int before(__u32 seq1, __u32 seq2)
  7. {
  8. return (__s32)(seq1-seq2) < 0;
  9. }
  10. #define after(seq2, seq1) before(seq1, seq2)

原理

为什么(__s32)(seq1-seq2)<0就可以判断seq1<seq2呢?这里的__s32是有符号整型的意思,而__u32则是无符号整型。< style="-ms-word-wrap: break-word;" div="">

为了方便说明,我们以unsigned char和char为例来说明:

假设seq1=255, seq2=1(发生了回绕)。

seq1 = 1111 1111 seq2 = 0000 0001

我们希望比较结果是seq1<seq2< style="-ms-word-wrap: break-word;" div="">

 seq1 - seq2=

 1111 1111

-0000 0001

-----------

 1111 1110

由于我们将结果转化成了有符号数,由于最高位是1,因此结果是一个负数,负数的绝对值为

 0000 0001 + 1 = 0000 0010 = 2

因此seq1 - seq2 < 0

注意:

如果seq2=128的话,我们会发现:

 seq1 - seq2=

 1111 1111

-1000 0000

-----------

 0111 1111

此时结果尤为正了,判断的结果是seq1>seq2。因此,上述算法正确的前提是,回绕后的增量小于2^(n-1)-1。

由于tcp序列号用的32位无符号数,因此可以支持的回绕幅度是2^31-1,满足要求了。

转自:https://blog.csdn.net/hi_software/article/details/51768026

猜你喜欢

转载自blog.csdn.net/liufuchun111/article/details/86301587