版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、Netfilter框架的設(shè)計與實現(xiàn)1. 什么是NetfilerNetfilter框架之所以能實現(xiàn)許多強大的功能,是因為它在內(nèi)核若干網(wǎng)絡(luò)轉(zhuǎn)發(fā)的關(guān)鍵函數(shù),設(shè)計了許多巧妙的鉤子函數(shù),比如數(shù)據(jù)轉(zhuǎn)發(fā),由兩個主要函數(shù)A 和B函數(shù)實現(xiàn),流程為A->B ,現(xiàn)在改變?yōu)锳->鉤子函數(shù)->B,就這么簡單,在本章里,就讓我們來看看Netfilter框架的設(shè)計與實現(xiàn)。2. 從NF_HOOK 談起在整個Netfilter中,NF_HOOK宏占有重要的作用,它定義在Netfilter.h中:CODE:/* This is gross, but inline doesn't cut it for a
2、voiding the function call in fast path: gcc doesn't inline (needs value tracking?). -RR */#ifdef CONFIG_NETFILTER_DEBUG#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (int _ret; if (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN) = 1) _ret = (okfn)(skb); _ret;)#define N
3、F_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) (int _ret; if (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh) = 1) _ret = (okfn)(skb); _ret;)#else#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (int _ret; if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf
4、, hook, &(skb), indev, outdev, okfn, INT_MIN) = 1) _ret = (okfn)(skb); _ret;)#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) (int _ret; if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh) = 1) _ret = (okfn)(skb); _ret;)#e
5、ndif一開始就擺出這么長一個宏出來,這個宏稍長了一點,縮短來看:CODE:#ifdef CONFIG_NETFILTER_DEBUG #else #endif我們暫且忽略用于調(diào)試作用的語句,把 #else中NF_HOOK的定義提取出來:CODE:#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) &
6、#160; (int _ret;
7、0; if (list_empty(&nf_hookspfhook) | (_ret=nf_hook_slow(pf, hook, &(skb), indev, out
8、dev, okfn, INT_MIN) = 1) _ret = (okfn)(skb); _ret;)首 先來看這個宏里邊的二維數(shù)組nf
9、_hookspfhook ,其中pf對應(yīng)協(xié)議簇,hook對應(yīng)了某個hook點,比如ipv4 協(xié)議簇(PF_INET)下有一個鉤子NF_IP_PRE_ROUTING(路由查找之前),那么這個Hook點對應(yīng)的這個二維數(shù)組中的元素就是 nf_hooksPF_INET NF_IP_PRE_ROUTING。nf_hooks數(shù)組是一個struct nf_hooks_ops 結(jié)構(gòu),這個結(jié)構(gòu)有一個hook成員,指向這個hook點的hook函數(shù)。另一方面,同一個hook點,可能同時注冊了多個hook,所以,結(jié)構(gòu) struct nf_hooks_ops 中有一個list成員,用來維護一個hook點的鏈表。該結(jié)構(gòu)定
10、義如下:CODE:struct nf_hook_ops struct list_head list; /鏈表成員 /* User fills in from here down. */ nf_hookfn *hook; /鏈子函數(shù)指針 struct module *owner; int pf; /協(xié)議簇,對于ipv4而言,是PF_INET int hooknum; /hook類型 /* Hooks are ordered in ascending priority. */ int priority; /優(yōu)先級;在一個結(jié)構(gòu)中,內(nèi)嵌一個struct list_head 類型的list成員,用來維護
11、一個雙向鏈表,是Linux 內(nèi)核雙向鏈表的標(biāo)準(zhǔn)用法。而list_empty函數(shù)用以判斷這個鏈表是否為空。所以,整個NF_HOOK,其含義為:如果當(dāng)前pf協(xié)議的當(dāng)前hook類型沒有定義鉤子函數(shù),則直接執(zhí)行okfn指向的函數(shù),否則,就調(diào)用函數(shù)nf_hook_slow函數(shù),當(dāng)該函數(shù)返回值為1,仍繼續(xù)調(diào)用okfn指向的函數(shù)舉個例子來講,ip_input.c中,ip_rcv 函數(shù)用于處理本機接受的數(shù)據(jù)包,處理完成后,按照正常順序會調(diào)用 ip_rcv_finish函數(shù),我們來看ip_rcv的最后一句:CODE:return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb,
12、dev, NULL,ip_rcv_finish);對照上面分析的NF_HOOK的實現(xiàn),我們可以得出以下結(jié)論:在內(nèi)核收到一個發(fā)往本機的數(shù)據(jù)包,會判斷PF_INET 協(xié)議的NF_IP_PRE_ROUTING HOOK類型下是否注冊有鉤子函數(shù),如果沒有,則直接繼續(xù)執(zhí)行ip_rcv_finish,如果有,則調(diào)用nf_hook_slow函數(shù),進而進一步調(diào)用 已注冊的鉤子函數(shù),再根據(jù)其返回值,看是否還需要繼續(xù)執(zhí)行ip_rcv_finish。這就是整個Linux內(nèi)核Netfilter Hook的含義。仔細閱讀內(nèi)核源碼,可以發(fā)現(xiàn),不僅在ip_input.c中,內(nèi)核在每一個數(shù)據(jù)轉(zhuǎn)發(fā)的關(guān)鍵節(jié)點,都放置了NF_HO
13、OK。其中,Hook的類型,可以分為五類:CODE:/* IP Hooks */* After promisc drops, checksum checks. */#define NF_IP_PRE_ROUTING 0 /路由前,進入本機的數(shù)據(jù)/* If the packet is destined for this box. */#define NF_IP_LOCAL_IN 1
14、 /路由后,進入本機的數(shù)據(jù)/* If the packet is destined for another interface. */#define NF_IP_FORWARD 2 /路由后,本機轉(zhuǎn)發(fā)的數(shù)據(jù)/* Packets coming from a local process. */#define NF_IP_LOCAL_OUT
15、160; 3 /路由前,本機本地進程發(fā)出的數(shù)據(jù)/* Packets about to hit the wire. */#define NF_IP_POST_ROUTING 4 /路由后,本機發(fā)出的數(shù)據(jù)我們可以從上圖中看出,我們常用的數(shù)據(jù)包過濾的表filter,在NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT三處注冊了鉤子函數(shù),以實現(xiàn)三種不
16、同類型的包過濾,另外兩個表也與此類似。3. Filter表及鉤子函數(shù)的注冊內(nèi)核在網(wǎng)絡(luò)堆棧的重要節(jié)點,引入了NF_HOOK宏,搭起了整個Netfilter的框架,但是NF_HOOK宏事實上僅是一個轉(zhuǎn)向,更重要的內(nèi)容是, “內(nèi)核是如何注冊鉤子函數(shù)以及如何使用它們?”。內(nèi)核默認的三個表,從框架的角度上來看,這些動作都是一致,我們以filter表為例,來回答這個問題。要在內(nèi)核中使用filter表,首先要向內(nèi)核注冊這個表,然后該表在NF_IP_LOCAL_IN、NF_IP_FORWARD、 NF_IP_LOCAL_OUT三個Hook點,注冊相應(yīng)的鉤子函數(shù),在內(nèi)核filter模塊的初始化函數(shù)(iptabl
17、e_filter.c),完成了 這一功能:CODE:static int _init init(void) int ret; if (forward < 0 | forward > NF_MAX_VERDICT) printk("iptables forward must be 0 or 1n"); return -EINVAL; /* Entry 1 is the FORWARD hook */ initial_table.entries1.target.verdict = -forward - 1; /* 注冊filter 表*/ ret = ipt_reg
18、ister_table(&packet_filter, &initial_table.repl); if (ret < 0) return ret; /*依次注冊filter表的三個鉤子*/ ret = nf_register_hook(&ipt_ops0); if (ret < 0) goto cleanup_table; ret = nf_register_hook(&ipt_ops1); if (ret < 0) goto cleanup_hook0; ret = nf_register_hook(&ipt_ops2); if (
19、ret < 0) goto cleanup_hook1; return ret;cleanup_hook1: nf_unregister_hook(&ipt_ops1);cleanup_hook0: nf_unregister_hook(&ipt_ops0);cleanup_table: ipt_unregister_table(&packet_filter); return ret;表的注冊表的注冊,是通過調(diào)用ipt_register_table 函數(shù)來實現(xiàn)的,我們先來看它的兩個參數(shù)packet_filter和initial_table.repl。packet_
20、filter的定義和賦值:CODE:static struct ipt_table packet_filter = .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .me = THIS_MODULE;它是一個struct ipt_table類型,內(nèi)核用結(jié)構(gòu)struct ipt_table(ip_tables.h)來描述表:CODE:struct ipt_table /*鏈表成員*/ struct list_head list; /* 表名,如"filter
21、"、"nat"等,為了滿足自動模塊加載的設(shè)計,包含該表的模塊應(yīng)命名為iptable_'name'.o */ char nameIPT_TABLE_MAXNAMELEN; /* 位向量,表示當(dāng)前表影響了哪些(個)HOOK 類型*/ unsigned int valid_hooks; /* 讀寫鎖,初始為打開狀態(tài) */ rwlock_t lock; /* iptable的數(shù)據(jù)區(qū)*/ struct ipt_table_info *private; /* 是否在模塊中定義,若否,則為NULL */ struct module *me;先來看成員valid_
22、hook,它 表示當(dāng)前表影響了哪些hook 類型,我們前邊談到,filter 表影響了NF_IP_LOCAL_IN等三個Hook點,這里給它賦的值是FILTER_VALID_HOOKS,來看這個宏的實現(xiàn):CODE:#define FILTER_VALID_HOOKS (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT)一個簡單的按位或,對應(yīng)了我們提到的filter表所影響的三個Hook點。這個結(jié)構(gòu)包含了表的名稱,以及表所影響的Hook類型外,并沒有其它實質(zhì)性的東西,諸如我們
23、關(guān)心的過濾規(guī)則等等,所以,private成員就成一個讓人感興趣的目標(biāo)。Private是一個struct ipt_table_info結(jié)構(gòu)類型,該結(jié)構(gòu)是實際描述表具體屬性的數(shù)據(jù)結(jié)構(gòu)(net/ipv4/netfilter/ip_tables.c):CODE:/* The table itself */struct ipt_table_info /* 每個表的大小 */ unsigned int size; /* 表中的規(guī)則數(shù) */ unsigned int number; /* 初始的規(guī)則數(shù),用于模塊計數(shù) */ unsigned int initial_entries; /* 記錄所影響的HOOK
24、的規(guī)則入口相對于下面的entries變量的偏移量*/ unsigned int hook_entryNF_IP_NUMHOOKS; /* 與hook_entry相對應(yīng)的規(guī)則表上限偏移量,當(dāng)無規(guī)則錄入時,相應(yīng)的hook_entry和underflow均為0 */ unsigned int underflowNF_IP_NUMHOOKS; /* 每個CPU的Hook點規(guī)則表入口 */ char entries0 _cacheline_aligned;ipt_register_table 函數(shù)調(diào)用中的第二個變量initial_table,它是表的初始化時使用的模板,其體體積相當(dāng)?shù)凝嫶?,這里暫時只用到
25、了其repl成員,它是一個struct ipt_replace結(jié)構(gòu),我們先來看這個成員賦值:CODE:static struct struct ipt_replace repl; struct ipt_standard entries3; struct ipt_error term; initial_table _initdata = "filter", FILTER_VALID_HOOKS, 4, sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), NF_IP_LOCAL_IN = 0, NF_IP_FO
26、RWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 , NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 ,0, NULL, ,struct ipt_replace是我們要提到的第三個重要的數(shù)據(jù)結(jié)構(gòu),除了這里講到的注冊表時作為初始化模板,用戶空間通過系統(tǒng)調(diào)用來操作表的時候也要用到這個結(jié)構(gòu)類型的變量
27、做為參數(shù):CODE:struct ipt_replace /*前面的部份,類似于ipt_table_info*/ /* Which table. */ char nameIPT_TABLE_MAXNAMELEN; /* Which hook entry points are valid: bitmask. You can't change this. */ unsigned int valid_hooks; /* Number of entries */ unsigned int num_entries; /* Total size of new entries */ unsigned
28、 int size; /* Hook entry points. */ unsigned int hook_entryNF_IP_NUMHOOKS; /* Underflow points. */ unsigned int underflowNF_IP_NUMHOOKS; /* 這個結(jié)構(gòu)不同于ipt_table_info之處在于,它還要保存表的舊的規(guī)則信息: */ /* Number of counters (must be equal to current number of entries). */ unsigned int num_counters; /* The old entries
29、' counters. */ struct ipt_counters _user *counters; /* The entries (hang off end: not really an array). */ struct ipt_entry entries0;結(jié)合上面的初始化賦值,我們可以對應(yīng)過來:name :"filter"/filter表所影響的Hook類型valid_hooks:FILTER_VALID_HOOKS/初始化的規(guī)則數(shù)為4條,每個Hook類型(對應(yīng)用戶空間的“鏈”)初始化一條,并以一條“錯誤的規(guī)則”表示結(jié)束num_entries:4/標(biāo)準(zhǔn)的規(guī)
30、則用struct ipt_standard,錯誤的規(guī)則用struct ipt_error描述size:sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error)/計算初始的各Hook點對應(yīng)的初始規(guī)則的偏移值hook_entryNF_IP_NUMHOOKS: NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard),
31、60; NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 underflowNF_IP_NUMHOOKS: NF_IP_LOCAL_IN = 0, NF_IP_FORWARD = sizeof(struct ipt_standard), NF_IP_LOCAL_OUT = sizeof(struct ipt_standard) * 2 /因為是初始化模塊,不存在舊表,所以,這些保存舊表
32、信息的參數(shù)均為空num_counters:0counters:NULLentries0: OK,ipt_register_table函數(shù)的兩個參數(shù),前一個包含了表的基本信息,包含了表名,所影響的Hook類型,規(guī)則長度,入口等等,后一個是一個表的模塊,即初始化的規(guī)則等等,有了這些基礎(chǔ),我們來看表的注冊的實現(xiàn):CODE:int ipt_register_table(struct ipt_table *table, const struct ipt_replace *repl) int ret; struct ipt_table_info *newinfo; static struct ipt_ta
33、ble_info bootstrap = 0, 0, 0, 0 , 0 , ; /*為每個CPU分配規(guī)則空間*/ newinfo = vmalloc(sizeof(struct ipt_table_info) + SMP_ALIGN(repl->size) * num_possible_cpus(); if (!newinfo) return -ENOMEM; /*將規(guī)則項拷貝到新表項的第一個cpu空間里面*/ memcpy(newinfo->entries, repl->entries, repl->size); /* translate_table函數(shù)將newinf
34、o表示的table的各個規(guī)則進行邊界檢查,然后對于* newinfo所指的ipt_talbe_info結(jié)構(gòu)中的hook_entries和underflows賦予正確的值,最* 后將表項向其他cpu拷貝*/ ret = translate_table(table->name, table->valid_hooks, newinfo, repl->size, repl->num_entries, repl->hook_entry, repl->underflow); if (ret != 0) vfree(newinfo); return ret; ret =
35、down_interruptible(&ipt_mutex); if (ret != 0) vfree(newinfo); return ret; /* 如果注冊的table已經(jīng)存在,釋放空間 并且遞減模塊計數(shù) */ if (list_named_find(&ipt_tables, table->name) ret = -EEXIST; goto free_unlock; /* 用新的table項替換舊的table項 */ table->private = &bootstrap; if (!replace_table(table, 0, newinfo, &
36、amp;ret) goto free_unlock; duprintf("table->private->number = %un", table->private->number); /* 保存初始規(guī)則計數(shù)器 */ table->private->initial_entries = table->private->number; rwlock_init(&table->lock); /*將表添加進表的鏈表當(dāng)中*/ list_prepend(&ipt_tables, table);unlock: up(&
37、amp;ipt_mutex); return ret;free_unlock: vfree(newinfo); goto unlock;這里調(diào)用的一些重要的函數(shù),我們后面會陸續(xù)分析到,實際上表的注冊,就是一個建立/維護表的鏈表的過程,是最終通過調(diào)用內(nèi)核鏈表函數(shù) list_prepend 來實現(xiàn)的,經(jīng)過這樣的注冊后,初始的filter表,就被添加至表的鏈表中了,鏈表首部是全局變量ipt_tables。同樣地,在NAT表的初始化函數(shù)ip_nat_rule_init中,有:ret = ipt_register_table(&nat_table, &nat_initial_table.repl);表mangle的初始化函數(shù)init中,有:ret = ipt_register_table(&packet_mangler, &initial_table.repl);注冊表的鉤子函數(shù)向內(nèi)核注冊了表后,下一步就是注冊表的Hook點對應(yīng)的鉤子函數(shù):CODE:static struct nf_hook_ops ipt_ops = .hook = ipt_hook, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_LOCAL_IN, .priority = NF_IP_PRI_FILTER, , .hook
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 招投標(biāo)項目供應(yīng)鏈管理優(yōu)化
- 商業(yè)廣場樓面耐磨施工合同
- 籃球場水電布線施工協(xié)議
- 石油開采挖機計時租賃合同
- 商務(wù)樓宇治安管理規(guī)則
- 生物科技項目跟投管理
- 舞臺座位分配指南
- 藝術(shù)展覽設(shè)計審查策略
- 言語康復(fù)治療師年終總結(jié)
- YY/T 0471.3-2004接觸性創(chuàng)面敷料試驗方法 第3部分:阻水性
- GB/T 6344-2008軟質(zhì)泡沫聚合材料拉伸強度和斷裂伸長率的測定
- GB/T 193-2003普通螺紋直徑與螺距系列
- 多旋翼無人機-法律法規(guī)
- 催乳穴位及手法課件
- 2023年新改版教科版六年級下冊科學(xué)全冊知識點 (共兩套)
- 隧道圍巖分級(表)
- 國家開放大學(xué)《液壓與氣壓傳動》形考任務(wù)1-2參考答案
- 食道超聲在心臟外科手術(shù)中的應(yīng)用課件
- 9《 復(fù)活》課件17張PPT 統(tǒng)編版高中語文選擇性必修上冊第三單元
- 血流動力學(xué)不穩(wěn)定骨盆骨折急診處理
評論
0/150
提交評論