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重組的實現,有空我會完善此文。