Netfilter框架的設計與實現(xiàn)_第1頁
Netfilter框架的設計與實現(xiàn)_第2頁
Netfilter框架的設計與實現(xiàn)_第3頁
Netfilter框架的設計與實現(xiàn)_第4頁
Netfilter框架的設計與實現(xiàn)_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、Netfilter框架的設計與實現(xiàn)1. 什么是NetfilerLinux 從2.4.X 開始,引入了Netfilter,代替了原來的ipchain,什么是Netfilter呢?有人將它稱為“Linux下一個優(yōu)秀的防火墻工具”,這樣 講,有一定的道理,但是卻是很片面的。Netfilter 更準確地講是Linux 內(nèi)核中,一個包過濾框架,默認地,它在這個框架上實現(xiàn)了包過濾、狀態(tài)檢測、網(wǎng)絡地址轉換和包標記等多種功能,因為它設計的開放性,任何有內(nèi)核開發(fā)經(jīng)驗的開 發(fā)人員,也可以很容易地利用它提供接口,在內(nèi)核的數(shù)據(jù)鏈路層、網(wǎng)絡層,實現(xiàn)自己的功能模塊。Netfilter的用戶空間管理工具,是著名的 ipta

2、bles 工具套件。Netfilter框架之所以能實現(xiàn)許多強大的功能,是因為它在內(nèi)核若干網(wǎng)絡轉發(fā)的關鍵函數(shù),設計了許多巧妙的鉤子函數(shù),比如數(shù)據(jù)轉發(fā),由兩個主要函數(shù)A 和B函數(shù)實現(xiàn),流程為A->B ,現(xiàn)在改變?yōu)锳->鉤子函數(shù)->B,就這么簡單,在本章里,就讓我們來看看Netfilter框架的設計與實現(xiàn)。2. 從NF_HOOK 談起在整個Netfilter中,NF_HOOK宏占有重要的作用,它定義在Netfilter.h中:CODE:/* This is gross, but inline doesn't cut it for avoiding the function

3、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 NF_HOOK_THRESH(pf, hoo

4、k, 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, hook, &(skb), i

5、ndev, 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;)#endif一開始就擺出這么長一個宏出來,這個

6、宏稍長了一點,縮短來看:CODE:#ifdef CONFIG_NETFILTER_DEBUG        #else        #endif我們暫且忽略用于調(diào)試作用的語句,把 #else中NF_HOOK的定義提取出來:CODE:#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                         &#

7、160;     (int _ret;                                                                       

8、if (list_empty(&nf_hookspfhook) |                                                   (_ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN) =

9、 1)         _ret = (okfn)(skb);                                                       _ret;)首 先來看這個宏里邊的二維數(shù)組nf_hookspfhook ,其中pf對應協(xié)

10、議簇,hook對應了某個hook點,比如ipv4 協(xié)議簇(PF_INET)下有一個鉤子NF_IP_PRE_ROUTING(路由查找之前),那么這個Hook點對應的這個二維數(shù)組中的元素就是 nf_hooksPF_INET NF_IP_PRE_ROUTING。nf_hooks數(shù)組是一個struct nf_hooks_ops 結構,這個結構有一個hook成員,指向這個hook點的hook函數(shù)。另一方面,同一個hook點,可能同時注冊了多個hook,所以,結構 struct nf_hooks_ops 中有一個list成員,用來維護一個hook點的鏈表。該結構定義如下:CODE:struct nf_ho

11、ok_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)先級;在一個結構中,內(nèi)嵌一個struct list_head 類型的list成員,用來維護一個雙向鏈表,是Linux 內(nèi)核雙向鏈表的

12、標準用法。而list_empty函數(shù)用以判斷這個鏈表是否為空。所以,整個NF_HOOK,其含義為:如果當前pf協(xié)議的當前hook類型沒有定義鉤子函數(shù),則直接執(zhí)行okfn指向的函數(shù),否則,就調(diào)用函數(shù)nf_hook_slow函數(shù),當該函數(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, dev, NULL,ip_rcv_fini

13、sh);對照上面分析的NF_HOOK的實現(xiàn),我們可以得出以下結論:在內(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ù)轉發(fā)的關鍵節(jié)點,都放置了NF_HOOK。其中,Hook的類型,可以分為五類:

14、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     

15、    /路由后,進入本機的數(shù)據(jù)/* If the packet is destined for another interface. */#define NF_IP_FORWARD                2          /路由后,本機轉發(fā)的數(shù)據(jù)/* Packets coming from a local process. */#define NF_IP_LOCAL_OUT        &#

16、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)三種不同類型的包過濾,另外兩個表也與此類似。3.

17、 Filter表及鉤子函數(shù)的注冊內(nèi)核在網(wǎng)絡堆棧的重要節(jié)點,引入了NF_HOOK宏,搭起了整個Netfilter的框架,但是NF_HOOK宏事實上僅是一個轉向,更重要的內(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點,注冊相應的鉤子函數(shù),在內(nèi)核filter模塊的初始化函數(shù)(iptable_filter.c),完成了 這一功能:

18、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_register_table(&pack

19、et_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 (ret < 0) goto clea

20、nup_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_filter的定義和賦值:CODE:sta

21、tic struct ipt_table packet_filter = .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .lock = RW_LOCK_UNLOCKED, .me = THIS_MODULE;它是一個struct ipt_table類型,內(nèi)核用結構struct ipt_table(ip_tables.h)來描述表:CODE:struct ipt_table /*鏈表成員*/ struct list_head list; /* 表名,如"filter"、"nat"

22、;等,為了滿足自動模塊加載的設計,包含該表的模塊應命名為iptable_'name'.o */ char nameIPT_TABLE_MAXNAMELEN; /* 位向量,表示當前表影響了哪些(個)HOOK 類型*/ unsigned int valid_hooks; /* 讀寫鎖,初始為打開狀態(tài) */ rwlock_t lock; /* iptable的數(shù)據(jù)區(qū)*/ struct ipt_table_info *private; /* 是否在模塊中定義,若否,則為NULL */ struct module *me;先來看成員valid_hook,它 表示當前表影響了哪些hook

23、 類型,我們前邊談到,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)一個簡單的按位或,對應了我們提到的filter表所影響的三個Hook點。這個結構包含了表的名稱,以及表所影響的Hook類型外,并沒有其它實質性的東西,諸如我們關心的過濾規(guī)則等等,所以,private成

24、員就成一個讓人感興趣的目標。Private是一個struct ipt_table_info結構類型,該結構是實際描述表具體屬性的數(shù)據(jù)結構(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的規(guī)則入口相對于下面的entries變量的

25、偏移量*/ unsigned int hook_entryNF_IP_NUMHOOKS; /* 與hook_entry相對應的規(guī)則表上限偏移量,當無規(guī)則錄入時,相應的hook_entry和underflow均為0 */ unsigned int underflowNF_IP_NUMHOOKS; /* 每個CPU的Hook點規(guī)則表入口 */ char entries0 _cacheline_aligned;ipt_register_table 函數(shù)調(diào)用中的第二個變量initial_table,它是表的初始化時使用的模板,其體體積相當?shù)凝嫶?,這里暫時只用到了其repl成員,它是一個struct i

26、pt_replace結構,我們先來看這個成員賦值: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_FORWARD = sizeof(struct

27、 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ù)結構,除了這里講到的注冊表時作為初始化模板,用戶空間通過系統(tǒng)調(diào)用來操作表的時候也要用到這個結構類型的變量做為參數(shù):CODE:struct ipt_

28、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 int size; /* Hook en

29、try points. */ unsigned int hook_entryNF_IP_NUMHOOKS; /* Underflow points. */ unsigned int underflowNF_IP_NUMHOOKS; /* 這個結構不同于ipt_table_info之處在于,它還要保存表的舊的規(guī)則信息: */ /* Number of counters (must be equal to current number of entries). */ unsigned int num_counters; /* The old entries' counters. */ st

30、ruct ipt_counters _user *counters; /* The entries (hang off end: not really an array). */ struct ipt_entry entries0;結合上面的初始化賦值,我們可以對應過來:name :"filter"/filter表所影響的Hook類型valid_hooks:FILTER_VALID_HOOKS/初始化的規(guī)則數(shù)為4條,每個Hook類型(對應用戶空間的“鏈”)初始化一條,并以一條“錯誤的規(guī)則”表示結束num_entries:4/標準的規(guī)則用struct ipt_standard

31、,錯誤的規(guī)則用struct ipt_error描述size:sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error)/計算初始的各Hook點對應的初始規(guī)則的偏移值hook_entryNF_IP_NUMHOOKS:        NF_IP_LOCAL_IN = 0,        NF_IP_FORWARD = sizeof(struct ipt_standard),        NF_IP_LOCAL_OUT =

32、 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 /因為是初始化模塊,不存在舊表,所以,這些保存舊表信息的參數(shù)均為空num_counters:

33、0counters:NULLentries0: OK,ipt_register_table函數(shù)的兩個參數(shù),前一個包含了表的基本信息,包含了表名,所影響的Hook類型,規(guī)則長度,入口等等,后一個是一個表的模塊,即初始化的規(guī)則等等,有了這些基礎,我們來看表的注冊的實現(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_table_info bootstrap =

34、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ù)將newinfo表示的table的各個規(guī)則進行邊界檢查,

35、然后對于* newinfo所指的ipt_talbe_info結構中的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 = down_interruptible(&a

36、mp;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, &ret) goto free_un

37、lock; duprintf("table->private->number = %un", table->private->number); /* 保存初始規(guī)則計數(shù)器 */ table->private->initial_entries = table->private->number; rwlock_init(&table->lock); /*將表添加進表的鏈表當中*/ list_prepend(&ipt_tables, table);unlock: up(&ipt_mutex); retur

38、n 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點對應的鉤子函數(shù):CODE:static struct nf_hook_ops ipt_ops = .hook = ipt_hook, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_LOCAL_IN, .priority

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論