netfilter整體架構解析初步

原文地址:http://blog.chinaunix.net/uid-22227409-id-2656909.html

1. 掛接點(hooknum)
 
netfilter是Linux2.4/2.6內核中自帶的防火牆架構,定義了5個掛接點:
NF_IP_PRE_ROUTING-------->NF_IP_FORWARD--------->NF_IP_POST_ROUTING
                     |                    ^
                     |                    |
                     V                    |
                 NF_IP_LOCAL_IN       NF_IP_LOCAL_OUT
 
netfilter定義了一個二維的鏈表頭數組struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]來表示所有協議族的各個掛接點,NPROTO值爲32,可表示linux所支持所有32個協議族(include/linux/socket.h文件中定義),也就是使用socket(2)函數的第一個參數的值,如互聯網的TCP/IP協議族PF_INET(2)。每個協議族有NF_MAX_HOOKS(8)個掛接點,但實際只用瞭如上所述的5個,數組中每個元素表示一個協議族在一個掛接點的處理鏈表頭,。
 
以下分析使用2.4.26內核中的netfilter代碼。
 
在IPv4(PF_INET協議族)下,各掛接點定義在:
NF_IP_PRE_ROUTING,在IP棧成功接收sk_buff包後處理,掛接點在在net/ipv4/ip_input.c的函數
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)中定義:
 return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
         ip_rcv_finish);
 
NF_IP_LOCAL_IN,在對接收的sk_buff包完成路由分類判斷是到達自身的包後進行處理,掛接點在在net/ipv4/ip_input.c的函數int ip_local_deliver(struct sk_buff *skb)中定義:
 
 return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
         ip_local_deliver_finish);
 
NF_IP_FORWARD,在對接收的sk_buff包完成路由分類判斷是需要進行轉發的包進行處理,掛接點在在net/ipv4/ip_input.c的函數int ip_forward(struct sk_buff *skb)中定義:
 
 return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2,
         ip_forward_finish);
 
NF_IP_LOCAL_OUT,在對自身發出的包進行處理,掛接點在在net/ipv4/ip_output.c的函數
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
     u32 saddr, u32 daddr, struct ip_options *opt)
中定義:
 
 return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
         output_maybe_reroute);

NF_IP_POST_ROUTING,在IP棧成功接收sk_buff包後處理,掛接點在在net/ipv4/ip_output.c的函數
__inline__ int ip_finish_output(struct sk_buff *skb)中定義:
 
 return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
         ip_finish_output2);
 
2. 掛接點操作
 
掛接點的操作由結構struct nf_hook_ops定義:
include/linux/netfilter.h
struct nf_hook_ops
{
 struct list_head list;
 /* User fills in from here down. */
 nf_hookfn *hook;
 int pf;
 int hooknum;
 /* Hooks are ordered in ascending priority. */
 int priority;
};
 
數組中的元素說明如下:
struct list_head list: 鏈表頭,用於將此結構接入操作鏈表
nf_hookfn *hook:用戶定義的掛接處理函數
int pf:協議族
hooknum:掛接點
priority:優先級
 
每個struct nf_hook_ops結構需要掛接到nf_hooks數組中的某個鏈表中才起作用,每個協議族的各種處理形成一個處理鏈表,鏈表上可以掛接多個節點,每個節點是一個數據處理結構(struct nf_hook_ops),用來描述對數據包進行如何處理,這些節點根據優先級順序進行排序處理,優先級是有符號的32位數,值越小優先級越高,如果優先級相同,則按掛接的順序依次處理,netfilter預定義了以下優先級:
 
 NF_IP_PRI_FIRST = INT_MIN,
 NF_IP_PRI_CONNTRACK = -200,
 NF_IP_PRI_MANGLE = -150,
 NF_IP_PRI_NAT_DST = -100,
 NF_IP_PRI_FILTER = 0,
 NF_IP_PRI_NAT_SRC = 100,
 NF_IP_PRI_LAST = INT_MAX,
 
由此可見,netfilter的處理順序是先連接跟蹤、然後是mangle處理,再目的NAT(PREROUTING),然後是過濾(FILTER),然後是源NAT(POSTROUTING),這就是爲什麼mangle鏈的規則會先執行。
 
在各個處理點處理數據包時,如果發現需要丟棄數據包,那麼數據包就被立即釋放而不再進入後面的處理點;如果該處理點的最終結論是接受,那該數據包還會繼續進入下一處理點進行匹配檢查,所以對於最終通過防火牆的數據包,是經過了所有處理點的匹配的。

3. 連接跟蹤
 
連接跟蹤的struct nf_hook_ops結構在net/ipv4/netfilter/ip_conntrack_standalone.c中定義,
這是對用戶隱藏的,也就是用戶不能通過iptables規則對此操作點進行配置,是有系統自動完成的。
 
定義如下:
static struct nf_hook_ops ip_conntrack_in_ops
= { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_PRE_ROUTING,
 NF_IP_PRI_CONNTRACK };
static struct nf_hook_ops ip_conntrack_local_out_ops
= { { NULL, NULL }, ip_conntrack_local, PF_INET, NF_IP_LOCAL_OUT,
 NF_IP_PRI_CONNTRACK };
 
分別掛接在外部數據包進入(NF_IP_PRE_ROUTING)和自身數據發出(NF_IP_LOCAL_OUT)時進行處理,其功能就是判斷該數據包是什麼狀態,填充該數據包struct sk_buff結構中struct nf_ct_info *nfct項的值,維護連接狀態表,從而實現狀態檢測,具體處理過程分析可見另一篇文章:Linux下如何實現狀態檢測。
 
4. 規則表(table)
 
爲了定義每個處理點上要執行哪些規則,netfilter定義了表(table)的概念,每個表由一個struct ipt_table來描述,如缺省的filter/nat/mangle表,每個表可單獨分成幾個規則鏈,分別在幾個掛接點起作用,如filter表是在只在NF_IP_LOCAL_IN/NF_IP_LOCAL_OUT/NF_IP_FORWARD上起作用,然後通過函數ipt_do_table()來實現對某個表中某個hooknum的規則集進行匹配處理。而由結構struct nf_hook_ops所定義的各個處理點的處理函數都是直接或間接的調用了ipt_do_table()函數來實現對規則集的調用。
 
下面是系統缺省的三個表的定義情況:
-------+---------------------------------+-------------------------------------
 table |  table definition               | file name
-------+---------------------------------+-------------------------------------
filter | struct ipt_table packet_filter  | net/ipv4/netfilter/iptable_filter.c
       |
hook   | NF_IP_LOCAL_IN、NF_IP_LOCAL_OUT、NF_IP_FORWARD
       |
ops    | struct nf_hook_ops ipt_ops[]
       | net/ipv4/netfilter/iptable_filter.c
       |
       |NF_IP_LOCAL_IN: ipt_hook()
       |    調用ipt_do_table()函數與filter表的INPUT鏈掛鉤
       |
       |NF_IP_LOCAL_FORWARD: ipt_hook()
       |    調用ipt_do_table()函數與filter表的FORWARD鏈掛鉤
       |
       |NF_IP_LOCAL_OUT: ipt_local_out_hook()
       |    調用ipt_do_table()函數與filter表的OUTPUT鏈掛鉤
       |
-------+---------------------------------+-------------------------------------
nat    | struct ipt_table nat_table      | net/ipv4/netfilter/ip_nat_rule.c
       |                                
hook   | NF_IP_PRE_ROUTING_IN、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING
       |
ops    | net/ipv4/netfilter/ip_nat_standalone.c
       |
       |NF_IP_PRE_ROUTING:
       |    struct nf_hook_ops ip_nat_in_ops, ip_nat_fn()
       |      調用ip_nat_rule_find()函數
       |        調用ipt_do_table()函數與nat表的PREROUTING鏈掛鉤
       |
       |NF_IP_POST_ROUTING:
       |    struct nf_hook_ops ip_nat_out_ops,ip_nat_out()
       |      調用ip_nat_fn()
       |        調用ip_nat_rule_find()函數
       |          調用ipt_do_table()函數與nat表的POSTROUTING鏈掛鉤
       |
-------+---------------------------------+-------------------------------------
mangle | struct ipt_table packet_mangler | net/ipv4/netfilter/iptable_mangle.c
       |                                 |
hook   | 全部五個都有
       |
ops    | struct nf_hook_ops ipt_ops[]
       | net/ipv4/netfilter/iptable_mangle.c
       |
       |NF_IP_PRE_ROUTING: ip_route_hook()
       |    調用ipt_do_table()函數與mangle表的PREROUTING鏈掛鉤
       |
       |NF_IP_LOCAL_IN: ip_route_hook()
       |    調用ipt_do_table()函數與mangle表的INPUT鏈掛鉤
       |
       |NF_IP_FORWARD: ipt_hook()
       |    調用ipt_do_table()函數與mangle表的FORWARD鏈掛鉤
       |
       |NF_IP_LOCAL_OUT: ipt_local_hook()
       |    調用ipt_do_table()函數與mangle表的OUTPUT鏈掛鉤
       |
       |NF_IP_POST_ROUTING: ip_route_hook()
       |    調用ipt_do_table()函數與mangle表的POSTROUTING鏈掛鉤
       |
-------+---------------------------------+-------------------------------------
 
每個數據處理表(table)中就是定義各自的規則集,是用動態長度的數組的形式保存,每個數組節點是一個規則,規則用struct ipt_entry結構進行描述,每條規則除了基本項外,其他附加匹配條件項還形成一個動態長度的匹配數組,struct ipt_entry中保存數組頭的地址,每個匹配用結構struct ipt_match描述。規則的動作如果是擴展動作的話,用struct ipt_target描述。
 
用戶可以自己定義自己的新的表,可以以缺省表爲藍本,然後定義新的struct nf_hook_ops操作節點,在該操作節點中調用ipt_do_table()函數將該ops和新表聯繫起來,這樣就可以用iptables定義新的規則集。如果不定義新表,用戶也可以在ops處理函數中對包直接進行判斷處理,適合需要對包進行固定方式處理的場合。
 
5. 總結
 
netfilter架構以nf_hooks數組爲基點,掛接在內核的協議處理的幾個基本點上,通過鏈表方式鏈接struct nf_hook_ops處理結構,這些處理結構可以是在內核內部自動固定處理,如狀態檢測;也可以通過和struct ipt_table聯繫,通過iptables來動態配置處理規則,實現了一個擴展性很高的防火牆處理架構,不過實現細節部分仍然很複雜,各種細節功能將在後續文章裏分析。


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