偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Netfileter & Iptables 實(shí)現(xiàn)之 Netfilter實(shí)現(xiàn)

開發(fā) 前端
在《Netfilter & iptables 原理》一文中,我們介紹了 Netfilter 和 iptables 的原理,而本文主要通過源碼分析來介紹一下 Netfilter 與 iptables 的實(shí)現(xiàn)過程。

 [[393194]]

本文轉(zhuǎn)載自微信公眾號(hào)「Linux內(nèi)核那些事」,作者songsong001。轉(zhuǎn)載本文請(qǐng)聯(lián)系Linux內(nèi)核那些事公眾號(hào)。

在《Netfilter & iptables 原理》一文中,我們介紹了 Netfilter 和 iptables 的原理,而本文主要通過源碼分析來介紹一下 Netfilter 與 iptables 的實(shí)現(xiàn)過程。

一、Netfilter 掛載點(diǎn)

我們先來回顧一下 Netfilter 的原理,Netfilter 是通過在網(wǎng)絡(luò)協(xié)議棧的不同階段注冊(cè)鉤子函數(shù)來實(shí)現(xiàn)對(duì)數(shù)據(jù)包的處理與過濾,如 圖1 所示:

(圖1 Netfilter掛載點(diǎn))

在 圖1 中,藍(lán)色部分就是 Netfilter 掛載鉤子函數(shù)的位置,所以 Netfilter 定義了 5 個(gè)常量來表示這 5 個(gè)位置,如下代碼:

  1. // 文件:include/linux/netfilter_ipv4.h 
  2.  
  3. #define NF_IP_PRE_ROUTING   0 
  4. #define NF_IP_LOCAL_IN      1 
  5. #define NF_IP_FORWARD       2 
  6. #define NF_IP_LOCAL_OUT     3 
  7. #define NF_IP_POST_ROUTING  4 

上面代碼中的常量與 圖1 中掛載鉤子函數(shù)的位置一一對(duì)應(yīng),如常量 NF_IP_PRE_ROUTING 對(duì)應(yīng)著 圖1 的 PRE_ROUTING 處。

二、Netfilter 鉤子函數(shù)鏈

前面說過,Netfilter 是通過在網(wǎng)絡(luò)協(xié)議中的不同位置掛載鉤子函數(shù)來對(duì)數(shù)據(jù)包進(jìn)行過濾和處理,而且每個(gè)掛載點(diǎn)能夠掛載多個(gè)鉤子函數(shù),所以 Netfilter 使用鏈表結(jié)構(gòu)來存儲(chǔ)這些鉤子函數(shù),如 圖2 所示:

(圖2 Netfilter鉤子函數(shù)鏈)

如 圖2 所示,Netfilter 的每個(gè)掛載點(diǎn)都使用一個(gè)鏈表來存儲(chǔ)鉤子函數(shù)列表。在內(nèi)核中,定義了一個(gè)名為 nf_hooks 的數(shù)組來存儲(chǔ)這些鏈表,如下代碼:

  1. // 文件:net/core/netfilter.c 
  2.  
  3. struct list_head nf_hooks[32][5]; 

struct list_head 結(jié)構(gòu)是內(nèi)核的通用鏈表結(jié)構(gòu)。

從 nf_hooks 變量定義為一個(gè)二維數(shù)組,第一維是用來表示不同的協(xié)議(如 IPv4 或者 IPv6,本文只討論 IPv4,所以可以把 nf_hooks 當(dāng)成是一維數(shù)組),而第二維用于表示不同的掛載點(diǎn),如 圖2 中的 5 個(gè)掛載點(diǎn)。

三、鉤子函數(shù)

接下來我們介紹一下鉤子函數(shù)在 Netfilter 中的存儲(chǔ)方式。

前面我們介紹過,Netfilter 通過鏈表來存儲(chǔ)鉤子函數(shù),而鉤子函數(shù)是通過結(jié)構(gòu) nf_hook_ops 來描述的,其定義如下:

  1. // 文件:include/linux/netfilter.h 
  2.  
  3. struct nf_hook_ops 
  4.     struct list_head list; // 連接相同掛載點(diǎn)的鉤子函數(shù) 
  5.     nf_hookfn *hook;       // 鉤子函數(shù)指針 
  6.     int pf;                // 協(xié)議類型 
  7.     int hooknum;           // 鉤子函數(shù)所在鏈 
  8.     int priority;          // 優(yōu)先級(jí) 
  9. }; 

下面我們對(duì) nf_hook_ops 結(jié)構(gòu)的各個(gè)字段進(jìn)行說明:

  • list:用于把處于相同掛載點(diǎn)的鉤子函數(shù)鏈接起來。
  • hook:鉤子函數(shù)指針,就是用于處理或者過濾數(shù)據(jù)包的函數(shù)。
  • pf:協(xié)議類型,用于指定鉤子函數(shù)掛載在 nf_hooks 數(shù)組第一維的位置,如 IPv4 協(xié)議設(shè)置為 PF_INET。
  • hooknum:鉤子函數(shù)所在鏈(掛載點(diǎn)),如 NF_IP_PRE_ROUTING。
  • priority:鉤子函數(shù)的優(yōu)先級(jí),用于管理鉤子函數(shù)的調(diào)用順序。

其中 hook 字段的類型為 nf_hookfn,nf_hookfn 類型的定義如下:

  1. // 文件:include/linux/netfilter.h 
  2.  
  3. typedef unsigned int nf_hookfn(unsigned int hooknum, 
  4.                                struct sk_buff **skb, 
  5.                                const struct net_device *in
  6.                                const struct net_device *out
  7.                                int (*okfn)(struct sk_buff *)); 

我們也介紹一下 nf_hookfn 函數(shù)的各個(gè)參數(shù)的作用:

  • hooknum:鉤子函數(shù)所在鏈(掛載點(diǎn)),如 NF_IP_PRE_ROUTING。
  • skb:數(shù)據(jù)包對(duì)象,就是要處理或者過濾的數(shù)據(jù)包。
  • in:接收數(shù)據(jù)包的設(shè)備對(duì)象。
  • out:發(fā)送數(shù)據(jù)包的設(shè)備對(duì)象。
  • okfn:當(dāng)掛載點(diǎn)上所有的鉤子函數(shù)都處理過數(shù)據(jù)包后,將會(huì)調(diào)用這個(gè)函數(shù)來對(duì)數(shù)據(jù)包進(jìn)行下一步處理。

四、注冊(cè)鉤子函數(shù)

當(dāng)定義好一個(gè)鉤子函數(shù)結(jié)構(gòu)后,需要調(diào)用 nf_register_hook 函數(shù)來將其注冊(cè)到 nf_hooks 數(shù)組中,nf_register_hook 函數(shù)的實(shí)現(xiàn)如下:

  1. // 文件:net/core/netfilter.c 
  2.  
  3. int nf_register_hook(struct nf_hook_ops *reg) 
  4.     struct list_head *i; 
  5.  
  6.     br_write_lock_bh(BR_NETPROTO_LOCK); // 對(duì) nf_hooks 進(jìn)行上鎖 
  7.  
  8.     // priority 字段表示鉤子函數(shù)的優(yōu)先級(jí) 
  9.     // 所以通過 priority 字段來找到鉤子函數(shù)的合適位置 
  10.     for (i = nf_hooks[reg->pf][reg->hooknum].next
  11.          i != &nf_hooks[reg->pf][reg->hooknum]; 
  12.          i = i->next
  13.     { 
  14.         if (reg->priority < ((struct nf_hook_ops *)i)->priority) 
  15.             break; 
  16.     } 
  17.  
  18.     list_add(&reg->list, i->prev); // 把鉤子函數(shù)添加到鏈表中 
  19.  
  20.     br_write_unlock_bh(BR_NETPROTO_LOCK); // 對(duì) nf_hooks 進(jìn)行解鎖 
  21.  
  22.     return 0; 

nf_register_hook 函數(shù)的實(shí)現(xiàn)比較簡(jiǎn)單,步驟如下:

  • 對(duì) nf_hooks 進(jìn)行上鎖操作,用于保護(hù) nf_hooks 變量不受并發(fā)競(jìng)爭(zhēng)。
  • 通過鉤子函數(shù)的優(yōu)先級(jí)來找到其在鉤子函數(shù)鏈表中的正確位置。
  • 把鉤子函數(shù)插入到鏈表中。
  • 對(duì) nf_hooks 進(jìn)行解鎖操作。

插入過程如 圖3 所示:

(圖3 鉤子函數(shù)插入過程)

如 圖3 所示,我們要把優(yōu)先級(jí)為 20 的鉤子函數(shù)插入到 PRE_ROUTING 這個(gè)鏈中,而 PRE_ROUTING 鏈已經(jīng)存在兩個(gè)鉤子函數(shù),一個(gè)優(yōu)先級(jí)為 10, 另外一個(gè)優(yōu)先級(jí)為 30。

通過與鏈表中的鉤子函數(shù)的優(yōu)先級(jí)進(jìn)行對(duì)比,發(fā)現(xiàn)新的鉤子函數(shù)應(yīng)該插入到優(yōu)先級(jí)為 10 的鉤子函數(shù)后面,所以就 如圖3 所示就把新的鉤子函數(shù)插入到優(yōu)先級(jí)為 10 的鉤子函數(shù)后面。

五、觸發(fā)調(diào)用鉤子函數(shù)

鉤子函數(shù)已經(jīng)被保存到不同的鏈上,那么什么時(shí)候才會(huì)觸發(fā)調(diào)用這些鉤子函數(shù)來處理數(shù)據(jù)包呢?

要觸發(fā)調(diào)用某個(gè)掛載點(diǎn)上(鏈)的所有鉤子函數(shù),需要使用 NF_HOOK 宏來實(shí)現(xiàn),其定義如下:

  1. // 文件:include/linux/netfilter.h 
  2.  
  3. #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)    \ 
  4.     (list_empty(&nf_hooks[(pf)][(hook)])               \ 
  5.         ? (okfn)(skb)                                  \ 
  6.         : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn))) 

首先介紹一下 NF_HOOK 宏的各個(gè)參數(shù)的作用:

  • pf:協(xié)議類型,就是 nf_hooks 數(shù)組的第一個(gè)維度,如 IPv4 協(xié)議就是 PF_INET。
  • hook:要調(diào)用哪一條鏈(掛載點(diǎn))上的鉤子函數(shù),如 NF_IP_PRE_ROUTING。
  • indev:接收數(shù)據(jù)包的設(shè)備對(duì)象。
  • outdev:發(fā)送數(shù)據(jù)包的設(shè)備對(duì)象。
  • okfn:當(dāng)鏈上的所有鉤子函數(shù)都處理完成,將會(huì)調(diào)用此函數(shù)繼續(xù)對(duì)數(shù)據(jù)包進(jìn)行處理。

而 NF_HOOK 宏的實(shí)現(xiàn)也比較簡(jiǎn)單,首先判斷一下鉤子函數(shù)鏈表是否為空,如果是空的話,就直接調(diào)用 okfn 函數(shù)來處理數(shù)據(jù)包,否則就調(diào)用 nf_hook_slow 函數(shù)來處理數(shù)據(jù)包。我們來看看 nf_hook_slow 函數(shù)的實(shí)現(xiàn):

  1. // 文件:net/core/netfilter.c 
  2.  
  3. int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, 
  4.                  struct net_device *indev, struct net_device *outdev, 
  5.                  int (*okfn)(struct sk_buff *)) 
  6.     struct list_head *elem; 
  7.     unsigned int verdict; 
  8.     int ret = 0; 
  9.  
  10.     elem = &nf_hooks[pf][hook]; // 獲取要調(diào)用的鉤子函數(shù)鏈表 
  11.  
  12.     // 遍歷鉤子函數(shù)鏈表,并且調(diào)用鉤子函數(shù)對(duì)數(shù)據(jù)包進(jìn)行處理 
  13.     verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn); 
  14.     ... 
  15.     // 如果處理結(jié)果為 NF_ACCEPT, 表示數(shù)據(jù)包通過所有鉤子函數(shù)的處理, 那么就調(diào)用 okfn 函數(shù)繼續(xù)處理數(shù)據(jù)包 
  16.     // 如果處理結(jié)果為 NF_DROP, 表示數(shù)據(jù)包被拒絕, 應(yīng)該丟棄此數(shù)據(jù)包 
  17.     switch (verdict) { 
  18.     case NF_ACCEPT: 
  19.         ret = okfn(skb); 
  20.         break; 
  21.     case NF_DROP: 
  22.         kfree_skb(skb); 
  23.         ret = -EPERM; 
  24.         break; 
  25.     } 
  26.  
  27.     return ret; 

nf_hook_slow 函數(shù)的實(shí)現(xiàn)也比較簡(jiǎn)單,過程如下:

  • 首先調(diào)用 nf_iterate 函數(shù)來遍歷鉤子函數(shù)鏈表,并調(diào)用鏈表上的鉤子函數(shù)來處理數(shù)據(jù)包。
  • 如果處理結(jié)果為 NF_ACCEPT,表示數(shù)據(jù)包通過所有鉤子函數(shù)的處理, 那么就調(diào)用 okfn 函數(shù)繼續(xù)處理數(shù)據(jù)包。
  • 如果處理結(jié)果為 NF_DROP,表示數(shù)據(jù)包沒有通過鉤子函數(shù)的處理,應(yīng)該丟棄此數(shù)據(jù)包。

既然 Netfilter 是通過調(diào)用 NF_HOOK 宏來調(diào)用鉤子函數(shù)鏈表上的鉤子函數(shù),那么內(nèi)核在什么地方調(diào)用這個(gè)宏呢?

比如數(shù)據(jù)包進(jìn)入 IPv4 協(xié)議層的處理函數(shù) ip_rcv 函數(shù)中就調(diào)用了 NF_HOOK 宏來處理數(shù)據(jù)包,代碼如下:

  1. // 文件:net/ipv4/ip_input.c 
  2.  
  3. int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) 
  4.     ... 
  5.     return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); 

如上代碼所示,在 ip_rcv 函數(shù)中調(diào)用了 NF_HOOK 宏來處理輸入的數(shù)據(jù)包,其調(diào)用的鉤子函數(shù)鏈(掛載點(diǎn))為 NF_IP_PRE_ROUTING。而 okfn 設(shè)置為 ip_rcv_finish,也就是說,當(dāng) NF_IP_PRE_ROUTING 鏈上的所有鉤子函數(shù)都成功對(duì)數(shù)據(jù)包進(jìn)行處理后,將會(huì)調(diào)用 ip_rcv_finish 函數(shù)來繼續(xù)對(duì)數(shù)據(jù)包進(jìn)行處理。

六、總結(jié)

本文主要介紹了 Netfilter 的實(shí)現(xiàn),因?yàn)?Netfilter 是 Linux 網(wǎng)絡(luò)數(shù)據(jù)包過濾的框架,而 iptables 就是建立在 Netfilter 之上的。所以,先了解 Netfilter 的實(shí)現(xiàn)對(duì)分析 iptables 的實(shí)現(xiàn)有非常大的幫助。

而在下一章中,我們將會(huì)繼續(xù)分析 iptables 的實(shí)現(xiàn)。

責(zé)任編輯:武曉燕 來源: Linux內(nèi)核那些事
相關(guān)推薦

2011-03-15 12:47:11

netfilteriptables

2011-03-15 15:47:30

netfilteriptables安裝

2011-03-15 10:00:01

NetfilterIPTables

2011-03-15 15:47:26

netfilteriptables

2011-03-15 09:10:43

iptables防火墻

2011-03-15 15:51:12

netfilteriptables

2010-12-07 09:51:43

Linux安全性netfilteriptables

2011-03-15 15:47:34

netfilteriptables

2011-03-15 10:48:47

2011-03-15 14:26:23

iptablesNAT

2011-03-15 09:10:47

iptablesNAT

2011-03-16 13:29:33

iptables 端口

2011-03-16 09:05:29

iptablesNAT

2011-03-16 09:05:32

RedhatiptablesNAT

2011-03-16 09:05:33

2011-03-16 10:52:20

2011-03-16 09:05:53

NATiptables

2011-07-05 14:34:23

2022-10-27 20:31:19

iptablesnetfilter

2011-07-13 10:30:09

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)