1 引言
TCP/IP 协议目前已经被广泛的被应用,数据在网络上应用 IP 协议进行传输的时候,可能将数据分成多个数据包。对于UDP报文,超过MTU将会被IP分片,而TCP则不用考虑IP分片问题,TCP连接双方会相互通告MSS(最大报文段长度),MSS肯定是<=网络层的最大路径MTU,然后TCP数据拆分成多段通过网络层发送,当服务器端传输层接收到数据之后进行TCP重组。
目前在网络安全领域都将用到 TCP 会话的重组问题。只有将数据包重组以后,才能还原一次完整的 TCP 会话。由于网络问题,数据包可能会经过不同的路由传输到目的地,并且到达目的地的数据包可能顺序会发生改变。在传输过程中,协议对数据的传输进行控制,对在传输过程中丢失的数据包协议将控制系统将丢失的数据包重新传送。乱序、重传、数据重叠,这些都是 TCP 会话在重组的时候将遇到的问题。
附:
关于MTU以及IP分片知识:https://www.cnblogs.com/realjimmy/p/12920701.html
TCP三次握手四次挥手,以及序列号计算知识:https://www.cnblogs.com/realjimmy/p/12920701.html
2 TCP会话重组
序列号是为了保证 TCP 数据包的按顺序传输来设计的,可以有效的实现 TCP 数据的完整传输,特别是在数据传送过程中出现错误的时候可以有效的进行错误修正。在TCP会话的重新组合过程中我们需要按照数据包的序列号对接收到的数据包进行排序。这部分的知识见引言中给得链接,这里不做过多解释。
2.1 BSD中的实现
参照TCP/IP详解第二卷24~29章,详细论述了TCP协议的实现。这只是一种思路,对于报文有实时要求的场景,比如需要发送RST报文阻断会话,如果等到FIN才完成重组,肯定会阻断失败。本章节根据网上资料整理,我还未看完TCP/IP详解卷二,实现如有出入请留言。
实现里涉及两个队列,队列1 存放顺序到来的数据包,队列2 存放失序到来的数据包。
假设队里1 里最后一个数据包seq=100,len=100,下一个数据包可能有以下多种情况:
1)顺序到来的数据包
数据包②的seq2 = seq1+len1
由此数据包的seq可知,这个报文是①报文的预期后续报文,将此报文追加到正常报文队列。
2)重复数据包
③ ④ ⑤ 都包含在①之中,应该被丢弃。
3)重叠数据包
⑥ 的前部分150~199与①重叠,而后部分100~199则是新数据,此时应该对这个报文作如下处理:
- 计算重复字节数
(seq1+len1) - Seq2= 100+100-150 = 50,即这个报文段前50个字节是重复的。这部分需要丢弃。
- 截取报文段新数据,即只保留字节序号段200~249
- 重新设置这个报文段的 seq = 200
- 重新设置这个报文段的数据长度len2 = 50
- 将重新设置后报文段加入顺序队列
4)提前到达的报文
数据包⑦ seq2>seq1+len1,是提前到来的报文,此时应该将这个报文放置到失序报文队列存储起来,以备后续重组使用。
这样直到tcp断开这个socket的链接(FIN=1),此时将正常报文队列和失序报文队列中的数据合并起来,完成重组。取出正常报文队列最后一个报文的seq和len,在失序报文队列中查找属于它的后续报文,该报文是否可以作为正常报文队列的后续报文,处理过程同前面步骤的分析。
2.2 其他TCP重组的实现
TCP会话重组研究:http://www.doc88.com/p-5055897205253.html
Snort、Suricata、linux内核里也都有TCP重组的实现,有空我会完善此文。