亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標題: Netfilter機制分析 [打印本頁]

作者: 狼族狼心    時間: 2010-07-16 16:47
標題: Netfilter機制分析
本帖最后由 狼族狼心 于 2010-07-21 11:09 編輯

最近研究了一下Netfilter,也做個總結(jié),總結(jié)中參考了幾位大俠前面發(fā)表的文章:獨孤九賤,Minit,godbach等,在此先謝謝大家!
文中有些內(nèi)容是直接對上面幾位的Copy,內(nèi)中注明了出處,希望小小心得能對大家有所幫助!


目 錄
1        前言        4
2        學習框架劃分        5
3        鉤子函數(shù)的注冊管理        6
3.1        hook的存儲機制        6
3.2        hook的管理機制        7
4        規(guī)則表的存儲管理        8
4.1        規(guī)則表的存儲機制        8
4.2        規(guī)則的遍歷機制        15
4.3        規(guī)則表的管理        16
4.3.1        注冊        16
4.3.2        查找        17
4.3.3        檢測        18
4.3.4        替換        22
4.4        iptables的實現(xiàn)機制        22
4.4.1        命令格式        23
4.4.2        命令解析        23
4.4.3        獲取內(nèi)核規(guī)則表        27
4.4.4        按命令進行處理        33
4.4.5        規(guī)則表提交到內(nèi)核        34
4.4.6        內(nèi)核處理規(guī)則表        34
5        Netfilter執(zhí)行流程        37
5.1.1        鉤子函數(shù)和掛載點關(guān)系        37
5.1.2        執(zhí)行中函數(shù)調(diào)用關(guān)系        37
6        參考文獻        39


1        前言
本文的參考分析的源代碼版本是2.6.15,我是邊學習邊總結(jié),學習的過程中得益于Linux論壇(http://linux.chinaunix.net/bbs/)上大俠們總結(jié)分析的文檔,他山之石可以攻玉,學習過程中我也會邊學邊總結(jié),開源的發(fā)展在于共享,我也拋塊磚,望能引到玉!
由于自身水平有限,且相關(guān)的參考資料較少,因此其中的結(jié)論不能保證完全正確,如果在閱讀本文的過程中發(fā)現(xiàn)了問題歡迎及時與作者聯(lián)系。也希望能有機會和大家多多交流學習心得!
        學習netfilter主要參考了Linux論壇幾位大俠的文檔,他們分別是:獨孤九賤,Godbach,Minitab;文檔中參考處或者引用處,我會用紫色注明;

2        學習框架劃分
我把Netfilter學習分為三塊:
♠  鉤子函數(shù)的注冊管理;
♠  規(guī)則表的存儲管理;
♠  Netfilter執(zhí)行流程;
說明:
♠  鉤子函數(shù)的注冊管理:
主要說明Netfilter鉤子函數(shù)的掛載點,hook函數(shù)的保存機制,注冊方式;
♠  規(guī)則表的存儲管理;
1.        主要描述規(guī)則表的存儲機制,表,匹配,動作(table,match,target)三者之間的關(guān)系;
2.        規(guī)則表一些管理操作;
3.        用戶態(tài)iptables的實現(xiàn)原理;
♠  Netfilter執(zhí)行流程;
通過ip報文的轉(zhuǎn)發(fā)執(zhí)行過程,描述netfilter的執(zhí)行流,闡述netfilter鉤子函數(shù)與規(guī)則表之間的聯(lián)系,以及在Ip報文處理過程中的應用;這三塊可以用一下圖來簡單描述:

圖2.1 Netfilter的三部分關(guān)系
3        鉤子函數(shù)的注冊管理
3.1        hook的存儲機制
鉤子函數(shù)由一個全局二維鏈表數(shù)組nf_hooks 保存,其按照協(xié)議族歸類存儲,在每個協(xié)
議族中,根據(jù)鉤子點順序排列,在鉤子點內(nèi)則根據(jù)鉤子函數(shù)的優(yōu)先級依次排列。鉤子函數(shù)的
存儲圖如下圖3-1所示,鏈表中的每個節(jié)點都是一個nf_hook_ops 結(jié)構(gòu),nf_hook_ops 實際存
儲了鉤子函數(shù)的內(nèi)容,其結(jié)構(gòu)如圖3-2 所示。在相應的鉤子點調(diào)用鉤子函數(shù)時,則根據(jù)協(xié)議
族和鉤子點找到相應的鏈表入口,然后依次調(diào)用該鏈中的每一個鉤子函數(shù)對數(shù)據(jù)包進行操
作。

圖3-1 鉤子函數(shù)的全局存儲

圖3-2 鉤子函數(shù)的鏈表
3.2        hook的管理機制
如果需要在相應的鉤子點掛載鉤子函數(shù),則需要首先定義一個nf_hook_ops 結(jié)構(gòu),在其
中實現(xiàn)實際的鉤子函數(shù),再調(diào)用函數(shù)nf_register_hook()將該鉤子函數(shù)注冊到圖3-1 所示的二
維鏈表中,nf_register_hook()函數(shù)的定義如下:


4        規(guī)則表的存儲管理
4.1        規(guī)則表的存儲機制
(以下內(nèi)容參考:http://linux.chinaunix.net/bbs/viewthread.php?tid=1054981
規(guī)則表(也可以叫規(guī)則集)在內(nèi)核是順序存儲的,涉及到的結(jié)構(gòu)體大概有這么幾個:
ipt_table、ipt _table_info、ipt_entry、ipt_entry_matches、ipt_entry_target;這幾個結(jié)構(gòu)之間的包含關(guān)系如下圖:

圖4-1:規(guī)則集各個結(jié)構(gòu)體之間的關(guān)系
各個結(jié)構(gòu)體具體內(nèi)容如下:
1.        Struct ipt_table


2.        struct ipt_table_info


3.        struct ipt_entry


4.        struct ipt_entry_match


5.        struct ipt_entry_target


6.        struct ipt_match


7.        struct ipt_target

各個結(jié)構(gòu)具體內(nèi)容包含關(guān)系如下圖:

圖4-2 規(guī)則集結(jié)構(gòu)體內(nèi)容關(guān)系

在 Netfilter 中規(guī)則是順序存儲的,一條規(guī)則主要包括三個部分:ipt_entry、ipt_entry_matches、ipt_entry_target。ipt_entry_matches 由多個ipt_entry_match組成,ipt_entry結(jié)構(gòu)主要保存標準匹配的內(nèi)容,ipt_entry_match 結(jié)構(gòu)主要保存擴展匹配的內(nèi)容,ipt_entry_target 結(jié)構(gòu)主要保存規(guī)則的動作。在ipt_entry 中還保存有與遍歷規(guī)則相關(guān)的變量target_offset 與next_offset,通過target_offset 可以找到規(guī)則中動作部分ipt_entry_target 的位置,通過next_offset可以找到下一條規(guī)則的位置。規(guī)則的存儲如下圖4-3 所示:

圖4-3 規(guī)則的存儲
ipt_entry 結(jié)構(gòu)如下圖4-4 所示,其成員ip 指向結(jié)構(gòu)ipt_ip,該結(jié)構(gòu)主要保存規(guī)則中標
準匹配的內(nèi)容(IP、mask、interface、proto 等),target_offset 的值等于ipt_entry 的長度與
ipt_entry_matches 的長度之和,next_offset 的值等于規(guī)則中三個部分的長度之和。通過
target_offset與next_offset可以實現(xiàn)規(guī)則的遍歷。

圖4-4 ipt_entry結(jié)構(gòu)
ipt_entry_match 主要保存規(guī)則中擴展匹配內(nèi)容(tos、ttl、time 等),其是Netfilter 中內(nèi)
核與用戶態(tài)交互的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),在其內(nèi)核部分由一個函數(shù)指針指向一個ipt_match結(jié)構(gòu),
該結(jié)構(gòu)體中包含了對包做匹配的函數(shù),是真正對包做匹配的地方。ipt_entry_target 結(jié)構(gòu)與
ipt_entry_match結(jié)構(gòu)很類似。

圖4-5 ipt_entry_match結(jié)構(gòu)

圖4-6 ipt_entry_target 結(jié)構(gòu)
4.2        規(guī)則的遍歷機制
在 Netfilter 中,函數(shù)ipt_do_table()實現(xiàn)了規(guī)則的遍歷(該函數(shù)的具體分析見5 Netfilter執(zhí)行流程),這里主要針對遍歷的幾個關(guān)鍵點說明;
1.        查找到規(guī)則起點:
該函數(shù)根據(jù)傳入的參數(shù)table 和hook找到相應的規(guī)則起點,即第一個ipt_entry的位置,主要通過函數(shù)get_entry()實現(xiàn)。

2.        匹配每條規(guī)則,處理相應動作:
標準匹配是通過函數(shù)ip_packet_match()實現(xiàn)的,該函數(shù)主要對包的五元組信息進行匹
配,擴展匹配則通過宏IPT_MATCH_ITERATE 實現(xiàn),該宏的定義分析如下:

宏IPT_MATCH_ITERATE 依次調(diào)用各個ipt_entry_match所指向的ipt_match中match()處理數(shù)據(jù)包,在for 循環(huán)中使用了terget_offset位置變量查找match的位置。
在對數(shù)據(jù)包進行了匹配后,接著需要進行相應的動作處理,通過函數(shù)ipt_get_target()獲取規(guī)則動作ipt_entry_target的位置:

如果還需要繼續(xù)遍歷下一條規(guī)則,則繼續(xù)執(zhí)行以下語句以找到下一條規(guī)則的開始位置:


4.3        規(guī)則表的管理
4.3.1        注冊
表,匹配,動作的注冊函數(shù)分別是:ipt_register_table、ipt_register_match、ipt_register_target;他們的實現(xiàn)在在ip_tables.c中;
每一個表,匹配,動作的注冊都是以單獨模塊來實現(xiàn)的,即如果用戶定義一條新的規(guī)則,提交到內(nèi)核中后,內(nèi)核通過檢查會發(fā)現(xiàn),沒有該項規(guī)則,則根據(jù)注冊名字分別申請模塊,分別進行相應的表,匹配,動作的創(chuàng)建;
如函數(shù)do_replace中的代碼:

上面三個注冊函數(shù)實現(xiàn)比較簡單,可以參考:
http://linux.chinaunix.net/bbs/viewthread.php?tid=1054981
4.3.2        查找
規(guī)則集在內(nèi)核中順序存放,具體規(guī)則的查找是遍歷規(guī)則集,通過匹配給定的關(guān)鍵字找到目標規(guī)則;函數(shù)find_match即實現(xiàn)該功能,源碼如下:

其中l(wèi)ist_for_each_entry是一個宏,具體定義如下:


4.3.3        檢測
檢測主要有函數(shù)translate_table函數(shù)來完成,該函數(shù)源碼分析如下:



該函數(shù)完成了兩個功能:
1.        規(guī)則表的檢查;檢查分為兩種:一種是基本檢查(基本檢測主要是基于IP報文五元組的檢查),第二種是規(guī)則自身的檢查,即通過宏IPT_ENTRY_ITERATE間接調(diào)用check_entry實現(xiàn)遍歷檢查每一個規(guī)則,check_entry源碼如下:

規(guī)則內(nèi)容具體的檢查包括兩個方面:
1)        對每個匹配的檢查,通過宏IPT_MATCH_ITERATE調(diào)用函數(shù)check_match;
2)        對動作進行檢查,如果沒有自定義的動作,則調(diào)用標準模板的檢查函數(shù)standard_check;否則調(diào)用動作自身的檢查函數(shù)t->u.kernel.target->checkentry;
下面具體分析下其中的宏IPT_MATCH_ITERATE和check_match:
IPT_MATCH_ITERATE源碼分析見(4.2):

check_match源碼分析:

函數(shù)translate_table在檢查完畢后還做了一件事情:Hook_entries和underflows的復制,在函數(shù):check_entry_size_and_hooks中完成該功能,而該函數(shù)是通過宏IPT_ENTRY_ITERATE間接調(diào)用;check_entry_size_and_hooks源碼分析如下:

4.3.4        替換
原理很簡單,分兩步:1. 獲取老的規(guī)則集;2. 用給定的新規(guī)則集替換老的;
實現(xiàn)函數(shù):replace_table,源碼如下:

作者: 沒本    時間: 2010-07-16 16:49
謝謝分享,下一份看看。
作者: hellioncu    時間: 2010-07-16 16:53
謝謝分享
作者: nelab    時間: 2010-07-17 11:23
看看
作者: 狼族狼心    時間: 2010-07-21 10:58
本帖最后由 狼族狼心 于 2010-07-21 11:01 編輯

4.4        iptables的實現(xiàn)機制
這里分析的iptables版本是1.3.6,iptables作為用戶態(tài)一個進程,入口函數(shù)比較簡單,非常清晰明了,添加注釋如下:

這里可以看到其中的do_command是處理的核心函數(shù);
4.4.1        命令格式
如允許所有包通過:
#iptables  –p  input  accept
具體的命令格式不在累贅,可網(wǎng)上查詢;
4.4.2        命令解析
命令行敲入命令后需要解析,解析在函數(shù)do_command中進行;
具體源碼分析如下:(藍色為代碼標注)
(該分析引自:http://linux.chinaunix.net/bbs/viewthread.php?tid=663849
do_command 函數(shù)是整個系統(tǒng)的核心,負責處理整個用戶的輸入命令。函數(shù)首先對一些結(jié)構(gòu)、變量進行初始化,初始化完畢后,進入while循環(huán),分析用戶輸入的命令,設置相關(guān)的標志變量,然后根據(jù)相應標志,調(diào)用對應的處理函數(shù)。
struct ipt_entry fw, *e = NULL;
        int invert = 0;
        unsigned int nsaddrs = 0, ndaddrs = 0;
        struct in_addr *saddrs = NULL, *daddrs = NULL;
        int c, verbose = 0;
        const char *chain = NULL;
        const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
        const char *policy = NULL, *newname = NULL;
        unsigned int rulenum = 0, options = 0, command = 0;
        const char *pcnt = NULL, *bcnt = NULL;
        int ret = 1;
        struct iptables_match *m;
        struct iptables_target *target = NULL;
        struct iptables_target *t;
        const char *jumpto = "";
        char *protocol = NULL;
        const char *modprobe = NULL;
        /*初始化變量*/
memset(&fw, 0, sizeof(fw));
        opts = original_opts;
        global_option_offset = 0;
        /* re-set optind to 0 in case do_command gets called
         * a second time */
        optind = 0;
        /*初始化兩個全局變量*/
/* clear mflags in case do_command gets called a second time
         * (we clear the global list of all matches for security)*/
        for (m = iptables_matches; m; m = m->next) {
                m->mflags = 0;
                m->used = 0;
        }
        for (t = iptables_targets; t; t = t->next) {
                t->tflags = 0;
                t->used = 0;
        }
ps:開頭一大堆的變量定義和初始化,可以在程序分析的時候看它們的作用,有兩個全局結(jié)構(gòu)變量很重要:iptables_matches和iptables_targets。現(xiàn)在來分析他們的作用會有一點困難,因為它們涉及到了太多方面的東東,這里,可以先把它們“想像成”用戶空間用來讀取內(nèi)核規(guī)則的結(jié)構(gòu)(當然,這有點錯誤)。
/*開始化析命令行*/
while ((c = getopt_long(argc, argv,
          "-A:C:R:I:L::M:F::Z::N:X::E:Vh:p:s:d:j:i:fbvnt:mc:",
                                           opts, NULL)) != -1)
{
}
這個while循環(huán)處理所有的用戶輸入,對應規(guī)則輸出-L,有:
case 'L':
add_command(&command, CMD_LIST, CMD_ZERO,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind++];
break;

add_command函數(shù)負責將命令標志變量command與令標志 CMD_LIST求&運算, CMD_ZERO只是一個附加的判斷標志而已,invert);然后,從命令行中取得要顯示的鏈名(如果有的話)。
與此相關(guān)的還有用t參數(shù)指定了表名:
                case 't':
                        if (invert)
                                exit_error(PARAMETER_PROBLEM,
                                           "unexpected ! flag before --table";
                        *table = argv[optind-1];
                        break;
即,如果有’t’參數(shù),則取’t’后跟的表名:*table = argv[optind-1],否則,它應該是主函數(shù)中默認的filter表。

命令處理完畢后,即進入執(zhí)行模塊:
/*因為程序定義了共享庫的話,iptables_matches/iptables_target這兩個結(jié)構(gòu)運行至此是NULL,并且target也是NULL,對于規(guī)則顯示而言,這一部份的處理目前沒有實際意義,回過頭再來看這一段更易理解。final_check成員函數(shù)的作用是作最終的標志檢查,如果檢測失則,則退出*/
        for (m = iptables_matches; m; m = m->next) {
                if (!m->used)
                        continue;
                m->final_check(m->mflags);
        }
        if (target)
                target->final_check(target->tflags);
接著對參數(shù)作一些必要的合法性檢查:
        /* Fix me: must put inverse options checking here --MN */
        if (optind < argc)
                exit_error(PARAMETER_PROBLEM,
                           "unknown arguments found on commandline";
        if (!command)
                exit_error(PARAMETER_PROBLEM, "no command specified";
        if (invert)
                exit_error(PARAMETER_PROBLEM,
                           "nothing appropriate following !";
/*對于如果要進行(CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)處理來說,如果沒有設置來源/目的地址及掩碼,則給予它們一個默認值*/
        if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
                if (!(options & OPT_DESTINATION))
                        dhostnetworkmask = "0.0.0.0/0";
                if (!(options & OPT_SOURCE))
                        shostnetworkmask = "0.0.0.0/0";
        }
/*對來源/目的地址及掩碼進行拆分,它們總是以 addr/mask的形式來出現(xiàn)的,根據(jù)’/’前面的字符串取得地址值,根據(jù)’/’后面的掩碼位數(shù),求得正確的掩碼值,值得注意的是,同時要處理主機地址和網(wǎng)絡地址的情況*/
        if (shostnetworkmask)
                parse_hostnetworkmask(shostnetworkmask, &saddrs,
                                      &(fw.ip.smsk), &nsaddrs);
        if (dhostnetworkmask)
                parse_hostnetworkmask(dhostnetworkmask, &daddrs,
                                      &(fw.ip.dmsk), &ndaddrs);
/*然后檢查來源/目的網(wǎng)絡地址的合法性*/
        if ((nsaddrs > 1 || ndaddrs > 1) &&
            (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
                exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
                           " source or destination IP addresses";
/*對命令行格式進行合法性檢查*/
generic_opt_check(command, options);

以上代碼可以看出,do_command函數(shù)完成對命令行的解析,并且做一些合法性檢查;命令輸入正確并解析完畢后,下一步是獲取內(nèi)核的所有規(guī)則;
4.4.3        獲取內(nèi)核規(guī)則表
源碼解析如下:
(該分析引自:http://linux.chinaunix.net/bbs/viewthread.php?tid=663849
do_command函數(shù)最后一個參數(shù)handle,是一個指向了具體表,如filter、nat表的句柄,這里判斷,如果handle為空,則調(diào)用iptc_init,根據(jù)table的名稱,讓handle指針指向相應的表的地址空間,也就是把對應表的所有信息從內(nèi)核中取出來:
        /* only allocate handle if we weren't called with a handle */
        if (!*handle)
                *handle = iptc_init(*table);
        /*如果獲取換敗,將試著插入模塊,再次獲取*/
        if (!*handle) {
                /* try to insmod the module if iptc_init failed */
                iptables_insmod("ip_tables", modprobe);
                *handle = iptc_init(*table);
        /*仍然失敗,則退出*/
        if (!*handle)
                exit_error(VERSION_PROBLEM,
                           "can't initialize iptables table `%s': %s",
                           *table, iptc_strerror(errno));
/*繼續(xù)進行一些簡單的判斷*/
if (command == CMD_APPEND
            || command == CMD_DELETE
            || command == CMD_INSERT
            || command == CMD_REPLACE) {
                /*List命令不在判斷之列,暫時不分析*/
        }

補充:
獲取內(nèi)核規(guī)則表的函數(shù)是iptc_init,該函數(shù)采用了轉(zhuǎn)定義,具體的實現(xiàn)為TC_INIT,在文件libiptc.c中;下面具體對該函數(shù)進行分析:
(代碼分析摘自:http://linux.chinaunix.net/bbs/viewthread.php?tid=663849
再回到iptc_init 函數(shù)上來,它根據(jù)表名,從內(nèi)核獲取對應的表的相關(guān)信息,handle是一個iptc_handle_t類型的指針,在libiptc.c中,有如下定義:
/* Transparent handle type. */
typedef struct iptc_handle *iptc_handle_t;
在Libip4tc中:
#define STRUCT_TC_HANDLE        struct iptc_handle
在Libiptc.c中,可以找到STRUCT_TC_HANDLE的定義:
STRUCT_TC_HANDLE
{
        /* Have changes been made? */
        int changed;
        /* Size in here reflects original state. */
        STRUCT_GETINFO info;

        struct counter_map *counter_map;
        /* Array of hook names */
        const char **hooknames;
        /* Cached position of chain heads (NULL = no cache). */
        unsigned int cache_num_chains;
        unsigned int cache_num_builtins;
        /* Rule iterator: terminal rule */
        STRUCT_ENTRY *cache_rule_end;
        /* Number in here reflects current state. */
        unsigned int new_number;
        STRUCT_GET_ENTRIES entries;
};
再來看看iptc_init函數(shù),同樣在在Libip4tc中,有如下定義:
#define TC_INIT                        iptc_init
在Libiptc.c中,可以看到函數(shù)的實現(xiàn),基本上iptables與內(nèi)核的交互,都是使用setsockopt函數(shù)來實現(xiàn)的,對于獲取取規(guī)是信息來說,標志位是SO_GET_INFO,而從內(nèi)核返回回來的規(guī)則信息是一個STRUCT_GETINFO結(jié)構(gòu):
TC_HANDLE_T TC_INIT(const char *tablename)
{
        TC_HANDLE_T h;
        STRUCT_GETINFO info;
        unsigned int i;
        int tmp;
        socklen_t s;
        iptc_fn = TC_INIT;
        if (sockfd != -1)
                close(sockfd);
        /*為獲取信息打開一個套接字接口*/
        sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
        if (sockfd < 0)
                return NULL;
        s = sizeof(info);
        if (strlen(tablename) >= TABLE_MAXNAMELEN) {
                errno = EINVAL;
                return NULL;
        }
        strcpy(info.name, tablename);
/*獲取規(guī)則信息*/
/*-- yangxh mark:第一次獲取表的一些大概信息,info結(jié)構(gòu),此時并沒有獲取具體的規(guī)則--*/
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
                return NULL;
/*-- yangxh mark:在第一次獲取基礎上知道了規(guī)則數(shù)以及總大小,此處申請空間存放規(guī)則,地址空間的地址保存在h->entries --*/
        if ((h = alloc_handle(info.name, info.size, info.num_entries))
            == NULL)
                return NULL;
/* Too hard --RR */
#if 0
        sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
        dynlib = dlopen(pathname, RTLD_NOW);
        if (!dynlib) {
                errno = ENOENT;
                return NULL;
        }
        h->hooknames = dlsym(dynlib, "hooknames";
        if (!h->hooknames) {
                errno = ENOENT;
                return NULL;
        }
#else
        h->hooknames = hooknames;
#endif
        /* Initialize current state */
        h->info = info;
        h->new_number = h->info.num_entries;
        for (i = 0; i < h->info.num_entries; i++)
                h->counter_map
                        = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
        h->entries.size = h->info.size;
        tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
/*--yangxh mark:此處才是真正獲取具體的規(guī)則,規(guī)則內(nèi)容存放在h->entries ,大小為tmp --*/
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
                       &tmp) < 0) {
                free(h);
                return NULL;
        }
        CHECK(h);
        return h;
}
        函數(shù)為h分配空間,然后賦予相應的值。要理解這個函數(shù),還需要了解STRUCT_GETINFO結(jié)構(gòu)和分配內(nèi)存空間的函數(shù)alloc_handle。
#define STRUCT_GETINFO                struct ipt_getinfo
/* The argument to IPT_SO_GET_INFO */
struct ipt_getinfo
{
        /* Which table: caller fills this in. */
        char name[IPT_TABLE_MAXNAMELEN];
        /* Kernel fills these in. */
        /* Which hook entry points are valid: bitmask */
        unsigned int valid_hooks;
        /* Hook entry points: one per netfilter hook. */
        unsigned int hook_entry[NF_IP_NUMHOOKS];
        /* Underflow points. */
        unsigned int underflow[NF_IP_NUMHOOKS];
        /* Number of entries */
        unsigned int num_entries;
        /* Size of entries. */
        unsigned int size;
};
/* Allocate handle of given size */
static TC_HANDLE_T
alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
{
        size_t len;
        TC_HANDLE_T h;
        len = sizeof(STRUCT_TC_HANDLE)
                + size
                + num_rules * sizeof(struct counter_map);
        if ((h = malloc(len)) == NULL) {
                errno = ENOMEM;
                return NULL;
        }
        h->changed = 0;
        h->cache_num_chains = 0;
        h->cache_chain_heads = NULL;
        h->counter_map = (void *)h
                + sizeof(STRUCT_TC_HANDLE)
                + size;
        strcpy(h->info.name, tablename);
        strcpy(h->entries.name, tablename);
        return h;
}

補充:
1.        最終獲取內(nèi)核的規(guī)則是通過getsockopt函數(shù)實現(xiàn)的;
2.        調(diào)用了兩次getsockopt函數(shù),第一次是獲取大概信息,主要是得到名字,規(guī)則數(shù),以及占用空間大小,以便申請存儲空間用;
3.        在iptables與內(nèi)核的交互通過getsockopt實現(xiàn),并通過指令(如SO_GET_INFO,該指令經(jīng)過轉(zhuǎn)定義,對于ip4來說iptables在libip4tc.c中轉(zhuǎn)定義,內(nèi)核對應的指令為IPT_SO_GET_INFO)來對應具體的操作,內(nèi)核對應的處理函數(shù)為do_ipt_get_ctl和do_ipt_set_ctl,在ip_tables.c中;
4.4.4        按命令進行處理
http://blog.chinaunix.net/u/33048/showart_1121090.html
這部分也是do_command函數(shù)的最后。用一個swicth(command)來判斷具體執(zhí)行什么動作。我們這里 command=CMD_APPEND,因此調(diào)用append_entry函數(shù)來將該條iptables規(guī)則加入進去。大致介紹一下 append_entry函數(shù)的功能:
源碼中具體對應的函數(shù)名為TC_APPEND_ENTRY()。該函數(shù)首先調(diào)用find_label找到整個規(guī)則中指定chain的struct chain_cache結(jié)構(gòu),然后做一下target的映射。真正執(zhí)行添加規(guī)則的是insert_rules函數(shù)。該函數(shù)找到插入規(guī)則的entry點,并將該規(guī)則插入。
調(diào)整后的所有的規(guī)則都保存在結(jié)構(gòu)體指針handle之中。
該邏輯結(jié)構(gòu)比較簡單,不在具體分析;
4.4.5        規(guī)則表提交到內(nèi)核
整個do_command執(zhí)行完后,也就完成了命令的解析,內(nèi)核規(guī)則表的獲取,具體動作的執(zhí)行,下面將根據(jù)do_command處理結(jié)構(gòu)決定是否需要將規(guī)則表提交到內(nèi)核,如命令是現(xiàn)實所有規(guī)則,并不對規(guī)則進行修改,則不需要提交到內(nèi)核;如果對規(guī)則進行了增刪改,則需要提交;
提交規(guī)則到內(nèi)核調(diào)用的函數(shù)是:iptc_commit;
iptc_commit的實現(xiàn)在libiptc.c中的TC_COMMIT;其中主要是通過setsockopt與內(nèi)核交互,完成規(guī)則表的提交;
4.4.6        內(nèi)核處理規(guī)則表
用戶態(tài)最終通過setsockopt/ getsockopt與內(nèi)核交互,內(nèi)核對應的處理函數(shù)是do_ipt_set_ctl/ do_ipt_get_ctl;
do_ipt_get_ctl主要是獲取規(guī)則,這里不討論,主要看do_ipt_set_ctl,用戶態(tài)提交規(guī)則表后內(nèi)核態(tài)的處理過程;
函數(shù)do_ipt_set_ctl源碼如下:

目前有兩個分支,我們主要討論IPT_SO_SET_REPLACE分支:將用戶態(tài)提交的規(guī)則表,替換內(nèi)核原有的規(guī)則表;即主要調(diào)用函數(shù)do_replace,該函數(shù)源碼如下:



由以上源碼可以看到,該函數(shù)的處理過程有四個:
1.        從用戶空間將將新的規(guī)則表拷貝到內(nèi)核空間;注意這里有兩次拷貝(copy_from_user函數(shù)),第一次拷貝是大概信息,即ipt_replace結(jié)構(gòu),第二次才是拷貝真正的規(guī)則表信息;
2.        對新規(guī)則進行合法性檢查(translate_table);
3.        根據(jù)規(guī)則表名字找到原來舊的規(guī)則表(find_table_lock);如果查不到則表示該規(guī)則是新建的,申請新的規(guī)則處理模塊(try_then_request_module request_module);
4.        新的規(guī)則表替換舊的規(guī)則表(replace_table);
5.        對舊規(guī)則表進行清理釋放(IPT_ENTRY_ITERATE, vfree);
6.        將結(jié)果返回給用戶空間(copy_to_user);
**這其中有兩個重點的函數(shù)需要關(guān)注:translate_table(見4..3.3),replace_table(見4..3.4)
作者: 狼族狼心    時間: 2010-07-21 11:02
本帖最后由 狼族狼心 于 2010-07-21 11:03 編輯

5        Netfilter執(zhí)行流程
5.1.1        鉤子函數(shù)和掛載點關(guān)系
在Netfilter中的不同鉤子點調(diào)用了不同的鉤子函數(shù),這些鉤子函數(shù)與各個掛載點的關(guān)系如下圖所示:

圖5-1 Netfilter中hook函數(shù)與各掛載點關(guān)系
Netfilter 中默認表filter 在建立時則在NF_IP_LOCAL_IN,NF_IP_FORWARD 鉤子點注冊了鉤子函數(shù)ipt_hook() , 在NF_IP_LOCAL_OUT 這個點注冊了鉤子函數(shù)ipt_local_out_hook(),兩個鉤子函數(shù)都會調(diào)用ipt_do_table()對相對應的表和鉤子點的規(guī)則進
行遍歷。
5.1.2        執(zhí)行中函數(shù)調(diào)用關(guān)系
IP報文在轉(zhuǎn)發(fā)過程中可能會流經(jīng)5個hook點:NF_IP_PRE_ROUTING、NF_IP_FORWARD、NF_IP_LOCAL_IN、NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING,內(nèi)核的報文處理函數(shù)分別是:ip_rcv、ip_forward、ip_local_deliver、ip_build_and_send_pkt、ip_finish_output;關(guān)于報文的轉(zhuǎn)發(fā)流程可以參考:
http://www.ibm.com/developerworks/cn/linux/l-ntflt/
http://linux.chinaunix.net/bbs/archiver/?tid-1132965.html
IP報文在處理過程中是怎樣與netfilter的鉤子函數(shù)掛鉤的呢?又是怎樣找到具體的規(guī)則表進行欲行定義好的動作處理?整個函數(shù)的調(diào)用過程可以見下圖(我們以filter表為例,它在NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT這三個點注冊了函數(shù),對應的hook函數(shù)分別為ipt_hook、ipt_hook、ipt_local_out_hook):

圖5-2 Netfilter執(zhí)行過程中的函數(shù)調(diào)用

6        參考文獻
1.        iptables 源碼分析 http://linux.chinaunix.net/bbs/viewthread.php?tid=663849
2.        iptables執(zhí)行的流程分析: http://blog.chinaunix.net/u/33048/showart_1121090.html
3.        Netfilter實現(xiàn)機制分析: http://linux.chinaunix.net/bbs/viewthread.php?tid=1054981
4.        端木隱博客: http://blog.chinaunix.net/u/12313/article_21496.html
5.        Linux netfilter實現(xiàn)機制以及擴展技術(shù):http://www.ibm.com/developerworks/cn/linux/l-ntflt/


為了方便大家下載,貼個附件
Linux-Netfilter機制分析.pdf (1.64 MB, 下載次數(shù): 1007)
作者: rain_fish    時間: 2010-07-21 14:13
建議管理員設置為精華!
作者: donglongchao    時間: 2010-07-21 17:18
幫頂一下,建議設精。
作者: ubuntuer    時間: 2010-07-21 23:39
支持下
作者: langue    時間: 2010-07-22 04:51
內(nèi)容多,希望能再接再厲,整理一下格式。
作者: RobinKQin    時間: 2010-07-22 08:37

收藏了,很不錯
作者: lujian19861986    時間: 2012-07-25 13:55
mark~~~~~~
作者: evaspring    時間: 2012-07-25 14:55
最近論壇帖子的水平明顯提高了不少啊
作者: 雷鋒不謝    時間: 2014-09-03 13:14
回復 1# 狼族狼心


    寫的很好,很多東西都連貫起來了,建議加精。!
作者: mysky0407    時間: 2014-09-23 17:21
挺好的,謝謝分享~
作者: taitood    時間: 2023-03-20 16:08
download        ok




歡迎光臨 Chinaunix (http://72891.cn/) Powered by Discuz! X3.2