本文的IP、TCP重组过程完全参考libnids中重组过程而来,模拟Linux 2.0.x内核中TCP/IP协议栈重组操作。
1、IP碎片重组过程示例
图 1.1
图1.1所示的为正常的三个分片,offset表示距离原始数据的偏移,offset==0表示第一个分片;end代表offset+datalen的大小,指向下一个分片的起始位置;offset与end共同决定着分片的长度以及该片段所在位置。
图 1.2
图1.2中为IP的四个分片,各分片到达顺序依次为1、2、3、4。碎片1、2、3到达后会根据offset值大小按序添加进碎片队列中,此时碎片4到来后的处理:
- 在碎片队列中找到正确位置: prev->end = 2000; next->offset = 2300;
- 与前一个prev比较是否存在重叠:(offset < prev->end) => (1900 < 2000) 出现重叠,修改碎片4的offset值:offset +=prev->end - offset; => offset = 2000;
- 与后面顺序分片进行重叠比较:(next->offset < end)=> (2300 < 3300) 出现重叠,修改后面分片的len和offset值:next->len -= end - next->offset; next->offset += end - next->offset; 对于碎片2来说,(next->len = -500) <= 0,完全重叠,所以碎片2需要从碎片队列中删除;对于碎片3而言:(next->offset < end)=> (3200 < 3300)出现重叠,(next->len = 700) > 0; 部分重叠! next->offset = 3300; 因此最终重组后的结果如图1.3所示:
碎片1:offset=600, len=1400; 碎片4:offset=2000, len=1300; 碎片3:3300, len=700;
图1.3
2、TCP重组过程示例—包失序
图 2.1
图2.1所示的为TCP报文段正常顺序,不考虑包失序的情况。橙色代表客户端发送给服务器的报文段,绿色代表服务器发送给客户端的报文段。
下面两个图表示经网络传输后出现包失序情况的处理,两个方向上的数据包的缓存和提交(给应用程序)。
图 2.2
图2.2中包1是0方向上的正常顺序,直接提交给应用程序,包3为1方向上的乱序包需要缓存,等待包2的到来;包2到来后将包2、3进行依次提交,包5到来后0方向上期望的是包4,因此包5将进行缓存,等待包4到来后提交包4、5给上层;1方向上期望的包6,所以包7、8将进行缓存,等待包6到来,最后进行包6、7、8提交。
图 2.3
图2.3中的分析同图2.2中一样,需要强调的是:两个方向上的报文段分别进行重组。
所以最后提交给应用程序的包的顺序为【1-4-5-2-3-6-7-8 】 不一定和原始正常顺序一致,但每个方向上顺序是一致的!
3、TCP重组过程示例—包重复
图 3.1
图3.1表示两个正常的报文段,当报文段2还没到来时,Seq2就已经确定为101,该序列号代表期望从这里开始接收的序列号。
图 3.2
图3.2表示报文段2的三种可能出现的情况:
- 包失序情况的处理,前面已经介绍过了,需要进行缓存;
- 出现包完全重复的情况,直接丢弃该报文段即可;
- 出现包部分重复的情况处理: (EXP_SEQ是目前已集齐的数据序列号,我们希望收到从这里开始的数据)
重复字节数:lost = EXP_SEQ - Seq2 重复报文段提交数据范围:(Seq2 + lost) ~ (len - lost)
如图3.3所示,上述出现的三种情况按顺序执行的结果为:
报文段1(Seq1=1, len=100)、 部分重复(Seq2=101, len=90)、 失序报文段(Seq3=191, len=30)
图 3.3