Linux 网络层收发包流程及 Netfilter 框架浅析
本文作者:sivenzhang,騰訊 IEG 測(cè)試開發(fā)工程師
1. 前言
本文主要對(duì) Linux 系統(tǒng)內(nèi)核協(xié)議棧中網(wǎng)絡(luò)層接收,發(fā)送以及轉(zhuǎn)發(fā)數(shù)據(jù)包的流程進(jìn)行簡要介紹,同時(shí)對(duì) Netfilter 數(shù)據(jù)包過濾框架的基本原理以及使用方式進(jìn)行簡單闡述。
內(nèi)容如有理解錯(cuò)誤而導(dǎo)致說明錯(cuò)誤的地方,還請(qǐng)指正。如存在引用而沒有添加說明的,也請(qǐng)及時(shí)告知,非常感謝!
2. 基礎(chǔ)網(wǎng)絡(luò)知識(shí)
2.1 網(wǎng)絡(luò)分層模型
OSI 模型中將網(wǎng)絡(luò)劃分為七層,但在目前實(shí)際廣泛使用的 TCP/IP 協(xié)議框架體系內(nèi),我們一般將網(wǎng)絡(luò)劃分為五層,從下到上依次為物理層,鏈路層,網(wǎng)絡(luò)層,傳輸層以及應(yīng)用層。兩者的區(qū)別在于 OSI 模型在應(yīng)用層對(duì)數(shù)據(jù)包做了更細(xì)致的劃分。兩者的關(guān)系如下圖所示:
圖片來源:
https://www.cnblogs.com/qishui/p/5428938.html
在 TCP/IP 協(xié)議框架體系的五層網(wǎng)絡(luò)模型中,每一層負(fù)責(zé)處理的數(shù)據(jù)包協(xié)議或類型均存在差異,物理層主要負(fù)責(zé)在物理載體上的數(shù)據(jù)包傳輸,如 WiFi,以太網(wǎng),光纖,電話線等;數(shù)據(jù)鏈路層主要負(fù)責(zé)鏈路層協(xié)議解析(主要為以太網(wǎng)幀,其他類型此處暫不考慮),網(wǎng)絡(luò)層主要負(fù)責(zé) IP 協(xié)議(包括 IPv4 和 IPv6)解析,傳輸層負(fù)責(zé)傳輸層協(xié)議解析(主要為 TCP,UDP 等),而傳輸層以上我們均歸類為應(yīng)用層,主要包括各類應(yīng)用層協(xié)議,如我們常用的 HTTP,FTP,SMTP,DNS,DHCP 等。
在 TCP/IP 協(xié)議框架體系內(nèi),下層協(xié)議對(duì)上層協(xié)議透明,即上層協(xié)議無需關(guān)注下層協(xié)議的實(shí)現(xiàn)邏輯和機(jī)制。
2.2 數(shù)據(jù)包協(xié)議分層
在 TCP/IP 協(xié)議框架體系內(nèi),上層協(xié)議報(bào)文被作為下層協(xié)議的數(shù)據(jù)載荷(Data Payload),存儲(chǔ)在下層協(xié)議的數(shù)據(jù)段區(qū)域中進(jìn)行傳輸。結(jié)合這一特性,我們常見的幾類網(wǎng)絡(luò)協(xié)議嵌套關(guān)系如下圖所示:
從上圖我們可以清晰地看到各類協(xié)議之間的嵌套關(guān)系,如使用 HTTP 協(xié)議的應(yīng)用 App1 在傳輸層封裝在 TCP 協(xié)議中,TCP 協(xié)議在網(wǎng)絡(luò)層又封裝到 IP 協(xié)議中,最后交到數(shù)據(jù)鏈路層中。其他應(yīng)用層 App 也類似。
網(wǎng)際報(bào)文控制協(xié)議(ICMP,使用該協(xié)議的 Ping 工具),以及網(wǎng)際組管理協(xié)議(IGMP,組播多播中的控制報(bào)文)是直接嵌套到 IP 數(shù)據(jù)包中,而不依賴于 TCP 或 UDP。
地址解析協(xié)議(ARP)和反解析協(xié)議(RARP)則是直接嵌套在數(shù)據(jù)鏈路層數(shù)據(jù)包中進(jìn)行傳輸。
注:在本文中,我們只大概了解整體的網(wǎng)絡(luò)框架,各協(xié)議的具體內(nèi)容這里不做贅述。
2.3 sk_buff 結(jié)構(gòu)
在 Linux 內(nèi)核中,系統(tǒng)使用 sk_buff 數(shù)據(jù)結(jié)構(gòu)對(duì)數(shù)據(jù)包進(jìn)行存儲(chǔ)和管理。在數(shù)據(jù)包接收過程中,該數(shù)據(jù)結(jié)構(gòu)從網(wǎng)卡驅(qū)動(dòng)收包開始,一直貫穿到內(nèi)核網(wǎng)絡(luò)協(xié)議棧的頂層,直到用戶態(tài)程序從內(nèi)核獲取數(shù)據(jù)。使用圖形表示 sk_buff 的結(jié)構(gòu)如下:
在 sk_buff 數(shù)據(jù)結(jié)構(gòu)中包含了諸多關(guān)于數(shù)據(jù)包存儲(chǔ),定位和管理的指針,數(shù)據(jù)包在網(wǎng)絡(luò)協(xié)議棧各層次之間進(jìn)行傳輸?shù)倪^程中,內(nèi)核通過操作指針的方式對(duì)數(shù)據(jù)包進(jìn)行逐層解析,避免頻繁的大數(shù)據(jù)段拷貝操作,從而提高數(shù)據(jù)包處理效率(但在某些特殊情況下依然會(huì)采用數(shù)據(jù)包拷貝操作)。
2.4 收發(fā)包整體框架
這里我們從客戶端和服務(wù)端整體框架層面來看數(shù)據(jù)收發(fā)流程:
用戶態(tài)(User Space)程序 Client 向另一臺(tái)主機(jī)上的 Server 發(fā)送數(shù)據(jù),需要通過調(diào)用內(nèi)核態(tài)(Kernel Space)提供給用戶態(tài)的 Socket 抽象層接口發(fā)送數(shù)據(jù);
Socket 抽象層接口收到用戶態(tài)數(shù)據(jù)后,向下交給傳輸層接口(TCP 或 UDP);
傳輸層負(fù)責(zé)創(chuàng)建 sk_buff,并將用戶數(shù)據(jù)(應(yīng)用層數(shù)據(jù))填充到緩沖區(qū),做合法性檢查后,添加傳輸層頭部,并通過網(wǎng)絡(luò)層注冊(cè)的接口將數(shù)據(jù)包交給網(wǎng)絡(luò)層處理;
網(wǎng)絡(luò)層收到傳輸層數(shù)據(jù)包后,會(huì)查詢路由表,決定數(shù)據(jù)包去向,如果是需要發(fā)出的數(shù)據(jù)包,會(huì)填充網(wǎng)絡(luò)層頭部,并交到內(nèi)核虛擬網(wǎng)絡(luò)接口設(shè)備的發(fā)送隊(duì)列中;
虛擬網(wǎng)絡(luò)接口從發(fā)送隊(duì)列獲取數(shù)據(jù),調(diào)用對(duì)應(yīng)網(wǎng)卡驅(qū)動(dòng)發(fā)送數(shù)據(jù);
Server 端接收數(shù)據(jù)時(shí),按照相反的過程從網(wǎng)卡驅(qū)動(dòng)中將數(shù)據(jù)包一層層上交,直到通過 Socket 抽象層接口將用戶數(shù)據(jù)上交到用戶態(tài) Server 進(jìn)程處理。
3. 網(wǎng)絡(luò)層(IPv4)收發(fā)包流程
數(shù)據(jù)包在實(shí)際現(xiàn)網(wǎng)傳輸過程中,會(huì)經(jīng)過各類交換機(jī),路由器的轉(zhuǎn)發(fā)處理,在這個(gè)過程中,路由器一般只處理到網(wǎng)絡(luò)層。這里我們僅對(duì) Linux 內(nèi)核中網(wǎng)絡(luò)層接收,發(fā)送以及轉(zhuǎn)發(fā)數(shù)據(jù)的流程進(jìn)行簡單介紹。
下圖為基于 Linux 2.6.38 版本內(nèi)核的網(wǎng)絡(luò)層相關(guān)接口在數(shù)據(jù)包收發(fā)過程的調(diào)用邏輯圖:
注:
1)不同版本內(nèi)核在函數(shù)名上可能存在一定差異,但整體調(diào)用邏輯基本不變;
2)該圖僅展示 IPv4 的處理流程,IPv6 不在該圖的函數(shù)中處理,但整體流程基本相似;
3)該圖展示的流程僅為普通單播并且未進(jìn)行 IP 分片的數(shù)據(jù)包處理流程,組播,多播,IP 分片的數(shù)據(jù)包在某些流程上存在差異;
從圖中可以看到,*ip_rcv*函數(shù)為網(wǎng)絡(luò)層向下層開放的入口,數(shù)據(jù)包通過該函數(shù)進(jìn)入網(wǎng)絡(luò)層進(jìn)行處理,該函數(shù)主要對(duì)上傳到網(wǎng)絡(luò)層的數(shù)據(jù)包進(jìn)行前期合法性檢查,通過后交由 Netfilter 的鉤子節(jié)點(diǎn);
綠色方框內(nèi)的IP_PRE_ROUTING為 Netfilter 框架的 Hook 點(diǎn),該節(jié)點(diǎn)會(huì)根據(jù)預(yù)設(shè)的規(guī)則對(duì)數(shù)據(jù)包進(jìn)行判決并根據(jù)判決結(jié)果做相關(guān)的處理,比如執(zhí)行 NAT 轉(zhuǎn)換;
IP_PRE_ROUTING節(jié)點(diǎn)處理完成后,數(shù)據(jù)包將交由*ip_rcv_finish*處理,該函數(shù)根據(jù)路由判決結(jié)果,決定數(shù)據(jù)包是交由本機(jī)上層應(yīng)用處理,還是需要進(jìn)行轉(zhuǎn)發(fā);如果是交由本機(jī)處理,則會(huì)交由*ip_local_deliver*走本地上交流程;如果需要轉(zhuǎn)發(fā),則交由*ip_forward*函數(shù)走轉(zhuǎn)發(fā)流程;
在數(shù)據(jù)包上交本地的流程中,IP_LOCAL_INPUT節(jié)點(diǎn)用于監(jiān)控和檢查上交到本地上層應(yīng)用的數(shù)據(jù)包,該節(jié)點(diǎn)是 Linux 防火墻的重要生效節(jié)點(diǎn)之一;
在數(shù)據(jù)包轉(zhuǎn)發(fā)流程中,Netfilter 框架的IP_FORWARD節(jié)點(diǎn)會(huì)對(duì)轉(zhuǎn)發(fā)數(shù)據(jù)包進(jìn)行檢查過濾;
而對(duì)于本機(jī)上層發(fā)出的數(shù)據(jù)包,網(wǎng)絡(luò)層通過注冊(cè)到上層的*ip_local_out*函數(shù)接收數(shù)據(jù)處理,處理 OK 進(jìn)一步交由IP_LOCAL_OUT節(jié)點(diǎn)檢測(cè);
對(duì)于即將發(fā)往下層的數(shù)據(jù)包,需要經(jīng)過IP_POST_ROUTING節(jié)點(diǎn)處理;網(wǎng)絡(luò)層處理結(jié)束,通過*dev_queue_xmit*函數(shù)將數(shù)據(jù)包交由 Linux 內(nèi)核中虛擬網(wǎng)絡(luò)設(shè)備做進(jìn)一步處理,從這里數(shù)據(jù)包即離開網(wǎng)絡(luò)層進(jìn)入到下一層;
4. Netfilter 框架
Netfilter 是 Linux 內(nèi)核中進(jìn)行數(shù)據(jù)包過濾,連接跟蹤(Connect Track),網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)等功能的主要實(shí)現(xiàn)框架;該框架在網(wǎng)絡(luò)協(xié)議棧處理數(shù)據(jù)包的關(guān)鍵流程中定義了一系列鉤子點(diǎn)(Hook 點(diǎn)),并在這些鉤子點(diǎn)中注冊(cè)一系列函數(shù)對(duì)數(shù)據(jù)包進(jìn)行處理。這些注冊(cè)在鉤子點(diǎn)的函數(shù)即為設(shè)置在網(wǎng)絡(luò)協(xié)議棧內(nèi)的數(shù)據(jù)包通行策略,也就意味著,這些函數(shù)可以決定內(nèi)核是接受還是丟棄某個(gè)數(shù)據(jù)包,換句話說,這些函數(shù)的處理結(jié)果決定了這些網(wǎng)絡(luò)數(shù)據(jù)包的“命運(yùn)”。
下圖為 Netfilter 框架的整體組件圖:
圖片來源:
http://wiki.dreamrunner.org/public_html/Linux/Networks/netfilter.html
從圖中我們可以看到,Netfilter 框架采用模塊化設(shè)計(jì)理念,并且貫穿了 Linux 系統(tǒng)的內(nèi)核態(tài)和用戶態(tài)。在用戶態(tài)層面,根據(jù)不同的協(xié)議類型,為上層用戶提供了不同的系統(tǒng)調(diào)用工具,比如我們常用的針對(duì) IPv4 協(xié)議 iptables,IPv6 協(xié)議的 ip6tables,針對(duì) ARP 協(xié)議的 arptables,針對(duì)網(wǎng)橋控制的 ebtables,針對(duì)網(wǎng)絡(luò)連接追蹤的 conntrack 等等。不同的用戶態(tài)工具在內(nèi)核中有對(duì)應(yīng)的模塊進(jìn)行實(shí)現(xiàn),而底層都需要調(diào)用 Netfilter hook API 接口進(jìn)行實(shí)現(xiàn)。
從圖中我們可以看到,我們常用的 Linux 防火墻工具 iptables 其實(shí)也是 Netfilter 框架中的一個(gè)組件。接下來我們就以 IPv4 為例,描述 iptables 在 Netfilter 框架中生效的基本原理,同時(shí),我們也看一下如果我們希望在內(nèi)核中添加我們自己的處理函數(shù),我們?cè)撛趺醋觥?/p>
4.1 IPv4 網(wǎng)絡(luò)層的 Netfilter Hook 點(diǎn)
在第二章已經(jīng)提及,Linux 內(nèi)核中,Netfiler 在網(wǎng)絡(luò)層設(shè)置了多個(gè) Hook 點(diǎn),這里我們不考慮實(shí)際的處理函數(shù),僅看 Netfilter 的鉤子節(jié)點(diǎn),從而將網(wǎng)絡(luò)層處理流程進(jìn)行簡化,如下圖:
其中,矩形方框中的即為 Netfilter 的鉤子節(jié)點(diǎn)。從圖中可以看到,三個(gè)方向的數(shù)據(jù)包需要經(jīng)過的鉤子節(jié)點(diǎn)不完全相同:
發(fā)往本地:NF_INET_PRE_ROUTING-->NF_INET_LOCAL_IN
轉(zhuǎn)發(fā):NF_INET_PRE_ROUTING-->NF_INET_FORWARD-->NF_INET_POST_ROUTING
本地發(fā)出:NF_INET_LOCAL_OUT-->NF_INET_POST_ROUTING
4.2 iptables 工具
iptables 在用戶態(tài)提供了表格和鏈的概念。包含的表格有 filter,nat,mangle 以及 raw。而每個(gè)表格下包含不同的鏈,如下圖所示:
圖片源自網(wǎng)絡(luò)iptables 中每個(gè)表格的作用不同,以我們比較常用的 filter 表為例,其主要起到數(shù)據(jù)包過濾和攔截作用,包含 INPUT,FORWARD 和 OUTPUT 三個(gè)鏈,根據(jù)鏈的名字我們可以知道,這三個(gè)鏈分別被放置到 Netfilter 三個(gè)不同的鉤子節(jié)點(diǎn)中生效。INPUT 鏈?zhǔn)窃?strong>NF_INET_LOCAL_IN節(jié)點(diǎn),FORWARD 鏈?zhǔn)窃?strong>NF_INET_FORWARD節(jié)點(diǎn),OUTPUT 鏈則是在NF_INET_LOCAL_OUT節(jié)點(diǎn)。其他表格的鏈也類似。
以如下 iptables 指令為例:
iptables?-t?filter?-A?INPUT?-s?172.16.0.0/16?-p?udp?--dport?53?-j?DROP該指令是在 filter 表的 INPUT 鏈中添加一條過濾規(guī)則,凡是收到源地址為 172.16.0.0/16,傳輸層協(xié)議為 UDP 并且目的端口為 53 的數(shù)據(jù)包(即 DNS 數(shù)據(jù)包),都將該數(shù)據(jù)包丟棄。在 Linux 內(nèi)核中,這一個(gè)指令會(huì)在 Netfilter 網(wǎng)絡(luò)層NF_INET_LOCAL_IN節(jié)點(diǎn)生成處理操作,凡是經(jīng)過這個(gè)鉤子節(jié)點(diǎn)的數(shù)據(jù)包,在前面規(guī)則都通過的情況下,都必須經(jīng)過這一規(guī)則的檢查,如果符合這條規(guī)則的匹配條件,則該數(shù)據(jù)包會(huì)被丟棄;如果不符合,則進(jìn)行下一條規(guī)則的匹配。
在 Linux 內(nèi)核內(nèi)部,使用 iptables 工具下發(fā)的指令規(guī)則,會(huì)存儲(chǔ)在內(nèi)核中的 Xtables 模塊中,這部分內(nèi)容這里不再深入分析。
4.3 Netfilter 重要數(shù)據(jù)結(jié)構(gòu)及相關(guān)函數(shù)
鉤子點(diǎn)枚舉類型
上面提到的網(wǎng)絡(luò)層中 Netfilter 的幾個(gè)鉤子節(jié)點(diǎn),在內(nèi)核中是以枚舉數(shù)據(jù)類型進(jìn)行標(biāo)記的。如下:
//?include/linux/netfilter.henum?nf_inet_hooks?{
????NF_INET_PRE_ROUTING,
????NF_INET_LOCAL_IN,
????NF_INET_FORWARD,
????NF_INET_LOCAL_OUT,
????NF_INET_POST_ROUTING,
????NF_INET_NUMHOOKS
};
注冊(cè)和解注冊(cè)鉤子函數(shù)
/*?Function?to?register/unregister?hook?points.?*/
int?nf_register_hook(struct?nf_hook_ops?*reg);
void?nf_unregister_hook(struct?nf_hook_ops?*reg);
int?nf_register_hooks(struct?nf_hook_ops?*reg,?unsigned?int?n);
void?nf_unregister_hooks(struct?nf_hook_ops?*reg,?unsigned?int?n);
這些函數(shù)用于將自定義的鉤子操作(struct nf_hook_ops)注冊(cè)到指定的鉤子節(jié)點(diǎn)中。
鉤子操作數(shù)據(jù)結(jié)構(gòu)
struct?nf_hook_ops?{
????struct?list_head?list;
????/*?User?fills?in?from?here?down.?*/
????nf_hookfn?*hook;
????struct?module?*owner;
????u_int8_t?pf;
????unsigned?int?hooknum;
????/*?Hooks?are?ordered?in?ascending?priority.?*/
????int?priority;
};
這個(gè)結(jié)構(gòu)體中存儲(chǔ)了自定義的鉤子函數(shù)(nf_hookfn),函數(shù)優(yōu)先級(jí)(priority),處理協(xié)議類型(pf),鉤子函數(shù)生效的鉤子節(jié)點(diǎn)(hooknum)等信息。
鉤子函數(shù)聲明
typedef?unsigned?int?nf_hookfn(unsigned?int?hooknum,
???????????????????struct?sk_buff?*skb,
???????????????????const?struct?net_device?*in,
???????????????????const?struct?net_device?*out,
???????????????????int?(*okfn)(struct?sk_buff?*));
如果我們自己實(shí)現(xiàn)一個(gè)內(nèi)核模塊,該模塊需要在 Netfilter 框架的幾個(gè)鉤子節(jié)點(diǎn)中對(duì)經(jīng)過的數(shù)據(jù)包進(jìn)行處理,則該內(nèi)核模塊需要向 Netfilter 中的鉤子節(jié)點(diǎn)注冊(cè)鉤子函數(shù),我們需要按照 nf_hookfn 函數(shù)的聲明類型,提供我們自己的實(shí)現(xiàn),再按照之前提供的注冊(cè)接口將相關(guān)數(shù)據(jù)類型注冊(cè)到內(nèi)核中使之生效。
4.4 一個(gè) Demo
如下為在網(wǎng)絡(luò)上找到的一個(gè)內(nèi)核模塊 Demo,該模塊的基本功能是將經(jīng)過 IPv4 網(wǎng)絡(luò)層 NF_INET_LOCAL_IN 節(jié)點(diǎn)的數(shù)據(jù)包的源 Mac 地址,目的 Mac 地址以及源 IP,目的 IP 打印出來。代碼如下所示:
#include?<linux/module.h>#include?<linux/kernel.h>
#include?<linux/types.h>
#include?<linux/skbuff.h>
#include?<linux/ip.h>
#include?<linux/udp.h>
#include?<linux/tcp.h>
#include?<linux/netfilter.h>
#include?<linux/netfilter_ipv4.h>
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR("SHI");
MODULE_DESCRIPTION("Netfliter?test");
static?unsigned?int
nf_test_in_hook(unsigned?int?hook,?struct?sk_buff?*skb,?const?struct?net_device?*in,
????????????????const?struct?net_device?*out,?int?(*okfn)(struct?sk_buff*));
static?struct?nf_hook_ops?nf_test_ops[]?__read_mostly?=?{
??{
????.hook?=?nf_test_in_hook,
????.owner?=?THIS_MODULE,
????.pf?=?NFPROTO_IPV4,
????.hooknum?=?NF_INET_LOCAL_IN,
????.priority?=?NF_IP_PRI_FIRST,
??},
};
void?hdr_dump(struct?ethhdr?*ehdr)?{
????printk("[MAC_DES:%x,%x,%x,%x,%x,%x"
???????????"MAC_SRC:?%x,%x,%x,%x,%x,%x?Prot:%x]\n",
???????????ehdr->h_dest[0],ehdr->h_dest[1],ehdr->h_dest[2],ehdr->h_dest[3],
???????????ehdr->h_dest[4],ehdr->h_dest[5],ehdr->h_source[0],ehdr->h_source[1],
???????????ehdr->h_source[2],ehdr->h_source[3],ehdr->h_source[4],
???????????ehdr->h_source[5],ehdr->h_proto);
}
#define?NIPQUAD(addr)?\
????((unsigned?char?*)&addr)[0],?\
????((unsigned?char?*)&addr)[1],?\
????((unsigned?char?*)&addr)[2],?\
????((unsigned?char?*)&addr)[3]
#define?NIPQUAD_FMT?"%u.%u.%u.%u"
static?unsigned?int
nf_test_in_hook(unsigned?int?hook,?struct?sk_buff?*skb,?const?struct?net_device?*in,
????????????????const?struct?net_device?*out,?int?(*okfn)(struct?sk_buff*))?{
??struct?ethhdr?*eth_header;
??struct?iphdr?*ip_header;
??eth_header?=?(struct?ethhdr?*)(skb_mac_header(skb));
??ip_header?=?(struct?iphdr?*)(skb_network_header(skb));
??hdr_dump(eth_header);
??printk("src?IP:'"NIPQUAD_FMT"',?dst?IP:'"NIPQUAD_FMT"'?\n",
?????????NIPQUAD(ip_header->saddr),?NIPQUAD(ip_header->daddr));
??return?NF_ACCEPT;
}
static?int?__init?init_nf_test(void)?{
??int?ret;
??ret?=?nf_register_hooks(nf_test_ops,?ARRAY_SIZE(nf_test_ops));
??if?(ret?<?0)?{
????printk("register?nf?hook?fail\n");
????return?ret;
??}
??printk(KERN_NOTICE?"register?nf?test?hook\n");
??return?0;
}
static?void?__exit?exit_nf_test(void)?{
??nf_unregister_hooks(nf_test_ops,?ARRAY_SIZE(nf_test_ops));
}
module_init(init_nf_test);
module_exit(exit_nf_test);
該 Demo 為網(wǎng)絡(luò)上找到的 Demo 程序,地址:
http://wiki.dreamrunner.org/public_html/Linux/Networks/netfilter.html
這個(gè) Demo 程序是個(gè)內(nèi)核模塊,模塊入口為module_init傳入的init_nf_test函數(shù)。
在init_nf_test函數(shù)中,其通過 Netfilter 提供的 nf_register_hooks 接口將自定義的nf_test_opt注冊(cè)到鉤子節(jié)點(diǎn)中。nf_test_opt為struct nf_hook_ops類型的結(jié)構(gòu)體數(shù)組,其內(nèi)部包含了所有關(guān)鍵元素,比如鉤子函數(shù)的注冊(cè)節(jié)點(diǎn)(此處為NF_INET_LOCAL_IN)以及鉤子函數(shù)(**nf_test_in_hook**)。
在nf_test_in_hook函數(shù)內(nèi)部,其檢查每一個(gè)傳遞過來的數(shù)據(jù)包,并將其源 Mac 地址,目的 Mac 地址,源 IP 地址以及目的 IP 地址打印出來。最后返回NF_ACCEPT,將數(shù)據(jù)包交給下一個(gè)鉤子函數(shù)處理。
4.5 NAT 和 conntrack
NAT(Network Address Translation)技術(shù)現(xiàn)如今被廣泛應(yīng)用于路由器等網(wǎng)絡(luò)設(shè)備中,其在解決 IPv4 地址緊缺的問題上起到了至關(guān)重要的作用,但與此同時(shí)也存在一定的安全隱患。
而 conntrack(連接追蹤)也是廣泛應(yīng)用于路由器網(wǎng)絡(luò)設(shè)備中的模塊,其根據(jù)數(shù)據(jù)包的五元組以及 NAT 的轉(zhuǎn)換結(jié)果,記錄每一條連接的狀態(tài),在提升設(shè)備轉(zhuǎn)發(fā)效率上起到了很大的作用,但另一方面,記錄連接信息需要消耗一部分資源,也會(huì)導(dǎo)致設(shè)備出現(xiàn)性能瓶頸。
5. 總結(jié)
Linux 網(wǎng)絡(luò)協(xié)議棧是 Linux 內(nèi)核中非常重要的子系統(tǒng)之一,雖然上層應(yīng)用的開發(fā)維護(hù)工作極少涉及修改內(nèi)核網(wǎng)絡(luò)部分的工作,但了解其設(shè)計(jì)思想,基本工作原理,也可以為我們?nèi)粘9ぷ鲙肀容^不少的幫助,特別是涉及到前后臺(tái)網(wǎng)絡(luò)交互,服務(wù)器網(wǎng)絡(luò)性能相關(guān)的工作時(shí)。
這篇文章所涉及的內(nèi)容也僅僅是 Linux 網(wǎng)絡(luò)協(xié)議棧中網(wǎng)絡(luò)層的極小一部分,如下為 Linux 內(nèi)核中數(shù)據(jù)包流向的整體脈絡(luò)圖以及 Netfilter 的整體生效節(jié)點(diǎn):
圖片來源:
http://wiki.dreamrunner.org/public_html/Linux/Networks/netfilter.html
從上圖可以看到,除了在網(wǎng)絡(luò)層,鏈路層中 Netfilter 也被廣泛地應(yīng)用,ebtables 是 Netfilter 提供給用戶態(tài)的鏈路層配置接口(工具),其生效機(jī)制與 iptables 基本類似。
6. 擴(kuò)展
這篇文章僅僅對(duì) Linux 內(nèi)核中網(wǎng)絡(luò)層數(shù)據(jù)處理流程以及 Netfilter 基本原理進(jìn)行簡單介紹,在此基礎(chǔ)上,關(guān)于 Linux 內(nèi)核網(wǎng)絡(luò)協(xié)議棧的其他技術(shù)還包括:
Linux TC(Traffic Control)模塊:Linux 提供的 QoS 功能支持模塊;
網(wǎng)橋和 VLAN 技術(shù);
Wireshark(tcpdump)等網(wǎng)絡(luò)抓包工具的基本實(shí)現(xiàn)原理;
7. 參考
博客
Linux Netfilter and Traffic Control
OSI 七層模型與 TCP/IP 五層模型
書籍
《TCP/IP 詳解 卷 I:協(xié)議》
《深入理解 Linux 網(wǎng)絡(luò)技術(shù)內(nèi)幕》
總結(jié)
以上是生活随笔為你收集整理的Linux 网络层收发包流程及 Netfilter 框架浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Vue-SSR优化方案归纳总结
- 下一篇: Linux 入门必看:如何60秒内分析L