1. 程式人生 > >Linux核心中Netfilter架構介紹

Linux核心中Netfilter架構介紹

NetfilterLinux 2.4.x引入的一個子系統,它作為一個通用的、抽象的框架,提供一整套的hook函式的管理機制,使得諸如資料包過濾、網路地址轉換(NAT)和基於協議型別的連線跟蹤成為了可能。Netfilter在核心中位置如下圖所示:
 
這幅圖很直觀的反應了使用者空間的iptables和核心空間的基於Netfilterip_tables模組之間的關係和其通訊方式,以及Netfilter在這其中所扮演的角色。
Netfilter
netfilter_ipv4.h中將這個五個點重新命了個名,如下圖所示:
 
在每個關鍵點上,有很多已經按照優先順序預先註冊了的回撥函式(稱為“鉤子函式”)
埋伏在這些關鍵點,形成了一條鏈。對於每個到來的資料包會依次被那些回撥函式“調戲”一番再視情況是將其放行,丟棄還是怎麼滴。但是無論如何,這些回撥函式最後必須向Netfilter報告一下該資料包的死活情況,因為畢竟每個資料包都是Netfilter從人家協議棧那兒借調過來給兄弟們Happy的,別個再怎麼滴也總得“活要見人,死要見屍”吧。每個鉤子函式最後必須向Netfilter框架返回下列幾個值其中之一:
1. NF_ACCEPT
繼續正常傳輸資料報。這個返回值告訴Netfilter:到目前為止,該資料包還是被接受的並且該資料包應當被遞交到網路協議棧的下一個階段。
2. NF_DROP
丟棄該資料報,不再傳輸。

3. NF_STOLEN
模組接管該資料報,告訴Netfilter“忘掉”該資料報。該回調函式將從此開始對資料包的處理,並且Netfilter應當放棄對該資料包做任何的處理。但是,這並不意味著該資料包的資源已經被釋放。這個資料包以及它獨自的sk_buff資料結構仍然有效,只是回撥函式從Netfilter獲取了該資料包的所有權。
4. NF_QUEUE
對該資料報進行排隊(通常用於將資料報給使用者空間的程序進行處理)
5. NF_REPEAT
再次呼叫該回調函式,應當謹慎使用這個值,以免造成死迴圈。為了讓我們顯得更專業些,我們開始做些約定:上面提到的五個關鍵點後面我們就叫它們為hook點,每個hook
點所註冊的那些回撥函式都將其稱為hook函式。
Linux 2.6
版核心的Netfilter目前支援IPv4IPv6以及DECnet等協議棧,這裡我們主要研究IPv4協議。關於協議型別,hook點,hook函式,優先順序,通過下面這個圖給大家做個詳細展示:
 
對於每種型別的協議,資料包都會依次按照hook點的方向進行傳輸,每個hook點上Netfilter又按照優先順序掛了很多hook函式。這些hook函式就是用來處理資料包用的。
Netfilter
使用NF_HOOK(include/linux/netfilter.h)巨集在協議棧內部切入到Netfilter框架中。相比於2.4版本,2.6版核心在該巨集的定義上顯得更加靈活一些,定義如下:
#define NF_HOOK(pf, hook, skb, indev, outdev,okfn) \

         NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)2
關於巨集NF_HOOK各個引數的解釋說明:
1)  pf
:協議族名,Netfilter架構同樣可以用於IP層之外,因此這個變數還可以有諸如PF_INET6PF_DECnet等名字。
2)  hook
HOOK點的名字,對於IP層,就是取上面的五個值;
3)  skb
:網路裝置資料快取區;
4) 
indev:資料包進來的裝置,以struct net_device結構表示;
5)   outdev
:資料包出去的裝置,以struct net_device結構表示;
(
後面可以看到,以上五個引數將傳遞給nf_register_hook中註冊的處理函式。)
6)   okfn:
是個函式指標,當所有的該HOOK點的所有登記函式呼叫完後,轉而走此流程。NF_HOOK_THRESH又是一個巨集:
#define NF_HOOK_THRESH(pf, hook, skb, indev,outdev, okfn,thresh)               \

({int__ret;                                                                               \
if ((__ret=nf_hook_thresh(pf, hook, &(skb),indev, outdev, okfn, thresh, 1)) == 1)\
        __ret =(okfn)(skb);                                                      \
__ret;})
我們發現NF_HOOK_THRESH巨集只增加了一個thresh引數,這個引數就是用來指定通過該巨集去遍歷鉤子函式時的優先順序,同時,該巨集內部又呼叫了nf_hook_thresh函式:
static inline int nf_hook_thresh(int pf, unsignedint hook,

                           struct sk_buff **pskb,
                           struct net_device *indev,
                           struct net_device *outdev,
                           int (*okfn)(struct sk_buff *), int thresh,
                           int cond)
{
if (!cond)
return 1;
#ifndef CONFIG_NETFILTER_DEBUG
if (list_empty(&nf_hooks[pf][hook]))
        return 1;
#endif
return nf_hook_slow(pf, hook, pskb, indev, outdev,okfn, thresh);
}
這個函式又只增加了一個引數cond,該引數為0則放棄遍歷,並且也不執行okfn函式;為1則執行nf_hook_slow去完成鉤子函式okfn的順序遍歷(優先順序從小到大依次執行)net/netfilter/core.h檔案中定義了一個二維的結構體陣列,用來儲存不同協議棧鉤子點的回撥處理函式。
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];

其中,行數NPROTO32,即目前核心所支援的最大協議簇;列數NF_MAX_HOOKS為掛載點的個數,目前在2.6核心中該值為8nf_hooks陣列的最終結構如下圖所示。
 
include/linux/socket.hIP協議AF_INET(PF_INET)的序號為2,因此我們就可以得到TCP/IP協議族的鉤子函式掛載點為:
PRE_ROUTING
     nf_hooks[2][0]
LOCAL_IN
        nf_hooks[2][1]
FORWARD
     nf_hooks[2][2]
LOCAL_OUT
      nf_hooks[2][3]
POST_ROUTING
         nf_hooks[2][4]