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

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 20897 | 回復(fù): 63
打印 上一主題 下一主題

[原創(chuàng)]Netfilter源碼分析-我來拋磚,望能引玉 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2005-12-16 12:50 |只看該作者 |倒序瀏覽
前段時間貼了一篇《iptables源碼分析》,雖然后來沒有寫完,但是我發(fā)現(xiàn)有許多朋友都挺喜歡netfilter的,于是我就有一個想法,大家能不能把各自學(xué)習(xí)的心得貼下來,寫成一篇完整的文章呢??
一來大家可以互相學(xué)習(xí)、交流、進步;
二來大家一起來完成,也希望能成為CU上一篇經(jīng)典之作,體現(xiàn)CUer們的互相協(xié)作精神。。

但是關(guān)鍵是要有條理,不要搞亂了,沒了體系,就不太好了,對于這一點,還希望版主們?nèi)绻С治业囊庖,多費點神……

我先來拋磚,把自己的開頭部份的筆記稍改了一下,貼出來,開個頭
歡迎大家指正(因為手頭沒有什么資料,都是一句句用sourceinsight跟的,錯誤難免了)
希望有志同道合的朋友繼續(xù)寫下去,期盼中……


[ 本帖最后由 獨孤九賤 于 2005-12-16 14:26 編輯 ]

論壇徽章:
0
2 [報告]
發(fā)表于 2005-12-16 12:52 |只看該作者
一、主函數(shù)

init為初始化函數(shù),主要完成表的注冊,然后再注冊與表相對應(yīng)的HOOK
//初始化函數(shù)為init:
module_init(init);

//init 函數(shù)負責(zé)注冊filter表和默認的三個chain
static int __init init(void)
{
        int ret;

        if (forward < 0 || forward > NF_MAX_VERDICT) {
                printk("iptables forward must be 0 or 1\n");
                return -EINVAL;
        }

        /* Entry 1 is the FORWARD hook */
        initial_table.entries[1].target.verdict = -forward - 1;

        /* 注冊filter表 */
        ret = ipt_register_table(&packet_filter);
        if (ret < 0)
                return ret;

        /* 注冊各個鉤子函數(shù) */
        ret = nf_register_hook(&ipt_ops[0]);
        if (ret < 0)
                goto cleanup_table;

        ret = nf_register_hook(&ipt_ops[1]);
        if (ret < 0)
                goto cleanup_hook0;

        ret = nf_register_hook(&ipt_ops[2]);
        if (ret < 0)
                goto cleanup_hook1;

        return ret;

//如果注冊失敗,將已注冊的鉤子清除掉
cleanup_hook1:
        nf_unregister_hook(&ipt_ops[1]);
cleanup_hook0:
        nf_unregister_hook(&ipt_ops[0]);
cleanup_table:
        ipt_unregister_table(&packet_filter);

        return ret;
}

論壇徽章:
0
3 [報告]
發(fā)表于 2005-12-16 12:52 |只看該作者
二、表的注冊
表的注冊由函數(shù)ipt_register_table來完成,
ipt_register_table(&packet_filter);
其參數(shù)packet_filter包含了待注冊表的各個參數(shù):
static struct ipt_table packet_filter
= { { NULL, NULL }, "filter", &initial_table.repl,
    FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
   
可見,內(nèi)核中,表是以結(jié)構(gòu)struct ipt_table來表示的:
struct ipt_table
{
                struct list_head list;
                        /* 用于構(gòu)造,維護鏈表的結(jié)構(gòu) */
                char name[IPT_TABLE_MAXNAMELEN];
                        /* 表名,如"filter"、"nat"等,為了滿足自動模塊加載的設(shè)計,包含該表的模塊應(yīng)命名為iptable_'name'.o */
                struct ipt_replace *table;
                        /* 表的初始化模板,初始為initial_table.repl */
                unsigned int valid_hooks;
                        /* 位向量,表示當前表所影響的HOOK */
                rwlock_t lock;
                        /* 讀寫鎖,初始為打開狀態(tài) */
                struct ipt_table_info *private;
                        /* iptable的數(shù)據(jù)區(qū)*/
                struct module *me;
                        /* 是否在模塊中定義,若否,則為NULL */
};

對照這一結(jié)構(gòu)分析,filter表的初始化為:
鏈表:{ NULL, NULL }
表名:"filter"
初始化模板:&initial_table.repl
當前表所影響的Hook:FILTER_VALID_HOOKS        /*#define FILTER_VALID_HOOKS ((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))*/
讀寫鎖:RW_LOCK_UNLOCKED,即為打開狀態(tài)
數(shù)據(jù)區(qū): NULL
是否在模塊中定義:THIS_MODULE,見如下宏定義:

#ifndef THIS_MODULE
#ifdef MODULE
#define THIS_MODULE (&__this_module)
#else
#define THIS_MODULE (NULL)
#endif
#endif

先來看維護表的鏈表的結(jié)構(gòu):
struct list_head {
        struct list_head *next, *prev;
};
很簡單,它是一個雙向鏈表。

另一個重要的東東就是表的模板和數(shù)據(jù)區(qū)。表模板定義了一個初始化用的該表的所默認的HOOK所包含的規(guī)則等信息,它被初始化成了
&initial_table.repl。而初始化的數(shù)據(jù)區(qū)struct ipt_table_info *private為空。這樣,ipt_register_table()函數(shù)會用repl成員的
內(nèi)容去填充private成員.

struct ipt_table_info是實際描述表的數(shù)據(jù)結(jié)構(gòu)(net/ipv4/netfilter/ip_tables.c):
struct ipt_table_info
{
                unsigned int size;
                        /* 表大小 */
                unsigned int number;
                        /* 表中的規(guī)則數(shù) */
                unsigned int initial_entries;
                        /* 初始的規(guī)則數(shù),用于模塊計數(shù) */
                unsigned int hook_entry[NF_IP_NUMHOOKS];
                        /* 記錄所影響的HOOK的規(guī)則入口相對于下面的entries變量的偏移量 */
                unsigned int underflow[NF_IP_NUMHOOKS];
                        /* 與hook_entry相對應(yīng)的規(guī)則表上限偏移量,當無規(guī)則錄入時,相應(yīng)的hook_entry和underflow均為0 */
                char entries[0] ____cacheline_aligned;
                        /* 規(guī)則表入口 */
};

再來看模板的定義,這個結(jié)構(gòu)很簡單,不過長了點:

static struct
{
        struct ipt_replace repl;
        struct ipt_standard entries[3];
        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 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, { } },
    {
            /* LOCAL_IN */
            { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
                0,
                sizeof(struct ipt_entry),
                sizeof(struct ipt_standard),
                0, { 0, 0 }, { } },
              { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
                -NF_ACCEPT - 1 } },
            /* FORWARD */
            { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
                0,
                sizeof(struct ipt_entry),
                sizeof(struct ipt_standard),
                0, { 0, 0 }, { } },
              { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
                -NF_ACCEPT - 1 } },
            /* LOCAL_OUT */
            { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
                0,
                sizeof(struct ipt_entry),
                sizeof(struct ipt_standard),
                0, { 0, 0 }, { } },
              { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
                -NF_ACCEPT - 1 } }
    },
    /* ERROR */
    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
        0,
        sizeof(struct ipt_entry),
        sizeof(struct ipt_error),
        0, { 0, 0 }, { } },
      { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },
          { } },
        "ERROR"
      }
    }
};

結(jié)構(gòu)長了點,我們先來關(guān)心注冊表時的初始值:
&initial_table.repl
這是一個struct ipt_replace結(jié)構(gòu),該結(jié)構(gòu)做為初始化模版被使用,同樣用戶通過系統(tǒng)調(diào)用更換
表時也要用到這個結(jié)構(gòu)。定義如下:

/* The argument to IPT_SO_SET_REPLACE. */
struct ipt_replace
{
        /* 表名. */
        char name[IPT_TABLE_MAXNAMELEN];

        /* 該表所影響的Hook. */
        unsigned int valid_hooks;

        /* Number of entries */
        unsigned int num_entries;

        /* Total size of new entries */
        unsigned int size;

        /* 記錄所影響的HOOK的規(guī)則入口相對于下面的entries變量的偏移量 */
        unsigned int hook_entry[NF_IP_NUMHOOKS];

        /* 與hook_entry相對應(yīng)的規(guī)則表上限偏移量,當無規(guī)則錄入時,相應(yīng)的hook_entry和underflow均為0 */
        unsigned int underflow[NF_IP_NUMHOOKS];

        /* Information about old entries: */
        /* Number of counters (must be equal to current number of entries). */
        unsigned int num_counters;
        /* The old entries' counters. */
        struct ipt_counters *counters;

        /* The entries (hang off end: not really an array). */
        struct ipt_entry entries[0];
};

對照結(jié)構(gòu),可以分析各個成員的初始化值了:
char name[IPT_TABLE_MAXNAMELEN]="filter";
unsigned int valid_hooks=FILTER_VALID_HOOKS;
unsigned int num_entries=4;
unsigned int size=sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error);
unsigned int hook_entry[NF_IP_NUMHOOKS]={ [NF_IP_LOCAL_IN] 0,
        [NF_IP_FORWARD] sizeof(struct ipt_standard),
        [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 };
unsigned int underflow={ [NF_IP_LOCAL_IN] 0,
        [NF_IP_FORWARD] sizeof(struct ipt_standard),
        [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 };
unsigned int num_counters=0;
struct ipt_counters *counters=NULL;
struct ipt_entry entries[0]={ };


了解了這些結(jié)構(gòu)后,再來看表的注冊函數(shù):

int ipt_register_table(struct ipt_table *table)
{
        int ret;
        struct ipt_table_info *newinfo;
        static struct ipt_table_info bootstrap
                = { 0, 0, 0, { 0 }, { 0 }, { } };

        /*宏MOD_INC_USE_COUNT用于模塊計數(shù)器累加,主要是為了防止模塊異常刪除,對應(yīng)的
        宏MOD_DEC_USE_COUNT就是累減了*/
        MOD_INC_USE_COUNT;
       
        /*為每個CPU分配規(guī)則空間*/
        newinfo = vmalloc(sizeof(struct ipt_table_info)
                          + SMP_ALIGN(table->table->size) * smp_num_cpus);
        /*分配失敗*/
        if (!newinfo) {
                ret = -ENOMEM;
                MOD_DEC_USE_COUNT;
                return ret;
        }
       
        /*將規(guī)則項拷貝到新表項的第一個cpu空間里面*/
        memcpy(newinfo->entries, table->table->entries, table->table->size);

/*translate_table函數(shù)將newinfo表示的table的各個規(guī)則進行邊界檢查,然后對于newinfo所指的
ipt_talbe_info結(jié)構(gòu)中的hook_entries和underflows賦予正確的值,最后將表項向其他cpu拷貝*/
        ret = translate_table(table->name, table->valid_hooks,
                              newinfo, table->table->size,
                              table->table->num_entries,
                              table->table->hook_entry,
                              table->table->underflow);
        if (ret != 0) {
                vfree(newinfo);
                MOD_DEC_USE_COUNT;
                return ret;
        }

        ret = down_interruptible(&ipt_mutex);
        if (ret != 0) {
                vfree(newinfo);
                MOD_DEC_USE_COUNT;
                return ret;
        }

        /* 如果注冊的table已經(jīng)存在,釋放空間 并且遞減模塊計數(shù) */
        if (list_named_find(&ipt_tables, table->name)) {
                ret = -EEXIST;
                goto free_unlock;
        }

        /* 替換table項. */
        table->private = &bootstrap;
        if (!replace_table(table, 0, newinfo, &ret))
                goto free_unlock;

        duprintf("table->private->number = %u\n",
                 table->private->number);
       
        /* 保存初始規(guī)則計數(shù)器 */
        table->private->initial_entries = table->private->number;

        table->lock = RW_LOCK_UNLOCKED;
        /*將表添加進鏈表*/
        list_prepend(&ipt_tables, table);

unlock:
        up(&ipt_mutex);
        return ret;

free_unlock:
        vfree(newinfo);
        MOD_DEC_USE_COUNT;
        goto unlock;
}

呵呵,初次看table的注冊,有點頭大,因為它不光是netfilter,還涉及到linux內(nèi)核中的內(nèi)存管理、
信號量設(shè)置等等,不過其實注冊也就完成兩件事:初始化表,將表添加進表的鏈表。

論壇徽章:
0
4 [報告]
發(fā)表于 2005-12-16 12:53 |只看該作者
表的注冊中涉及到的重要函數(shù)

表注冊函數(shù)中,主要涉及到的重要函數(shù)有:
translate_table
list_named_find
list_prepend

1、translate_table
/*
* 函數(shù):translate_table()
* 參數(shù):
*         name:表名稱;
*        valid_hooks:當前表所影響的hook
*        newinfo:包含當前表的所有信息的結(jié)構(gòu)
*        size:表的大小
*        number:表中的規(guī)則數(shù)
*        hook_entries:記錄所影響的HOOK的規(guī)則入口相對于下面的entries變量的偏移量
*        underflows:與hook_entry相對應(yīng)的規(guī)則表上限偏移量
* 作用:
*        translate_table函數(shù)將newinfo表示的table的各個規(guī)則進行邊界檢查,然后對于newinfo所指的
*        ipt_talbe_info結(jié)構(gòu)中的hook_entries和underflows賦予正確的值,最后將表項向其他cpu拷貝
* 返回值:
*        int ret==0表示成功返回
*/

static int
translate_table(const char *name,
                unsigned int valid_hooks,
                struct ipt_table_info *newinfo,
                unsigned int size,
                unsigned int number,
                const unsigned int *hook_entries,
                const unsigned int *underflows)
{
        unsigned int i;
        int ret;

        newinfo->size = size;
        newinfo->number = number;

        /* 初始化所有Hooks為不可能的值. */
        for (i = 0; i < NF_IP_NUMHOOKS; i++) {
                newinfo->hook_entry[i] = 0xFFFFFFFF;
                newinfo->underflow[i] = 0xFFFFFFFF;
        }

        duprintf("translate_table: size %u\n", newinfo->size);
        i = 0;
        /* 遍歷所有規(guī)則,檢查所有偏量,檢查的工作都是由IPT_ENTRY_ITERATE這個宏來完成,并且它
        的最后一個參數(shù)i,返回表的所有規(guī)則數(shù). */
        ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                check_entry_size_and_hooks,
                                newinfo,
                                newinfo->entries,
                                newinfo->entries + size,
                                hook_entries, underflows, &i);
        if (ret != 0)
                return ret;

        /*實際計算得到的規(guī)則數(shù)與指定的不符*/
        if (i != number) {
                duprintf("translate_table: %u not %u entries\n",
                         i, number);
                return -EINVAL;
        }

        /* 因為函數(shù)一開始將HOOK的偏移地址全部初始成了不可能的值,而在上一個宏的遍歷中設(shè)置了
        hook_entries和underflows的值,這里對它們進行檢查 */
        for (i = 0; i < NF_IP_NUMHOOKS; i++) {
                /* 只檢查當前表所影響的hook */
                if (!(valid_hooks & (1 << i)))
                        continue;
                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
                        duprintf("Invalid hook entry %u %u\n",
                                 i, hook_entries[i]);
                        return -EINVAL;
                }
                if (newinfo->underflow[i] == 0xFFFFFFFF) {
                        duprintf("Invalid underflow %u %u\n",
                                 i, underflows[i]);
                        return -EINVAL;
                }
        }

        /*確保新的table中不存在規(guī)則環(huán)*/
        if (!mark_source_chains(newinfo, valid_hooks))
                return -ELOOP;

        /* 對tables中的規(guī)則項進行完整性檢查,保證每一個規(guī)則項在形式上是合法的*/
        i = 0;
        ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                check_entry, name, size, &i);
       
        /*檢查失敗,釋放空間,返回*/
        if (ret != 0) {
                IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                  cleanup_entry, &i);
                return ret;
        }

        /* 為每個CPU復(fù)制一個完整的table項*/
        for (i = 1; i < smp_num_cpus; i++) {
                memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
                       newinfo->entries,
                       SMP_ALIGN(newinfo->size));
        }

        return ret;
}

函數(shù)的核心處理,是調(diào)用了IPT_ENTRY_ITERATE,我在《iptables源碼分析》中已提過,這個宏用來遍歷每一個規(guī)則,然后
調(diào)用其第三個參數(shù)(函數(shù)指針)進行處理,前兩個參數(shù)分別表示規(guī)則的起始位置和規(guī)則總大小,后面的參數(shù)則視情況而定。
再來看一次:
/* fn returns 0 to continue iteration */
#define IPT_ENTRY_ITERATE(entries, size, fn, args...)                \
({                                                                \
        unsigned int __i;                                        \
        int __ret = 0;                                                \
        struct ipt_entry *__entry;                                \
                                                                \
        for (__i = 0; __i < (size); __i += __entry->next_offset) { \
                __entry = (void *)(entries) + __i;                \
                                                                \
                __ret = fn(__entry , ## args);                        \
                if (__ret != 0)                                        \
                        break;                                        \
        }                                                        \
        __ret;                                                        \
})

對應(yīng)地,函數(shù)的第一次宏的調(diào)用,
        ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                check_entry_size_and_hooks,
                                newinfo,
                                newinfo->entries,
                                newinfo->entries + size,
                                hook_entries, underflows, &i);
遍歷到每一項規(guī)則后,就調(diào)用check_entry_size_and_hooks繼續(xù)處理。

static inline int
check_entry_size_and_hooks(struct ipt_entry *e,
                           struct ipt_table_info *newinfo,
                           unsigned char *base,
                           unsigned char *limit,
                           const unsigned int *hook_entries,
                           const unsigned int *underflows,
                           unsigned int *i)
{
        unsigned int h;
       
        /*(unsigned long)e % __alignof__(struct ipt_entry) != 0--不能整除,規(guī)則不完整
        (unsigned char *)e + sizeof(struct ipt_entry) >= limit--超過上限了*/
       
        if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
            || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
       
        /*e->next_offset
            < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)--規(guī)則太"短"了,小于最基本的長度
            */
        if (e->next_offset
            < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
                duprintf("checking: element %p size %u\n",
                         e, e->next_offset);
                return -EINVAL;
        }

        /* 檢查并設(shè)置正確的 hooks & underflows */
        for (h = 0; h < NF_IP_NUMHOOKS; h++) {
                if ((unsigned char *)e - base == hook_entries[h])
                        newinfo->hook_entry[h] = hook_entries[h];
                if ((unsigned char *)e - base == underflows[h])
                        newinfo->underflow[h] = underflows[h];
        }

        /* FIXME: underflows must be unconditional, standard verdicts
           < 0 (not IPT_RETURN). --RR */

        /* Clear counters and comefrom */
        e->counters = ((struct ipt_counters) { 0, 0 });                /*包和字節(jié)的計數(shù)器清零*/
        e->comefrom = 0;                                        /*環(huán)路計數(shù)器清零*/

        (*i)++;                                                        /*規(guī)則計數(shù)器累加*/
        return 0;
}

2、replace_table
前面說過,表中以struct ipt_table_info *private;表示實際數(shù)據(jù)區(qū)。但是在初始化賦值的時候,被設(shè)為
NULL,而表的初始變量都以模版的形式,放在struct ipt_replace *table;中。
注冊函數(shù)一開始,就聲明了:
struct ipt_table_info *newinfo;
然后對其分配了空間,將模塊中的初值拷貝了進來。所以replace_table要做的工作,主要就是把newinfo中的
值傳遞給table結(jié)構(gòu)中的private成員。

其函數(shù)原型如下:

static struct ipt_table_info *
replace_table(struct ipt_table *table,
              unsigned int num_counters,
              struct ipt_table_info *newinfo,
              int *error)
{
        struct ipt_table_info *oldinfo;

#ifdef CONFIG_NETFILTER_DEBUG
        {
                struct ipt_entry *table_base;
                unsigned int i;

                for (i = 0; i < smp_num_cpus; i++) {
                        table_base =
                                (void *)newinfo->entries
                                + TABLE_OFFSET(newinfo, i);

                        table_base->comefrom = 0xdead57ac;
                }
        }
#endif

        /* Do the substitution. */
        write_lock_bh(&table->lock);
        /* Check inside lock: is the old number correct? */
        if (num_counters != table->private->number) {
                duprintf("num_counters != table->private->number (%u/%u)\n",
                         num_counters, table->private->number);
                write_unlock_bh(&table->lock);
                *error = -EAGAIN;
                return NULL;
        }
        oldinfo = table->private;
        table->private = newinfo;
        newinfo->initial_entries = oldinfo->initial_entries;
        write_unlock_bh(&table->lock);

        return oldinfo;
}


3、list_named_find

在注冊函數(shù)中,調(diào)用
        /* Don't autoload: we'd eat our tail... */
        if (list_named_find(&ipt_tables, table->name)) {
                ret = -EEXIST;
                goto free_unlock;
        }
來檢查當前表是否已被注冊過了?梢,第一個參數(shù)為鏈表首部,第二個參數(shù)為當前表名。
其原型如下:
/* Find this named element in the list. */
#define list_named_find(head, name)                        \
LIST_FIND(head, __list_cmp_name, void *, name)

/* Return pointer to first true entry, if any, or NULL.  A macro
   required to allow inlining of cmpfn. */
#define LIST_FIND(head, cmpfn, type, args...)                \
({                                                        \
        const struct list_head *__i = (head);                \
                                                        \
        ASSERT_READ_LOCK(head);                                \
        do {                                                \
                __i = __i->next;                        \
                if (__i == (head)) {                        \
                        __i = NULL;                        \
                        break;                                \
                }                                        \
        } while (!cmpfn((const type)__i , ## args));        \
        (type)__i;                                        \
})

前面提過,表是一個雙向鏈表,在宏當中,以while進行循環(huán),以__i = __i->next;
進行遍歷,然后調(diào)用比較函數(shù)進行比較,傳遞過來的比較函數(shù)是__list_cmp_name。

比較函數(shù)很簡單:
static inline int __list_cmp_name(const void *i, const char *name)
{
        return strcmp(name, i+sizeof(struct list_head)) == 0;
}

4、list_prepend
當所有的初始化工作結(jié)束,就調(diào)用list_prepend來構(gòu)建鏈表了。
/* Prepend. */
static inline void
list_prepend(struct list_head *head, void *new)
{
        ASSERT_WRITE_LOCK(head);                /*設(shè)置寫互斥*/
        list_add(new, head);                        /*將當前表節(jié)點添加進鏈表*/
}
list_add就是一個構(gòu)建雙向鏈表的過程:
static __inline__ void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head, head->next);
}

static __inline__ void __list_add(struct list_head * new,
        struct list_head * prev,
        struct list_head * next)
{
        next->prev = new;
        new->next = next;
        new->prev = prev;
        prev->next = new;
}

論壇徽章:
0
5 [報告]
發(fā)表于 2005-12-16 12:54 |只看該作者
三、Hook的注冊

如果你對Netfilter的hook的注冊還不了解的話,推薦先到網(wǎng)上搜搜《深入Linux網(wǎng)絡(luò)核心堆!穊ioforge <alkerr@yifan.net>
看看先。(本節(jié)中有部份文字引自該文)

注冊一個hook函數(shù)是圍繞nf_hook_ops數(shù)據(jù)結(jié)構(gòu)的一個非常簡單的操作,nf_hook_ops數(shù)據(jù)結(jié)構(gòu)在linux/netfilter.h中定義,
該數(shù)據(jù)結(jié)構(gòu)的定義如下:
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ù)據(jù)結(jié)構(gòu)中的list成員用于維護Netfilter hook的列表。
hook成員是一個指向nf_hookfn類型的函數(shù)的指針,該函數(shù)是這個hook被調(diào)用時執(zhí)行的函數(shù)。
nf_hookfn同樣在linux/netfilter.h中定義。
pf這個成員用于指定協(xié)議族。有效的協(xié)議族在linux/socket.h中列出,但對于IPv4我們希望使用協(xié)議族PF_INET。
hooknum這個成員用于指定安裝的這個函數(shù)對應(yīng)的具體的hook類型:
        NF_IP_PRE_ROUTING    在完整性校驗之后,選路確定之前
        NF_IP_LOCAL_IN        在選路確定之后,且數(shù)據(jù)包的目的是本地主機
        NF_IP_FORWARD        目的地是其它主機地數(shù)據(jù)包
        NF_IP_LOCAL_OUT        來自本機進程的數(shù)據(jù)包在其離開本地主機的過程中
        NF_IP_POST_ROUTING    在數(shù)據(jù)包離開本地主機“上線”之前

最后,priority這個成員用于指定在執(zhí)行的順序中,這個hook函數(shù)應(yīng)當在被放在什么地方。
對于IPv4,可用的值在linux/netfilter_ipv4.h的nf_ip_hook_priorities枚舉中定義。

針對HOOK的注冊,在初始化函數(shù)中有:
        /* Register table */
        ret = ipt_register_table(&packet_filter);
        if (ret < 0)
                return ret;

        /* Register hooks */
        ret = nf_register_hook(&ipt_ops[0]);
        if (ret < 0)
                goto cleanup_table;

        ret = nf_register_hook(&ipt_ops[1]);
        if (ret < 0)
                goto cleanup_hook0;

        ret = nf_register_hook(&ipt_ops[2]);
        if (ret < 0)
                goto cleanup_hook1;
可見,注冊是通過nf_register_hook函數(shù)來完成,每一個Hook的相關(guān)信息,都在ipt_ops結(jié)構(gòu)數(shù)組中,它的成員變量前面已做分析,
來看看它的初始化值:
static struct nf_hook_ops ipt_ops[]
= { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_FILTER },
    { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_FORWARD, NF_IP_PRI_FILTER },
    { { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,
                NF_IP_PRI_FILTER }
};

對應(yīng)結(jié)構(gòu)各成員變量的含義,可見,filter表上總共設(shè)置了NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,用熟了iptables三個
鏈,對這三個東東應(yīng)該是刻骨銘心了。協(xié)議簇是PF_INET,初始化鏈表為NULL,處理函數(shù),前兩個為ipt_hook,后一個為ipt_local_out_hook,
優(yōu)化級均為NF_IP_PRI_FILTER。

hook的注冊,是通過nf_register_hook來完成的,它也是一個維護雙向鏈表的過程,值得注意的是,注冊的鉤子函數(shù),全部是放在全局變量
nf_hooks中,它是一個二維數(shù)組,函數(shù)一開始先遍歷它,找到合適的地方,再將當前節(jié)點插入之。(我們可以想像,將來調(diào)用鉤子函數(shù)時,就
是一個查找nf_hooks數(shù)組成員的過程)
int nf_register_hook(struct nf_hook_ops *reg)
{
        struct list_head *i;

        br_write_lock_bh(BR_NETPROTO_LOCK);
        /*尋找與當前待注冊節(jié)點reg匹配的數(shù)組元素(按協(xié)議族和Hook來匹配)*/
        for (i = nf_hooks[reg->pf][reg->hooknum].next;
             i != &nf_hooks[reg->pf][reg->hooknum];
             i = i->next) {
                if (reg->priority < ((struct nf_hook_ops *)i)->priority)
                        break;
        }
        /*添加節(jié)點*/
        list_add(&reg->list, i->prev);
        br_write_unlock_bh(BR_NETPROTO_LOCK);
        return 0;
}

能過表的注冊,HOOK的注冊,準備工作基本上就完成了,其它表的注冊和Hook的注冊,都是一樣的,可以對照分析,沒有必要再詳述了。
不過注冊也只是準備工作。重要的事情是對數(shù)據(jù)包的處理,對于filter來說,就是包過濾,對于nat來講,就是地址轉(zhuǎn)換。

四、數(shù)據(jù)包過濾

1、鉤子函數(shù)
以中轉(zhuǎn)包過濾為例(FORWARD),注冊的時候,向內(nèi)核注冊了一個ipt_hook的鉤子函數(shù)。
static unsigned int
ipt_hook(unsigned int hook,                                //Hook類型
         struct sk_buff **pskb,                                //數(shù)據(jù)包
         const struct net_device *in,                        //進入數(shù)據(jù)包接口
         const struct net_device *out,                        //離開數(shù)據(jù)包接口
         int (*okfn)(struct sk_buff *))                        //默認處理函數(shù)
{
        return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);
}

轉(zhuǎn)向到了ipt_do_table。也就是說,如果向內(nèi)核掛了鉤,中轉(zhuǎn)的數(shù)據(jù),將進入ipt_do_table函數(shù)。

2、鉤子函數(shù)被調(diào)用
鉤子函數(shù)被注冊了,但是內(nèi)核是如何調(diào)用它的呢?
在/src/net/ipv4下邊,對應(yīng)于input/output/forward,分別有Ip_forward.c,Ip_output.c,Ip_input.c。同樣繼續(xù)以forward為例,
(關(guān)于linux堆棧處理數(shù)據(jù)包流程的各個函數(shù)的作用等,這里就不進一步詳述,請參考其它相關(guān)資料)。

對于轉(zhuǎn)發(fā)的數(shù)據(jù),將進入Ip_forward.c中的ip_forward函數(shù),當處理完成后,在最后一句,可以看到:
        return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2,
                       ip_forward_finish);
事實上,你在linux的每一個數(shù)據(jù)轉(zhuǎn)發(fā)的"關(guān)節(jié)"的函數(shù)處,都可以發(fā)現(xiàn)這個宏的調(diào)用,它就是調(diào)用我們注冊的鉤子,其最后一個參數(shù)為
下一步處理的函數(shù),即,如果有鉤子函數(shù),則處理完所有的鉤子函數(shù)后,調(diào)用這個函數(shù)繼續(xù)處理,如果沒有注冊任何鉤子,則直接調(diào)用
此函數(shù)。

/* This is gross, but inline doesn't cut it for avoiding the function
   call in fast path: gcc doesn't inline (needs value tracking?). --RR */
#ifdef CONFIG_NETFILTER_DEBUG
#define NF_HOOK nf_hook_slow
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                        \
(list_empty(&nf_hooks[(pf)][(hook)])                                        \
? (okfn)(skb)                                                                \
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
#endif


先初略看看這個宏,okfn,我們已講過,它是下一步要處理的函數(shù),這里先調(diào)用
list_empty函數(shù)檢查nf_hooks是否為空,為空則表示沒有Hook注冊,則直接調(diào)用
okfn繼續(xù)處理。如果不為空,則轉(zhuǎn)入nf_hook_slow函數(shù):


int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
                 struct net_device *indev,
                 struct net_device *outdev,
                 int (*okfn)(struct sk_buff *))
{
        struct list_head *elem;
        unsigned int verdict;
        int ret = 0;

        /* This stopgap cannot be removed until all the hooks are audited. */
        if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
                kfree_skb(skb);
                return -ENOMEM;
        }
        if (skb->ip_summed == CHECKSUM_HW) {
                if (outdev == NULL) {
                        skb->ip_summed = CHECKSUM_NONE;
                } else {
                        skb_checksum_help(skb);
                }
        }

        /* We may already have this, but read-locks nest anyway */
        br_read_lock_bh(BR_NETPROTO_LOCK);

#ifdef CONFIG_NETFILTER_DEBUG
        if (skb->nf_debug & (1 << hook)) {
                printk("nf_hook: hook %i already set.\n", hook);
                nf_dump_skb(pf, skb);
        }
        skb->nf_debug |= (1 << hook);
#endif

        /*因為在調(diào)用NF_HOOK宏時,已經(jīng)指定了協(xié)議簇和鉤子名稱,所以要找到對應(yīng)的Hook點,是很容易的
        elem即為我們要找的,記得struct nf_hook_ops結(jié)構(gòu)么?雙向鏈表中的每個elem->hook就是我們關(guān)心的終極目標*/
        elem = &nf_hooks[pf][hook];
        /*找到后,遍歷雙向鏈表,進一步處理,以調(diào)用Hook函數(shù),并返回相應(yīng)的動作*/
        verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
                             outdev, &elem, okfn);
        if (verdict == NF_QUEUE) {
                NFDEBUG("nf_hook: Verdict = QUEUE.\n");
                nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
        }
        /*如果是接受,則調(diào)用okfn繼續(xù)處理,否則丟度之*/
        switch (verdict) {
        case NF_ACCEPT:
                ret = okfn(skb);       
                break;

        case NF_DROP:
                kfree_skb(skb);
                ret = -EPERM;
                break;
        }

        br_read_unlock_bh(BR_NETPROTO_LOCK);
        return ret;
}

再來看nf_iterate:

static unsigned int nf_iterate(struct list_head *head,
                               struct sk_buff **skb,
                               int hook,
                               const struct net_device *indev,
                               const struct net_device *outdev,
                               struct list_head **i,
                               int (*okfn)(struct sk_buff *))
{
        /*循環(huán)遍歷所有注冊的鉤子函數(shù),包括系統(tǒng)默認的三個,用戶自定義的……*/
        for (*i = (*i)->next; *i != head; *i = (*i)->next) {
                struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
                /*就在這里調(diào)用了*/
                switch (elem->hook(hook, skb, indev, outdev, okfn)) {
                case NF_QUEUE:
                        return NF_QUEUE;

                case NF_STOLEN:
                        return NF_STOLEN;

                case NF_DROP:
                        return NF_DROP;

                case NF_REPEAT:
                        *i = (*i)->prev;
                        break;

#ifdef CONFIG_NETFILTER_DEBUG
                case NF_ACCEPT:
                        break;

                default:
                        NFDEBUG("Evil return from %p(%u).\n",
                                elem->hook, hook);
#endif
                }
        }
        return NF_ACCEPT;
}   

解釋一下各個返回值:

NF_DROP                丟棄該數(shù)據(jù)包
NF_ACCEPT            保留該數(shù)據(jù)包
NF_STOLEN            忘掉該數(shù)據(jù)包
NF_QUEUE            將該數(shù)據(jù)包插入到用戶空間
NF_REPEAT            再次調(diào)用該hook函數(shù)

這樣,最終關(guān)心的還是每一個注冊的函數(shù),這樣又回到本節(jié)開頭所說的ipt_do_table……

說了這么多,其實只是把最簡單,最沒有用的講了,難的還在于Hook函數(shù),呵呵命運的魔輪已經(jīng)開啟,每一個數(shù)據(jù)包的命運將會如何?
那就要去分析每一個Hook函數(shù)了……

未完,待續(xù)……

論壇徽章:
0
6 [報告]
發(fā)表于 2005-12-16 15:05 |只看該作者
里面有太多的結(jié)構(gòu)體不了解,而對應(yīng)的 include 文件又不好找,且 include 文件里面有可能又存在嵌套 include
對于我這樣的水平,該如何學(xué)啊 ^_^

論壇徽章:
0
7 [報告]
發(fā)表于 2005-12-16 15:22 |只看該作者
原帖由 platinum 于 2005-12-16 15:05 發(fā)表
里面有太多的結(jié)構(gòu)體不了解,而對應(yīng)的 include 文件又不好找,且 include 文件里面有可能又存在嵌套 include
對于我這樣的水平,該如何學(xué)啊 ^_^


我是先找結(jié)構(gòu)中重要的成員拿下來(網(wǎng)上找資料,源碼中本來的注釋),剩下的就在讀代碼的時候,再明白一些,最后個別的,到現(xiàn)在也沒有明白。

至于不好找的問題,應(yīng)該不會吧,SourceInsight很好跟的。

論壇徽章:
0
8 [報告]
發(fā)表于 2005-12-16 15:26 |只看該作者
原帖由 platinum 于 2005-12-16 15:05 發(fā)表
里面有太多的結(jié)構(gòu)體不了解,而對應(yīng)的 include 文件又不好找,且 include 文件里面有可能又存在嵌套 include
對于我這樣的水平,該如何學(xué)啊 ^_^


白金兄習(xí)慣在什么系統(tǒng)下工作?
如果是linux,我用vim+ctags足夠瀏覽整個kernel了。

ctags -R arch/i386 fs include/asm-i386 include/asm-generic include/asm-net ……
vim
:ts the-symbol
:tf ──第一個
:tn ──下一個
:tp ──上一個
:tl ──最后一個

如果在安裝TagList的Vim插件,就更爽了

論壇徽章:
0
9 [報告]
發(fā)表于 2005-12-16 15:35 |只看該作者
原帖由 albcamus 于 2005-12-16 15:26 發(fā)表


白金兄習(xí)慣在什么系統(tǒng)下工作?
如果是linux,我用vim+ctags足夠瀏覽整個kernel了。

ctags -R arch/i386 fs include/asm-i386 include/asm-generic include/asm-net ……
vim
:ts the-symbol
:tf ── ...


albcamus:你不是也在看Netfilter?能不能把你看的分享出來,繼續(xù)寫下去呢……?

論壇徽章:
0
10 [報告]
發(fā)表于 2005-12-16 15:45 |只看該作者
原帖由 獨孤九賤 于 2005-12-16 15:35 發(fā)表


albcamus:你不是也在看Netfilter?能不能把你看的分享出來,繼續(xù)寫下去呢……?


沒看netfilter呢
我學(xué)的比較雜,想起什么來學(xué)什么,現(xiàn)在想鉆CPU和中斷,以及協(xié)議棧, 得慢慢來
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP