|
本文檔的Copyleft歸yfydz所有,使用GPL發(fā)布,可以自由拷貝,轉(zhuǎn)載,轉(zhuǎn)載時請保持文檔的完整性,嚴禁用于任何商業(yè)用途。 msn: yfydz_no1@hotmail.com 來源:http://yfydz. 1. 掛接點 netfilter是Linux2.4/2.6內(nèi)核中自帶的防火墻架構(gòu),定義了5個掛接點: NF_IP_PRE_ROUTING-------->NF_IP_FORWARD--------->NF_IP_POST_ROUTING | ^ | | V | NF_IP_LOCAL_IN NF_IP_LOCAL_OUT netfilter定義了一個二維的鏈表頭數(shù)組struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]來表示所有協(xié)議族的各個掛接點,NPROTO值為32,可表示linux所支持所有32個協(xié)議族(include/linux/socket.h文件中定義),也就是使用socket(2)函數(shù)的第一個參數(shù)的值,如互聯(lián)網(wǎng)的TCP/IP協(xié)議族PF_INET(2)。每個協(xié)議族有NF_MAX_HOOKS(8)個掛接點,但實際只用了如上所述的5個,數(shù)組中每個元素表示一個協(xié)議族在一個掛接點的處理鏈表頭,。 以下分析使用2.4.26內(nèi)核中的netfilter代碼。 在IPv4(PF_INET協(xié)議族)下,各掛接點定義在: NF_IP_PRE_ROUTING,在IP棧成功接收sk_buff包后處理,掛接點在在net/ipv4/ip_input.c的函數(shù) 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的函數(shù)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包完成路由分類判斷是需要進行轉(zhuǎn)發(fā)的包進行處理,掛接點在在net/ipv4/ip_input.c的函數(shù)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,在對自身發(fā)出的包進行處理,掛接點在在net/ipv4/ip_output.c的函數(shù) 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的函數(shù) __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. 掛接點操作 掛接點的操作由結(jié)構(gòu)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; }; 數(shù)組中的元素說明如下: struct list_head list: 鏈表頭,用于將此結(jié)構(gòu)接入操作鏈表 nf_hookfn *hook:用戶定義的掛接處理函數(shù) int pf:協(xié)議族 hooknum:掛接點 priority:優(yōu)先級 每個struct nf_hook_ops結(jié)構(gòu)需要掛接到nf_hooks數(shù)組中的某個鏈表中才起作用,每個協(xié)議族的各種處理形成一個處理鏈表,鏈表上可以掛接多個節(jié)點,每個節(jié)點是一個數(shù)據(jù)處理結(jié)構(gòu)(struct nf_hook_ops),用來描述對數(shù)據(jù)包進行如何處理,這些節(jié)點根據(jù)優(yōu)先級順序進行排序處理,優(yōu)先級是有符號的32位數(shù),值越小優(yōu)先級越高,如果優(yōu)先級相同,則按掛接的順序依次處理,netfilter預定義了以下優(yōu)先級: 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鏈的規(guī)則會先執(zhí)行。 在各個處理點處理數(shù)據(jù)包時,如果發(fā)現(xiàn)需要丟棄數(shù)據(jù)包,那么數(shù)據(jù)包就被立即釋放而不再進入后面的處理點;如果該處理點的最終結(jié)論是接受,那該數(shù)據(jù)包還會繼續(xù)進入下一處理點進行匹配檢查,所以對于最終通過防火墻的數(shù)據(jù)包,是經(jīng)過了所有處理點的匹配的。 3. 連接跟蹤 連接跟蹤的struct nf_hook_ops結(jié)構(gòu)在net/ipv4/netfilter/ip_conntrack_standalone.c中定義, 這是對用戶隱藏的,也就是用戶不能通過iptables規(guī)則對此操作點進行配置,是有系統(tǒng)自動完成的。 定義如下: 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 }; 分別掛接在外部數(shù)據(jù)包進入(NF_IP_PRE_ROUTING)和自身數(shù)據(jù)發(fā)出(NF_IP_LOCAL_OUT)時進行處理,其功能就是判斷該數(shù)據(jù)包是什么狀態(tài),填充該數(shù)據(jù)包struct sk_buff結(jié)構(gòu)中struct nf_ct_info *nfct項的值,維護連接狀態(tài)表,從而實現(xiàn)狀態(tài)檢測,具體處理過程分析可見另一篇文章:Linux下如何實現(xiàn)狀態(tài)檢測。 4. 規(guī)則表(table) 為了定義每個處理點上要執(zhí)行哪些規(guī)則,netfilter定義了表(table)的概念,每個表由一個struct ipt_table來描述,如缺省的filter/nat/mangle表,每個表可單獨分成幾個規(guī)則鏈,分別在幾個掛接點起作用,如filter表是在只在NF_IP_LOCAL_IN/NF_IP_LOCAL_OUT/NF_IP_FORWARD上起作用,然后通過函數(shù)ipt_do_table()來實現(xiàn)對某個表中某個hooknum的規(guī)則集進行匹配處理。而由結(jié)構(gòu)struct nf_hook_ops所定義的各個處理點的處理函數(shù)都是直接或間接的調(diào)用了ipt_do_table()函數(shù)來實現(xiàn)對規(guī)則集的調(diào)用。 下面是系統(tǒng)缺省的三個表的定義情況: -------+---------------------------------+------------------------------------- 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() | 調(diào)用ipt_do_table()函數(shù)與filter表的INPUT鏈掛鉤 | |NF_IP_LOCAL_FORWARD: ipt_hook() | 調(diào)用ipt_do_table()函數(shù)與filter表的FORWARD鏈掛鉤 | |NF_IP_LOCAL_OUT: ipt_local_out_hook() | 調(diào)用ipt_do_table()函數(shù)與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() | 調(diào)用ip_nat_rule_find()函數(shù) | 調(diào)用ipt_do_table()函數(shù)與nat表的PREROUTING鏈掛鉤 | |NF_IP_POST_ROUTING: | struct nf_hook_ops ip_nat_out_ops,ip_nat_out() | 調(diào)用ip_nat_fn() | 調(diào)用ip_nat_rule_find()函數(shù) | 調(diào)用ipt_do_table()函數(shù)與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() | 調(diào)用ipt_do_table()函數(shù)與mangle表的PREROUTING鏈掛鉤 | |NF_IP_LOCAL_IN: ip_route_hook() | 調(diào)用ipt_do_table()函數(shù)與mangle表的INPUT鏈掛鉤 | |NF_IP_FORWARD: ipt_hook() | 調(diào)用ipt_do_table()函數(shù)與mangle表的FORWARD鏈掛鉤 | |NF_IP_LOCAL_OUT: ipt_local_hook() | 調(diào)用ipt_do_table()函數(shù)與mangle表的OUTPUT鏈掛鉤 | |NF_IP_POST_ROUTING: ip_route_hook() | 調(diào)用ipt_do_table()函數(shù)與mangle表的POSTROUTING鏈掛鉤 | -------+---------------------------------+------------------------------------- 每個數(shù)據(jù)處理表(table)中就是定義各自的規(guī)則集,是用動態(tài)長度的數(shù)組的形式保存,每個數(shù)組節(jié)點是一個規(guī)則,規(guī)則用struct ipt_entry結(jié)構(gòu)進行描述,每條規(guī)則除了基本項外,其他附加匹配條件項還形成一個動態(tài)長度的匹配數(shù)組,struct ipt_entry中保存數(shù)組頭的地址,每個匹配用結(jié)構(gòu)struct ipt_match描述。規(guī)則的動作如果是擴展動作的話,用struct ipt_target描述。 用戶可以自己定義自己的新的表,可以以缺省表為藍本,然后定義新的struct nf_hook_ops操作節(jié)點,在該操作節(jié)點中調(diào)用ipt_do_table()函數(shù)將該ops和新表聯(lián)系起來,這樣就可以用iptables定義新的規(guī)則集。如果不定義新表,用戶也可以在ops處理函數(shù)中對包直接進行判斷處理,適合需要對包進行固定方式處理的場合。 5. 總結(jié) netfilter架構(gòu)以nf_hooks數(shù)組為基點,掛接在內(nèi)核的協(xié)議處理的幾個基本點上,通過鏈表方式鏈接struct nf_hook_ops處理結(jié)構(gòu),這些處理結(jié)構(gòu)可以是在內(nèi)核內(nèi)部自動固定處理,如狀態(tài)檢測;也可以通過和struct ipt_table聯(lián)系,通過iptables來動態(tài)配置處理規(guī)則,實現(xiàn)了一個擴展性很高的防火墻處理架構(gòu),不過實現(xiàn)細節(jié)部分仍然很復雜,各種細節(jié)功能將在后續(xù)文章里分析。 -- tech blog: http://yfydz. |
|
|
來自: mrjbydd > 《linux kernel》