對Netfilter中conntrack機制的理解


0x 01 狀態防火牆和鏈接追蹤系統

認識和熟悉過iptables之後,更加感嘆netfilter的磅礴和浩瀚。在netfilter體系中,狀態跟蹤機制(conntrack)是重要的一部分。它是基於Linux系統的stateful防火牆的基礎,也是NAT完成對相關包進行轉換的手段。本文嘗試對conntrack的機制進行分析和理解。

在基於header信息(IP,端口等)進行過濾的包過濾防火牆發展多年之後,對防火牆的需求也再逐漸豐富,stateless防火牆對探測跟蹤以及DoS的防護顯得力不從心。當然從歷史時間來看,包過濾防火牆是符合當時發展需要,效率也更高,對於更高層的應用當時都很少,自然也不需要太關注防護。

conntrack將信息存在內存結構中,包括IP,端口,協議類型,狀態以及超時時間。
而且conntrack不僅可以對TCP這種有狀態的會話進行狀態跟蹤,還可以對UDP進行狀態跟蹤。

conntrack本身並不會對包進行過濾,而是提供一種基於狀態和關係的過濾依據。


0x 02 五種狀態

conntrack定義了5種連接狀態,分別如下:

NEW:NEW匹配連接的第一個包。意思就是,iptables從連接跟蹤表中查到此包是某連接的第一個包。判斷此包是某連接的第一個包是依據conntrack當前”只看到一個方向數據包”( [UNREPLIED] ),不關聯特定協議,因此NEW並不單指tcp連接的SYN包。

ESTABLISHED ESTABLISHED匹配連接的響應包及後續的包。意思是,iptables從連接跟蹤表中查到此包是屬於一個已經收到響應的連接(即沒有 [UNREPLIED] 字段)。因此在iptables狀態中,只要發送並接到響應,連接就認爲是ESTABLISHED的了。這個特點使iptables可以控制由誰發起的連接纔可以通過,比如A與B通信,A發給B數據包屬於NEW狀態,B回覆給A的數據包就變爲ESTABLISHED狀態。ICMP的錯誤和重定向等信息包也被看作是ESTABLISHED,只要它們是我們所發出的信息的應答。

RELATED RELATED匹配那些屬於RELATED連接的包,這句話說了跟沒說一樣。RELATED狀態有點複雜,當一個連接與另一個已經是ESTABLISHED的連接有關時,這個連接就被認爲是RELATED。這意味着,一個連接要想成爲RELATED,必須首先有一個已經是ESTABLISHED的連接存在。這個ESTABLISHED連接再產生一個主連接之外的新連接,這個新連接就是RELATED狀態了,當然首先conntrack模塊要能”讀懂”它是RELATED。拿ftp來說,FTP數據傳輸連接就是RELATED與先前已創建的FTP控制連接,還有通過IRC的DCC連接。有了RELATED這個狀態,ICMP錯誤消息、FTP傳輸、DCC等才能穿過防火牆正常工作。有些依賴此機制的TCP協議和UDP協議非常複雜,他們的連接被封裝在其它的TCP或UDP包的數據部分(可以瞭解下overlay/vxlan/gre),這使得conntrack需要藉助其它輔助模塊才能正確”讀懂”這些複雜數據包,比如 nf_conntrack_ftp 這個輔助模塊。

INVALID INVALID匹配那些無法識別或沒有任何狀態的數據包。這可能是由於系統內存不足或收到不屬於任何已知連接的ICMP錯誤消息。一般情況下我們應該DROP此類狀態的包。

UNTRACKED UNTRACKED狀態比較簡單,它匹配那些帶有 NOTRACK 標籤的數據包。需要注意的一點是,如果你在 raw 表中對某些數據包設置有 NOTRACK 標籤,那上面的4種狀態將無法匹配這樣的數據包,因此你需要單獨考慮 NOTRACK 包的放行規則。


0x 03基礎結構

conntrack功能從2.6.15的內核版本後,就開始支持狀態跟蹤。該功能依賴於nf_conntrack模塊模塊,而該模塊基於ip_conn_track模塊。

nf_conntrack_ipv4在netfilter的hook點中共註冊了4個回調函數。這些回調函數在 nf_conntrack_core.c。這些回調函數可以分爲三類,連接跟蹤創建以及查詢、碎片整理、helpers。

conntrack通過一個hash表去實現高效查詢,表結構如下:
每個連接都存在兩個hash元組,一個代表是連接的入方向,一個是代表連接的出方向或者叫“reply”方向。

每個元組都包含了連接的相關信息,包括源目IP,以及四層信息。這些元組存在於一個hash元組中。這些結構被定義在nf_conntrack_tuple.h文件中。

Hash計算使用了隨機數去放大計算結果,從而避免數據包潛在的丟包風險。

conntrack結構


0x 04 conntrack創建以及查詢過程

nf_conntrack_in回調函數被註冊在PREROUTING的hook上,在包進入路由前,會對包的正確性進行檢查。每當收到一個包,都會檢查這個包是否與當前的某個conntrack相匹配。如果沒有與之匹配的conntrack,就會創建一個新的conntrack,這種機制被寫在resolve_normal_ct函數中。


0x 05 碎片處理

碎片處理的功能,被寫在了ipv4_conntrack_defrag的回調函數中,這個函數用來收集需要進行整理的碎片。在2.4的kernel中,需要整理的碎片是線性存在的,也就是說他們被複制進一個持續的內存中。不過這種方式的性能消耗比較大,從2.6的kernel開始,碎片不再被複制進線性空間,而是被放入list中。因此,所有的處理必須是可以感知碎片的。例如,如果我們需要一些存儲在TCP頭的信息,我們就需要先檢查這個報文頭是否是碎片;如果是碎片的話,只需要將需要的信息複製到棧中。


0x 06 helpers和期望

一些應用層的協議是很難跟蹤的,比如FTP協議的被動模式下,使用21號端口去從server獲得數據,但是使用1024-65535之間的端口去接收數據。雖然後者的端口是無法預測的,但是這兩個過程是相關聯的,並非是真正獨立的,這就需要防火牆去通過更多的信息去過濾這種包。

conntrack定義了一種被稱爲helpers的機制,通過這種機制我們就可以確定連接之間是否相關。這種機制被定義在nf_conntrack_core.h文件中的nf_conntrack_expect函數。

以FTP爲例,helpers去尋找用於回覆passive模式請求的端口類型,如果找到了,一個期望就會被創建,並被嵌入到期望的全局list中。conntrack與期望之間的關係如下圖:
conntrack與期望的關係

每個期望都有存活時間。如果一個conntrack創建後,沒有發現與其相匹配的期望,helper將會對這個連接進行再處理。如果與某個主conntrack的期望相匹配,我們就認爲這兩個連接是相關的。還是以FTP的被動模式爲例,21端口的鏈接就是主conntrack,通過大端口(1024-65535)的conntrack就是與其主conntrack的期望相符合的鏈接。


0x 07 參考

[1] Miguel Rio et al., “A Map of the Networking Code in Linux Kernel
2.4.20,” Technical Report DataTAG-2004-1, FP5/IST DataTAG Project,
2004
[2]Pablo Neira et al.,“Netfilter’s connection tracking system”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章