Linux連接跟蹤Conntrack:原理、應(yīng)用與內(nèi)核實現(xiàn)
從我們?nèi)粘J褂玫氖謾C APP,到企業(yè)復(fù)雜的網(wǎng)絡(luò)架構(gòu),連接跟蹤(conntrack)都在背后默默發(fā)揮著關(guān)鍵作用。它就像是網(wǎng)絡(luò)世界的 “大管家”,幫助眾多網(wǎng)絡(luò)應(yīng)用維持著高效、穩(wěn)定的運行。以 Kubernetes Service 為例,在容器編排的復(fù)雜環(huán)境中,Kubernetes 需要將外部的網(wǎng)絡(luò)請求準(zhǔn)確無誤地轉(zhuǎn)發(fā)到對應(yīng)的容器服務(wù)上。這一過程中,連接跟蹤就像是導(dǎo)航儀,它記錄著每個服務(wù)的連接狀態(tài),確保請求能夠被正確路由,讓各個容器服務(wù)之間的通信有條不紊地進行 。
再看 Docker network,當(dāng)我們在容器中運行各種應(yīng)用時,容器之間以及容器與外部網(wǎng)絡(luò)之間的通信管理十分重要。連接跟蹤能夠精準(zhǔn)地識別和跟蹤這些連接,讓容器里的應(yīng)用如同在一個有序的網(wǎng)絡(luò)社區(qū)中,彼此可以順暢交流,互不干擾。還有 iptables 主機防火墻,它守護著主機網(wǎng)絡(luò)的安全。
連接跟蹤則為它提供了關(guān)鍵信息,幫助防火墻判斷哪些連接是合法的,哪些可能存在風(fēng)險,從而對網(wǎng)絡(luò)流量進行有效的過濾和控制,保障主機的網(wǎng)絡(luò)安全。可以說,連接跟蹤就像網(wǎng)絡(luò)世界的基石,雖然平時不太容易被我們察覺,但一旦缺失,許多網(wǎng)絡(luò)應(yīng)用都將陷入混亂。接下來,讓我們深入了解連接跟蹤的原理、應(yīng)用以及它在 Linux 內(nèi)核中的實現(xiàn)機制,揭開它神秘的面紗。
Part1.連接跟蹤(Conntrack)概述
顧名思義,連接跟蹤是保存連接狀態(tài)的一種機制。為什么要保存連接狀態(tài)呢? 舉個例子,當(dāng)你通過瀏覽器訪問一個網(wǎng)站(連接網(wǎng)站的80端口)時,預(yù)期會收到服務(wù)器發(fā)送的源端口為80的報文回應(yīng),防火墻自然應(yīng)該放行這些回應(yīng)報文。那是不是所有源端口為80端口的報文都應(yīng)該放行呢?顯然不是,我們只應(yīng)該放行源IP為服務(wù)器地址,源端口為80的報文,而應(yīng)該阻止源地址不符的報文,即使它的源端口也是80??偨Y(jié)一下這種情況就是,我們只應(yīng)該讓主動發(fā)起的連接產(chǎn)生的雙向報文通過。
圖片
另一個例子是NAT。我們可以使用iptables配置nat表進行地址或者端口轉(zhuǎn)換的規(guī)則。如果每一個報文都去查詢規(guī)則,這樣效率太低了,因為同一個連接的轉(zhuǎn)換方式是不變的!連接跟蹤提供了一種緩存解決方案:當(dāng)一條連接的第一個數(shù)據(jù)包通過時查詢nat表時,連接跟蹤將轉(zhuǎn)換方法保存下來,后續(xù)的報文只需要根據(jù)連接跟蹤里保存的轉(zhuǎn)換方法就可以了。
1.1連接跟蹤發(fā)生在哪里?
連接跟蹤需要拿到報文的第一手資料,因此它們的入口是以高優(yōu)先級存在于LOCAL_OUT(本機發(fā)送)和PRE_ROUTING(報文接收)這兩個鏈。
既然有入口,自然就有出口。連接跟蹤采用的方案是在入口記錄,在出口確認(rèn)(confirm)。以IPv4為例:
圖片
當(dāng)連接的第一個skb通過入口時,連接跟蹤會將連接跟蹤信息保存在skb->nfctinfo,而在出口處,連接跟蹤會從skb上取下連接跟蹤信息,保存在自己的hash表中。當(dāng)然,如果這個數(shù)據(jù)包在中途其他HOOK點被丟棄了,也就不存在最后的confirm過程了。
1.2連接跟蹤信息是什么?
連接跟蹤信息會在入口處進行計算,保存在skb上,信息具體包括tuple信息(地址、端口、協(xié)議號等)、擴展信息以及各協(xié)議的私有信息。
圖片
- tuple信息包括發(fā)送和接收兩個方向,對TCP和UDP來說,是IP加Port;對ICMP來說是IP加Type和Code,等等;
 - 擴展信息比較復(fù)雜,本文暫時略過;
 - 各協(xié)議的私有信息,比如對TCP就是序號、重傳次數(shù)、縮放因子等。
 
報文的連接跟蹤狀態(tài)
途徑Netfilter框架的每一個報文總是會在入口處(PRE ROUTING或者LOCAL OUT)被賦予一個連接跟蹤狀態(tài)。這個狀態(tài)存儲在skb->nfctinfo,有以下常見的取值:
- IP_CT_ESTABLISHED:這是一個屬于已經(jīng)建立連接的報文,Netfilter目擊過兩個方向都互通過報文了
 - IP_CT_RELATED:這個狀態(tài)的報文所處的連接與另一個IP_CT_ESTABLISHED狀態(tài)的連接是有聯(lián)系的。比如典型的ftp,ftp-data的連接就是ftp-control派生出來的,它就是RELATED狀態(tài)
 - IP_CT_NEW:這是連接的第一個包,常見的就是TCP中的SYN包,UDP、ICMP中第一個包,
 - IP_CT_ESTABLISHED + IP_CT_IS_REPLY:與IP_CT_ESTABLISHED類似,但是是在回復(fù)方向
 - IP_CT_RELATED + IP_CT_IS_REPLY:與IP_CT_RELATED類似,但是是在回復(fù)方向
 
Part2.連接跟蹤(Conntrack)原理剖析
2.1 底層工作機制
連接跟蹤的底層工作機制,就像是一場精心編排的 “數(shù)據(jù)包處理交響樂”,每一個環(huán)節(jié)都緊密相扣,有條不紊。
當(dāng)數(shù)據(jù)包進入網(wǎng)絡(luò)節(jié)點時,連接跟蹤系統(tǒng)就像一個敏銳的 “攔截者”,迅速對其進行攔截。它會仔細(xì)分析數(shù)據(jù)包的頭部信息,從中提取出關(guān)鍵的五元組信息,即源 IP 地址、目的 IP 地址、源端口、目的端口和協(xié)議類型。這些信息就像是數(shù)據(jù)包的 “身份密碼”,連接跟蹤系統(tǒng)憑借它們來判斷這個數(shù)據(jù)包是否屬于一個已有的連接,或者是否需要建立一個新的連接。
一旦識別出數(shù)據(jù)包的 “身份”,連接跟蹤系統(tǒng)就會開始建立連接追蹤記錄。如果這個數(shù)據(jù)包是一個新連接的開始,比如 TCP 協(xié)議中的 SYN 包,連接跟蹤系統(tǒng)會在連接跟蹤表中創(chuàng)建一個新的記錄項。這個記錄項就像是一個新的 “檔案”,記錄著這個連接的初始狀態(tài)和相關(guān)信息。在這個 “檔案” 里,會包含連接的五元組信息、創(chuàng)建時間、當(dāng)前狀態(tài)等內(nèi)容,為后續(xù)對這個連接的跟蹤和管理提供了基礎(chǔ) 。
隨著數(shù)據(jù)的傳輸,連接跟蹤系統(tǒng)會持續(xù)關(guān)注連接的狀態(tài)變化。當(dāng)接收到屬于已有連接的數(shù)據(jù)包時,它會像一個嚴(yán)謹(jǐn)?shù)?“檔案管理員”,及時更新連接記錄中的各種統(tǒng)計信息,比如收發(fā)包數(shù)、字節(jié)數(shù)等。同時,它還會根據(jù)數(shù)據(jù)包的類型和內(nèi)容,更新連接的狀態(tài)。例如,在 TCP 連接中,當(dāng)接收到 SYN + ACK 包時,連接狀態(tài)就會從 “SYN_SENT” 更新為 “ESTABLISHED”,標(biāo)志著連接已經(jīng)成功建立 。
當(dāng)連接結(jié)束時,無論是正常的結(jié)束,還是因為超時、錯誤等原因?qū)е碌漠惓=Y(jié)束,連接跟蹤系統(tǒng)都會及時刪除連接記錄。它會從連接跟蹤表中移除對應(yīng)的記錄項,釋放相關(guān)的系統(tǒng)資源,就像清理不再使用的 “檔案” 一樣,確保連接跟蹤表的高效運行,為新的連接記錄騰出空間 。
2.2與 Netfilter 的淵源
在 Linux 內(nèi)核的網(wǎng)絡(luò)世界里,Netfilter 就像是一個強大的 “交通樞紐”,而連接跟蹤則是這個 “交通樞紐” 中不可或缺的一部分。Netfilter 是 Linux 內(nèi)核中一個對數(shù)據(jù)包進行控制、修改和過濾的框架,它在內(nèi)核協(xié)議棧中精心設(shè)置了若干 hook 點,這些 hook 點就像是交通要道上的 “檢查站”,所有流經(jīng)的數(shù)據(jù)包都必須在這里接受檢查和處理 。
連接跟蹤正是借助 Netfilter 的 hook 點來實現(xiàn)其功能的。當(dāng)數(shù)據(jù)包到達 Netfilter 設(shè)置的 hook 點時,連接跟蹤模塊就會被觸發(fā)。它會對數(shù)據(jù)包進行分析和處理,提取出連接相關(guān)的信息,并將這些信息記錄到連接跟蹤表中。在 NF_INET_PRE_ROUTING 這個 hook 點,連接跟蹤模塊會對進入系統(tǒng)的數(shù)據(jù)包進行檢查,判斷是否需要建立新的連接跟蹤記錄;而在 NF_INET_POST_ROUTING 這個 hook 點,它會對離開系統(tǒng)的數(shù)據(jù)包進行處理,更新連接的狀態(tài)信息 。
不過,隨著技術(shù)的不斷發(fā)展,云原生網(wǎng)絡(luò)方案 Cilium 帶來了一種全新的思路。Cilium 在 1.7.4 + 版本中,基于 BPF hook 實現(xiàn)了一套獨立的連接跟蹤和 NAT 機制。BPF hook 就像是 Cilium 自己搭建的 “檢查站”,它能夠?qū)崿F(xiàn)與 Netfilter 中 hook 機制類似的數(shù)據(jù)包攔截功能 。
在這個基礎(chǔ)上,Cilium 構(gòu)建了一套全新的連接跟蹤和 NAT 系統(tǒng),這使得它即便在卸載 Netfilter 的情況下,也能正常支持 Kubernetes 的 ClusterIP、NodePort、ExternalIPs 和 LoadBalancer 等功能 。由于這套連接跟蹤機制是獨立于 Netfilter 的,它的連接跟蹤和 NAT 信息不會存儲在內(nèi)核中 Netfilter 的連接跟蹤表和 NAT 表中,而是有自己獨立的存儲和管理方式,這也為網(wǎng)絡(luò)管理帶來了更多的靈活性和創(chuàng)新性 。
Part3.連接跟蹤(Conntrack)的多元應(yīng)用
3.1 在 NAT 中的關(guān)鍵角色
NAT,全稱 Network Address Translation,即網(wǎng)絡(luò)地址轉(zhuǎn)換,它就像是網(wǎng)絡(luò)世界里的 “地址翻譯官”。在 IPv4 地址資源日益緊張的今天,NAT 技術(shù)應(yīng)運而生,它允許一個機構(gòu)或網(wǎng)絡(luò)以一個公用 IP 地址出現(xiàn)在 Internet 上 。簡單來說,它能夠把內(nèi)部私有網(wǎng)絡(luò)地址翻譯成合法網(wǎng)絡(luò) IP 地址 。
在一個企業(yè)內(nèi)部網(wǎng)絡(luò)中,眾多員工的電腦都分配有私有 IP 地址,如 192.168.1.x 段的地址。當(dāng)這些電腦需要訪問互聯(lián)網(wǎng)時,NAT 設(shè)備就會發(fā)揮作用。它會將數(shù)據(jù)包中的源 IP 地址(私有 IP)轉(zhuǎn)換為一個合法的公網(wǎng) IP 地址,然后將數(shù)據(jù)包發(fā)送出去。當(dāng)外部服務(wù)器返回響應(yīng)數(shù)據(jù)包時,NAT 設(shè)備又能根據(jù)之前的轉(zhuǎn)換記錄,把目的 IP 地址(公網(wǎng) IP)轉(zhuǎn)換回對應(yīng)的私有 IP 地址,確保數(shù)據(jù)包能準(zhǔn)確無誤地回到員工的電腦上 。
在這個過程中,連接跟蹤起著至關(guān)重要的輔助作用。當(dāng)NAT設(shè)備進行地址轉(zhuǎn)換時,連接跟蹤系統(tǒng)會記錄下每個連接的相關(guān)信息,包括源IP、目的 IP、源端口、目的端口以及協(xié)議類型等 。這些信息就像是一個個 “連接檔案”,當(dāng)后續(xù)的數(shù)據(jù)包到來時,NAT設(shè)備可以根據(jù)連接跟蹤表中的記錄,快速準(zhǔn)確地進行地址轉(zhuǎn)換,確保通信的順暢 。而且,對于那些經(jīng)過NAT轉(zhuǎn)換的連接,連接跟蹤系統(tǒng)能夠識別出回復(fù)報文,自動完成反向轉(zhuǎn)換,無需額外添加規(guī)則,大大提高了網(wǎng)絡(luò)通信的效率 。
3.2 助力狀態(tài)包過濾
狀態(tài)包過濾,是一種比傳統(tǒng)包過濾更為智能和高效的網(wǎng)絡(luò)安全技術(shù) 。傳統(tǒng)包過濾只基于每個數(shù)據(jù)包的源和目的地址以及端口號進行檢查,對每個數(shù)據(jù)包獨立處理,就像是一個只看表面信息的 “檢查員” 。而狀態(tài)包過濾則像是一個經(jīng)驗豐富的 “偵探”,它會根據(jù)網(wǎng)絡(luò)連接的狀態(tài),對數(shù)據(jù)包進行過濾和管理 。
狀態(tài)包過濾器能夠檢測網(wǎng)絡(luò)連接的狀態(tài),比如TCP連接的三次握手過程,它都了如指掌 。當(dāng)一個TCP SYN包到達時,狀態(tài)包過濾器會知道這是一個新連接的開始;當(dāng)后續(xù)的SYN + ACK 包和 ACK包到來時,它能根據(jù)之前的記錄,判斷這些數(shù)據(jù)包是否屬于同一個連接 。通過這種方式,狀態(tài)包過濾器可以判斷是否允許特定的數(shù)據(jù)包通過網(wǎng)絡(luò),從而有效地防范會話劫持、拒絕服務(wù)攻擊等網(wǎng)絡(luò)安全威脅 。
連接跟蹤為狀態(tài)包過濾提供了關(guān)鍵的連接狀態(tài)信息。連接跟蹤系統(tǒng)會維護一個連接狀態(tài)表,記錄著每個連接的詳細(xì)信息,包括連接的建立、數(shù)據(jù)傳輸以及關(guān)閉等各個階段 。狀態(tài)包過濾器在處理數(shù)據(jù)包時,會參考連接跟蹤表中的信息,判斷該數(shù)據(jù)包是否屬于一個已建立的合法連接 。如果是,就允許數(shù)據(jù)包通過;如果不是,就會根據(jù)安全策略進行處理,比如丟棄數(shù)據(jù)包 。在一個企業(yè)網(wǎng)絡(luò)中,連接跟蹤表記錄著內(nèi)部員工與外部服務(wù)器建立的 HTTP 連接信息。當(dāng)外部服務(wù)器返回的 HTTP 響應(yīng)數(shù)據(jù)包到達時,狀態(tài)包過濾器會根據(jù)連接跟蹤表,確認(rèn)該數(shù)據(jù)包屬于合法連接,從而允許其進入企業(yè)內(nèi)部網(wǎng)絡(luò) 。
3.3 應(yīng)用層網(wǎng)關(guān)擴展
在網(wǎng)絡(luò)協(xié)議的世界里,大多數(shù)協(xié)議在 IP 層和傳輸層頭部進行轉(zhuǎn)換處理就可以正常工作,但有些應(yīng)用層協(xié)議比較特殊,它們在協(xié)議數(shù)據(jù)報文中包含了地址信息 。為了讓這些應(yīng)用也能順利完成 NAT 轉(zhuǎn)換,就需要連接跟蹤通過擴展組件來跟蹤應(yīng)用層協(xié)議,這就是應(yīng)用層網(wǎng)關(guān)(ALG)技術(shù) 。
以 FTP 協(xié)議為例,它在數(shù)據(jù)傳輸過程中會包含地址和端口信息。當(dāng)一個 FTP 客戶端連接到 FTP 服務(wù)器時,首先會建立控制連接,用于傳輸命令和響應(yīng) 。在這個過程中,連接跟蹤系統(tǒng)會記錄下控制連接的相關(guān)信息 。當(dāng)客戶端需要傳輸數(shù)據(jù)時,會通過 PORT 或 PASV 命令來建立數(shù)據(jù)連接 。這些命令中包含了客戶端或服務(wù)器的數(shù)據(jù)傳輸?shù)刂泛投丝谛畔?。
連接跟蹤的擴展組件會對這些信息進行分析和處理,確保在 NAT 環(huán)境下,數(shù)據(jù)連接能夠正確建立 。如果客戶端使用 PORT 命令,連接跟蹤組件會根據(jù) NAT 轉(zhuǎn)換規(guī)則,修改命令中的地址信息,使其與轉(zhuǎn)換后的公網(wǎng)地址一致 。這樣,F(xiàn)TP 服務(wù)器就能正確地與客戶端建立數(shù)據(jù)連接,實現(xiàn)文件的傳輸 。
Part4.內(nèi)核中連接跟蹤的實現(xiàn)
4.1 模塊初始化流程
在 Linux 內(nèi)核中,連接跟蹤的模塊初始化是一個嚴(yán)謹(jǐn)且關(guān)鍵的過程,就像是搭建一座大廈前的奠基工作,為后續(xù)的連接跟蹤功能奠定了堅實的基礎(chǔ)。這一過程主要由 nf_conntrack_init_init_net () 函數(shù)和 nf_conntrack_init_net () 函數(shù)協(xié)同完成 。
圖片
這張圖乍一看比較亂,但是這張圖非常好的說明了數(shù)據(jù)流在內(nèi)核中的過程;在圖中的灰色框標(biāo)記了conntrack的位置,這個是連接跟蹤的起始點,其實在netfilter的所有HOOK點都可以更改流(flow)跟蹤的信息。
nf_conntrack_init_init_net () 函數(shù)首先會根據(jù)系統(tǒng)內(nèi)存的大小來確定連接跟蹤表的關(guān)鍵參數(shù)。它會給全局變量 nf_conntrack_htable_size 賦值,這個值指定了存放連接跟蹤條目的哈希表的大小 。同時,它還會計算出系統(tǒng)可以創(chuàng)建的連接跟蹤條目的最大數(shù)量,并賦值給 nf_conntrack_max 。這兩個參數(shù)就像是連接跟蹤表的 “容量規(guī)劃師”,合理地規(guī)劃了連接跟蹤表的存儲能力 。接著,該函數(shù)會為 nf_conn 結(jié)構(gòu)申請 slab cache,這個緩存就像是一個專門為 nf_conn 結(jié)構(gòu)打造的 “倉庫”,用于高效地存儲和管理 nf_conn 結(jié)構(gòu) 。
每個連接的狀態(tài)都由一個 nf_conn 結(jié)構(gòu)體實例來描述,每個連接又分為 original 和 reply 兩個方向,每個方向都用一個元組(tuple)表示,tuple 中包含了這個方向上數(shù)據(jù)包的關(guān)鍵信息,如源 IP、目的 IP、源 port、目的 port 等 。隨后,它會給全局的 nf_ct_l3protos [] 數(shù)組賦上默認(rèn)值,這個數(shù)組中的每個元素都被賦值為 nf_conntrack_l3proto_generic,這是一個不區(qū)分 L3 協(xié)議的處理函數(shù),后續(xù)的初始化會根據(jù)不同的 L3 協(xié)議為其賦上相應(yīng)的值 。最后,它還會給全局的 hash 表 nf_ct_helper_hash 分配一個頁大小的空間,這個 hash 表用于存放 helper 類型的 conntrack extension 。
nf_conntrack_init_net () 函數(shù)則主要負(fù)責(zé)初始化 net->ct 成員 。net 是本地 CPU 的網(wǎng)絡(luò)命名空間,在單 CPU 系統(tǒng)中就是全局變量 init_net 。它會將 net->ct.count 計數(shù)器設(shè)置為 0,就像是給連接跟蹤的 “計數(shù)器” 清零,準(zhǔn)備開始記錄連接數(shù)量 。然后初始化 unconfirmed 鏈表和 dying 鏈表,這兩個鏈表就像是連接跟蹤的 “臨時存放區(qū)”,分別用于存放未確認(rèn)的連接和即將死亡的連接 。
接著,它會給 net->ct.stat 分配空間并清零,net->ct.stat 用于統(tǒng)計連接跟蹤的相關(guān)數(shù)據(jù),就像是一個 “數(shù)據(jù)統(tǒng)計員” 。之后,它會初始化 conntrack hash table,根據(jù)之前計算好的 nf_conntrack_htable_size 來分配相應(yīng)的內(nèi)存空間 。最后,它還會初始化 net->ct.expect_hash 及緩存,并在 /proc 中創(chuàng)建相應(yīng)文件,完成一系列的初始化工作 。
4.2 連接跟蹤表的數(shù)據(jù)結(jié)構(gòu)
連接跟蹤表的數(shù)據(jù)結(jié)構(gòu)是連接跟蹤功能實現(xiàn)的核心部分,它就像是一個精心設(shè)計的 “信息倉庫”,高效地存儲和管理著網(wǎng)絡(luò)連接的各種信息 。在這個 “倉庫” 中,nf_conn 結(jié)構(gòu)體是描述連接狀態(tài)的關(guān)鍵 “單元” 。每個 nf_conn 結(jié)構(gòu)體實例都對應(yīng)著一個網(wǎng)絡(luò)連接,它包含了連接的各種詳細(xì)信息,如連接的兩個方向(original 和 reply)的元組信息,這些元組信息就像是連接的 “身份標(biāo)識”,包含了源 IP、目的 IP、源端口、目的端口和協(xié)議類型等關(guān)鍵內(nèi)容 。
同時,nf_conn 結(jié)構(gòu)體還記錄了連接的狀態(tài),比如是新建連接(NEW)、已建立連接(ESTABLISHED)還是其他狀態(tài),這些狀態(tài)信息就像是連接的 “狀態(tài)標(biāo)簽”,方便系統(tǒng)對連接進行管理和處理 。此外,它還包含了一些與連接相關(guān)的統(tǒng)計信息,如收發(fā)包數(shù)、字節(jié)數(shù)等,這些統(tǒng)計信息就像是連接的 “運行數(shù)據(jù)記錄”,有助于系統(tǒng)了解連接的運行情況 。
連接跟蹤表采用哈希表(hash table)來實現(xiàn),這是一種高效的數(shù)據(jù)結(jié)構(gòu),能夠快速地定位和查找連接信息 。哈希表就像是一個大型的 “索引庫”,每個連接的元組信息通過特定的哈希函數(shù)計算出一個哈希值,這個哈希值就像是連接在 “索引庫” 中的 “索引編號”,通過這個編號可以快速地找到對應(yīng)的連接記錄 。
在哈希表中,每個哈希桶(bucket)是一個鏈表,當(dāng)多個連接的哈希值相同時,這些連接的記錄就會以鏈表的形式存儲在同一個哈希桶中 。這種設(shè)計既利用了哈希表的快速查找特性,又解決了哈希沖突的問題 。在一個高并發(fā)的網(wǎng)絡(luò)環(huán)境中,可能會有大量的連接同時存在,哈希表的這種設(shè)計能夠保證系統(tǒng)在處理這些連接時,依然能夠快速地進行查找和匹配操作,大大提高了連接跟蹤的效率 。
(1)重要結(jié)構(gòu)體
- struct nf_hook_ops {}: 在HOOK點上注冊的連接跟蹤信息,通過nf_register_hooks()注冊
 - struct nf_conntrack_tuple {}: 連接跟蹤的基本元素,表示特定方向的流。
 - struct nf_conn {}:連接跟蹤條目,定義一個 flow。
 
在nf_conn中有重要成員:如ct_general、status、master、tuplehash、timeout等。
struct nf_conn {
	struct nf_conntrack ct_general;
	spinlock_t	lock;
	u16		cpu;
	/* XXX should I move this to the tail ? - Y.K */
	/* These are my tuples; original and reply */
	struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
	/* Have we seen traffic both ways yet? (bitset) */
	unsigned long status;
	/* Timer function; drops refcnt when it goes off. */
	struct timer_list timeout;
	possible_net_t ct_net;
	/* all members below initialized via memset */
	u8 __nfct_init_offset[0];
	/* If we were expected by an expectation, this will be it */
	struct nf_conn *master;
#if defined(CONFIG_NF_CONNTRACK_MARK)
	u_int32_t mark;
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
	u_int32_t secmark;
#endif
	/* Extensions */
	struct nf_ct_ext *ext;
	/* Storage reserved for other modules, must be the last member */
	union nf_conntrack_proto proto;
};(2)重要函數(shù)
- hash_conntrack_raw():根據(jù) tuple 計算出一個 32 位的哈希值(hash key)。
 - nf_conntrack_in():連接跟蹤模塊的核心,包進入連接跟蹤的地方。在此函數(shù)中包含下邊的步驟:resolve_normal_ct() -> nf_ct_timeout_lookup()在resolve_normal_ct() 中會計算元組的散列值,進行匹配,沒有就創(chuàng)建nf_conntrack_tuple_hash,將其加入未確認(rèn)tuplehash列表中,已經(jīng)創(chuàng)建則判斷狀態(tài)是否超時。
 - nf_conntrack_confirm():確認(rèn)前面通過 nf_conntrack_in() 創(chuàng)建的新連接(是否被丟棄),將元組從未確認(rèn)tuplehash列表中刪除。
 - nf_ct_get(skb, ctinfo):獲取連接跟蹤數(shù)據(jù),沒有建立返回null
 
具體細(xì)節(jié)可以在內(nèi)核代碼中查看代碼路徑官方網(wǎng)站:https://elixir.bootlin.com/linux/v4.4.155/source/net/netfilter
4.3 連接查找與匹配策略
當(dāng)一個數(shù)據(jù)包到達時,連接查找的流程就像是在一個龐大的圖書館中查找一本特定的書籍 。首先,系統(tǒng)會從數(shù)據(jù)包中提取出關(guān)鍵的元組信息,這些元組信息就像是書籍的 “關(guān)鍵詞” 。然后,根據(jù)這些元組信息,通過哈希函數(shù)計算出一個哈希值,這個哈希值就像是圖書館中的 “書架編號” 。系統(tǒng)會根據(jù)這個哈希值快速定位到哈希表中的相應(yīng)哈希桶,就像是找到了對應(yīng)的書架 。
如果哈希桶中只有一個連接記錄,那么就可以直接找到對應(yīng)的連接,這就像是在書架上只有一本書時,直接就能拿到想要的書 。但如果哈希桶中存在多個連接記錄(即發(fā)生了哈希沖突),系統(tǒng)就會沿著鏈表依次查找,對比每個連接記錄的元組信息與數(shù)據(jù)包的元組信息是否匹配,這就像是在書架上有很多本書時,需要逐一查看書籍的內(nèi)容來找到目標(biāo)書籍 。
匹配策略的實現(xiàn)主要基于數(shù)據(jù)包的元組信息 。系統(tǒng)會嚴(yán)格對比數(shù)據(jù)包的源 IP、目的 IP、源端口、目的端口和協(xié)議類型等元組信息與連接跟蹤表中記錄的元組信息 。只有當(dāng)這些信息完全一致時,才認(rèn)為找到了匹配的連接 。在一個企業(yè)網(wǎng)絡(luò)中,當(dāng)內(nèi)部員工的電腦向外部服務(wù)器發(fā)送數(shù)據(jù)包時,系統(tǒng)會根據(jù)數(shù)據(jù)包的元組信息在連接跟蹤表中進行查找和匹配 。如果找到了匹配的連接,就說明這是一個已建立連接的后續(xù)數(shù)據(jù)包,系統(tǒng)會根據(jù)連接的狀態(tài)進行相應(yīng)的處理,比如更新連接的統(tǒng)計信息等 。如果沒有找到匹配的連接,就說明這可能是一個新的連接請求,系統(tǒng)會按照新連接的處理流程進行處理,比如創(chuàng)建新的連接跟蹤記錄 。
4.4 連接生命周期管理
連接的生命周期管理就像是一場精心安排的 “旅程”,涵蓋了連接的創(chuàng)建、確認(rèn)、更新和刪除等多個重要階段,每個階段都有其特定的內(nèi)核處理邏輯和狀態(tài)轉(zhuǎn)換 。
當(dāng)一個新的連接請求到來時,比如 TCP 協(xié)議中的 SYN 包到達,內(nèi)核會創(chuàng)建一個新的連接跟蹤記錄 。內(nèi)核會從數(shù)據(jù)包中提取出五元組信息,然后為這個連接分配一個 nf_conn 結(jié)構(gòu)體實例 。在這個結(jié)構(gòu)體中,會初始化連接的兩個方向的元組信息,將連接狀態(tài)設(shè)置為初始狀態(tài),比如 TCP 連接的 SYN_SENT 狀態(tài) 。同時,還會將這個連接的原始方向的 tuple_hash 添加到 unconfirmed 鏈表中,等待進一步的確認(rèn) 。
當(dāng)連接收到對方的確認(rèn)信息時,比如 TCP 協(xié)議中的 SYN + ACK 包到達,內(nèi)核會對連接進行確認(rèn)操作 。它會將連接從 unconfirmed 鏈表中移除,并將其添加到連接跟蹤表的哈希表中,標(biāo)志著連接已被確認(rèn) 。同時,會更新連接的狀態(tài),將 TCP 連接的狀態(tài)更新為 ESTABLISHED,表明連接已成功建立 。
在連接的數(shù)據(jù)傳輸過程中,內(nèi)核會持續(xù)關(guān)注連接的狀態(tài)變化,并及時更新連接的相關(guān)信息 。當(dāng)接收到屬于已有連接的數(shù)據(jù)包時,內(nèi)核會更新連接記錄中的收發(fā)包數(shù)、字節(jié)數(shù)等統(tǒng)計信息 。如果連接的狀態(tài)發(fā)生了變化,比如 TCP 連接進入了 FIN_WAIT_1 狀態(tài),內(nèi)核也會及時更新連接的狀態(tài)信息 。
當(dāng)連接結(jié)束時,無論是正常結(jié)束還是異常結(jié)束,內(nèi)核都會執(zhí)行刪除操作 。對于正常結(jié)束的連接,比如 TCP 連接完成了四次揮手過程,內(nèi)核會將連接從連接跟蹤表的哈希表中移除,并釋放 nf_conn 結(jié)構(gòu)體實例所占用的內(nèi)存空間 。對于異常結(jié)束的連接,比如連接超時,內(nèi)核也會同樣進行刪除操作,確保連接跟蹤表的高效運行,為新的連接記錄騰出空間 。 。















 
 
 










 
 
 
 