欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Linux netfilter源碼分析

Linux netfilter源碼分析

一、IP報文的接收到hook函數的調用

1.1  ip_input.c    ip_rcv()函數

以接收到的報文為例,類(lèi)似的還有ip_forward(ip_forward.c)ip_output(ip_output.c)

int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)

{

    struct iphdr *iph;                          //定義一個(gè)ip報文的數據報頭

    u32 len;

 

    if (skb->pkt_type == PACKET_OTHERHOST)

        goto drop;                              //數據包不是發(fā)給我們的

 

    IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);    //收到數據包統計量加1

 

    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)

   {

        /* 如果數據報是共享的,則復制一個(gè)出來(lái),此時(shí)復制而出的已經(jīng)和socket脫離了關(guān)系 */

        IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

        goto out;

   }

 

   if (!pskb_may_pull(skb, sizeof(struct iphdr)))

        goto inhdr_error;  //對數據報的頭長(cháng)度進(jìn)行檢查,

 

   iph = skb->nh.iph;  //取得數據報的頭部位置

 

    if (iph->ihl < 5 || iph->version != 4)  //版本號或者頭長(cháng)度不對,

        goto inhdr_error; //頭長(cháng)度是以4字節為單位的,所以5表示的是20字節

 

    if (!pskb_may_pull(skb, iph->ihl*4))

        goto inhdr_error;

 

    if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))

        goto inhdr_error; //檢查報文的檢驗和字段

 

    len = ntohs(iph->tot_len);

    if (skb->len < len || len < (iph->ihl*4))

        goto inhdr_error; //整個(gè)報文長(cháng)度不可能比報頭長(cháng)度小

 

    if (pskb_trim_rcsum(skb, len))

    { //對數據報進(jìn)行裁減,這樣可以分片發(fā)送過(guò)來(lái)的數據報不會(huì )有重復數據

        IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

        goto drop;

    }

 

   return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); //通過(guò)回調函數調用ip_rcv_finish

 

 

    inhdr_error:

        IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);

    drop:

        kfree_skb(skb); //丟掉數據報

    out:

        return NET_RX_DROP;

}

 

1.2  include/linux/netfilter.h  NF_HOOK

#ifdef CONFIG_NETFILTER_DEBUG

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)\

 nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)

#define NF_HOOK_THRESH 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), INT_MIN))

#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)       \

(list_empty(&nf_hooks[(pf)][(hook)])                                  \

  (okfn)(skb)                                                       \

 : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))

#endif

/* 如果nf_hooks[PF_INET][NF_IP_FORWARD]所指向的鏈表為空(即該鉤子上沒(méi)有掛處理函數),則直接調用okfn;否則,則調用net/core/netfilter.c::nf_hook_slow()轉入Netfilter的處理。  */

 

1.3  net/core/netfilter.c  nf_kook_slow()函數

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,

                 struct net_device *indev,

                 struct net_device *outdev,

                 int (*okfn)(struct sk_buff *),

                 int hook_thresh)

{

    struct list_head *elem;

    unsigned int verdict;

    int ret = 0;

 

    rcu_read_lock();

 

    /*取得對應的鏈表首部*/

    elem = &nf_hooks[pf][hook];

next_hook:

    /*調用對應的鉤子函數*/

    verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,

                             outdev, &elem, okfn, hook_thresh);

 

    /*判斷返回值,做相應的處理*/

    if (verdict == NF_ACCEPT || verdict == NF_STOP) {

        ret = 1;    /*前面提到過(guò),返回1,則表示裝繼續調用okfn函數指針*/

        goto unlock;

    } else if (verdict == NF_DROP) {

        kfree_skb(*pskb);                /*刪除數據包,需要釋放skb*/

        ret = -EPERM;

    } else if (verdict == NF_QUEUE) {

        NFDEBUG("nf_hook: Verdict = QUEUE.\n");

            if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))

                goto next_hook;

    }

unlock:

    rcu_read_unlock();

    return ret;

}

 

1.4   net/core/netfilter.c   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 *),

                            int hook_thresh)

{

       /*

        * The caller must not block between calls to this

        * function because of risk of continuing from deleted element.

        */

 

/* 依次調用指定hook點(diǎn)下的所有nf_hook_ops->(*hook)函數,這些nf_hook_ops里有filter表注冊的,有mangle表注冊的,等等。

list_for_each_continue_rcu函數是一個(gè)for循環(huán)的宏,當調用結點(diǎn)中的hook函數后,根據返回值進(jìn)行相應處理。如果hook函數的返回值是NF_QUEUE,NF_STOLEN,NF_DROP時(shí),函數返回該值;如果返回值是NF_REPEAT時(shí),則跳到前一個(gè)結點(diǎn)繼續處理;如果是其他值,由下一個(gè)結點(diǎn)繼續處理。如果整條鏈表處理完畢,返回值不是上面四個(gè)值,則返回NF_ACCEPT。*/

 

       list_for_each_continue_rcu(*i, head) {

            struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

 

            if (hook_thresh > elem->priority)

                continue;

 

            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;

            }

        }

      

       return NF_ACCEPT;

}

 

二、ipt_table數據結構和表的初始化

2.1  include/linux/netfilter_ipv4/ip_tables.h struct ipt_table 表結構

struct ipt_table

{

    struct list_head list;

    /* 表鏈 */

    char name[IPT_TABLE_MAXNAMELEN];

    /* 表名,如"filter"、"nat"等,為了滿(mǎn)足自動(dòng)模塊加載的設計,包含該表的模塊應命名為iptable_'name'.o */

    struct ipt_replace *table;

    /* 表模子,初始為initial_table.repl */

    unsigned int valid_hooks;

    /* 位向量,標示本表所影響的HOOK */

    rwlock_t lock;

    /* 讀寫(xiě)鎖,初始為打開(kāi)狀態(tài) */

    struct ipt_table_info *private;

    /* iptable的數據區,見(jiàn)下 */

    struct module *me;

    /* 是否在模塊中定義 */

};

 

2.2  struct ipt_table_info是實(shí)際描述表的數據結構 ip_tables.c

struct ipt_table_info

{

    unsigned int size;

    /* 表大小 */

    unsigned int number;

    /* 表中的規則數 */

    unsigned int initial_entries;

    /* 初始的規則數,用于模塊計數 */

    unsigned int hook_entry[NF_IP_NUMHOOKS];

    /* 記錄所影響的HOOK的規則入口相對于下面的entries變量的偏移量 */

    unsigned int underflow[NF_IP_NUMHOOKS];

    /* hook_entry相對應的規則表上限偏移量,當無(wú)規則錄入時(shí),相應的hook_entryunderflow均為0 */

    char entries[0] ____cacheline_aligned;

    /* 規則表入口 */

};

 

2.3   include/linux/netfilter_ipv4  規則用struct ipt_entry結構表示,包含匹配用的IP頭部分、一個(gè)Target0個(gè)或多個(gè)Match。由于Match數不定,所以一條規則實(shí)際的占用空間是可變的。結構定義如下:

struct ipt_entry

{

    struct ipt_ip ip;

    /* 所要匹配的報文的IP頭信息 */

    unsigned int nfcache;

    /* 位向量,標示本規則關(guān)心報文的什么部分,暫未使用 */

    u_int16_t target_offset;

    /* target區的偏移,通常target區位于match區之后,而match區則在ipt_entry的末尾;

    初始化為sizeof(struct ipt_entry),即假定沒(méi)有match */

    u_int16_t next_offset;

    /* 下一條規則相對于本規則的偏移,也即本規則所用空間的總和,

    初始化為sizeof(struct ipt_entry)+sizeof(struct ipt_target),即沒(méi)有match */

    unsigned int comefrom;

    /* 規則返回點(diǎn),標記調用本規則的HOOK號,可用于檢查規則的有效性 */

    struct ipt_counters counters;

    /* 記錄該規則處理過(guò)的報文數和報文總字節數 */

    unsigned char elems[0];

    /*target或者是match的起始位置 */

}

 

2.4  iptables的初始化init(void) ,以filter表為例 iptable_filter.c

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;

 

    /* Register table */

    ret = ipt_register_table(&packet_filter);     //注冊filter

 

    if (ret < 0)

        return ret;

 

    /* Register hooks */

    ret = nf_register_hook(&ipt_ops[0]);       //注冊三個(gè)HOOK

 

    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;

}

 

/* ipt_register_table函數的參數packet_filter包含了待注冊表的各個(gè)參數 */

static struct ipt_table packet_filter = {

    .name           = "filter",

    .table          = &initial_table.repl,

    .valid_hooks    = FILTER_VALID_HOOKS,

    .lock           = RW_LOCK_UNLOCKED,

    .me             = THIS_MODULE

};

 

/* 上面的&initial_table.repl是一個(gè)ipt_replace結構,也就是ipt_table-*table的初始值。

下面是ipt_replace結構的定義,它和ipt_table_info很相似,基本上就是用來(lái)初始化ipt_table中的ipt_table_info *private的,這個(gè)結構不同于ipt_table_info之處在于,它還要保存表的舊的規則信息 */

 

struct ipt_replace

{

    char name[IPT_TABLE_MAXNAMELEN];            /* 表名 */

    unsigned int valid_hooks;                   /* 影響的hook */

    unsigned int num_entries;                   /* entry */

    unsigned int size;                          /* entry的總大小 */

    unsigned int hook_entry[NF_IP_NUMHOOKS];   /* 規則入口的偏移值 */

    unsigned int underflow[NF_IP_NUMHOOKS];     /* 規則的最大偏移值 */

    unsigned int num_counters;                  /* 規則數 */

    struct ipt_counters __user *counters;          

    struct ipt_entry entries[0];                /* 規則入口 */

};

 

/* 下面是initial_table.repl的初始化 */

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"

      }

    }

};

 

三、ipt_table表的注冊

init()函數初始化時(shí)調用了ipt_register_table函數進(jìn)行表的注冊

 

3.1  ip_tables.c 表的注冊 ipt_register_table

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用于模塊計數器累加,主要是為了防止模塊異常刪除,對應的宏MOD_DEC_USE_COUNT就是累減了*/

    MOD_INC_USE_COUNT; 

 

    /*為每個(gè)CPU分配規則空間*/

    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;

    }

 

    /*將規則項拷貝到新表項的第一個(gè)cpu空間里面*/

    memcpy(newinfo->entries, table->table->entries, table->table->size);

 

    /*translate_table函數將newinfo表示的table的各個(gè)規則進(jìn)行邊界檢查,然后對于newinfo所指的ipt_talbe_info結構中的hook_entriesunderflows賦予正確的值,最后將表項向其他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)存在,釋放空間 并且遞減模塊計數 */

 

    /* Don't autoload: we'd eat our tail... */

    if (list_named_find(&ipt_tables, table->name)) {

        ret = -EEXIST;

        goto free_unlock;

    }

 

    /* 替換table. */

    /* Simplifies replace_table code. */

    table->private = &bootstrap;

 

    if (!replace_table(table, 0, newinfo, &ret))

        goto free_unlock;

 

    duprintf("table->private->number = %u\n",

                table->private->number);

 

    /* 保存初始規則計數器 */

    /* save number of initial entries */

    table->private->initial_entries = table->private->number;

    table->lock = RW_LOCK_UNLOCKED;

 

    /*將表添加進(jìn)鏈表*/

    list_prepend(&ipt_tables, table);

 

    unlock:

        up(&ipt_mutex);

 

    return ret;

 

    free_unlock:

        vfree(newinfo);

        MOD_DEC_USE_COUNT;

        goto unlock;

}

 

3.2  ip_tables.c   translate_table()函數

/* 函數:translate_table()

* 參數:

*   name:表名稱(chēng);

*   valid_hooks:當前表所影響的hook

*   newinfo:包含當前表的所有信息的結構

*  size:表的大小

*  number:表中的規則數

*   hook_entries:記錄所影響的HOOK的規則入口相對于下面的entries變量的偏移量

*   underflows:與hook_entry相對應的規則表上限偏移量

* 作用:

*   translate_table函數將newinfo表示的table的各個(gè)規則進(jìn)行邊界檢查,然后對于newinfo所指的ipt_talbe_info結構中的hook_entriesunderflows賦予正確的值,最后將表項向其他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;

        /* 遍歷所有規則,檢查所有偏量,檢查的工作都是由IPT_ENTRY_ITERATE這個(gè)宏來(lái)完成,并且它的最后一個(gè)參數i,返回表的所有規則數. */

        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;

 

        /*實(shí)際計算得到的規則數與指定的不符*/

        if (i != number) {

                duprintf("translate_table: %u not %u entries\n",

                         i, number);

                return -EINVAL;

        }

 

        /* 因為函數一開(kāi)始將HOOK的偏移地址全部初始成了不可能的值,而在上一個(gè)宏的遍歷中設置了hook_entriesunderflows的值,這里對它們進(jìn)行檢查 */

        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中不存在規則環(huán)*/

        if (!mark_source_chains(newinfo, valid_hooks))

                return -ELOOP;

 

        /* tables中的規則項進(jìn)行完整性檢查,保證每一個(gè)規則項在形式上是合法的*/

        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;

        }

 

        /* 為每個(gè)CPU復制一個(gè)完整的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;

}

 

3.3   IPT_ENTRY_ITERAT  ip_tables.h

用來(lái)遍歷每一個(gè)規則,然后調用其第三個(gè)參數(函數指針)進(jìn)行處理,前兩個(gè)參數分別表示規則的起始位置和規則總大小,后面的參數則視情況而定。

 

#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;                                                      \

})

 

/* translate_table中出現了三次,分別是 */

IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,

                                check_entry_size_and_hooks,

                                newinfo,

                                newinfo->entries,

                                newinfo->entries + size,

                                hook_entries, underflows, &i);

 

IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,

                                check_entry, name, size, &i);

IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,

                                  cleanup_entry, &i);

即是在遍歷到每條entry時(shí)分別調用

 

check_entry_size_and_hooks,check_entry,  cleanup_entry,三個(gè)函數

 

check_entry有大用處,后面解釋

 

3.4   list_named_find()函數   listhelp.h

在注冊函數中,調用

        list_named_find(&ipt_tables, table->name)

來(lái)檢查當前表是否已被注冊過(guò)了??梢?jiàn),第一個(gè)參數為鏈表首部,第二個(gè)參數為當前表名。

其原型如下:

#define list_named_find(head, name)                             \

LIST_FIND(head, __list_cmp_name, void *, name)

 

#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;                                              \

})

 

前面提過(guò),表是一個(gè)雙向鏈表,在宏當中,以while進(jìn)行循環(huán),以__i = __i->next;

進(jìn)行遍歷,然后調用比較函數進(jìn)行比較,傳遞過(guò)來(lái)的比較函數是__list_cmp_name。

 

比較函數很簡(jiǎn)單:

static inline int __list_cmp_name(const void *i, const char *name)

{

        return strcmp(name, i+sizeof(struct list_head)) == 0;

}

 

3.5  replace_table()函數    ip_tables.c

表中以struct ipt_table_info *private;表示實(shí)際數據區。但是在初始化賦值的時(shí)候,被設為NULL,而表的初始變量都以模版的形式,放在struct ipt_replace *table;中。

注冊函數一開(kāi)始,就聲明了:struct ipt_table_info *newinfo;

然后對其分配了空間,將模塊中的初值拷貝了進(jìn)來(lái)。所以replace_table要做的工作,主要就是把newinfo中的值傳遞給table結構中的private成員。

 

replace_table(struct ipt_table *table,

             unsigned int num_counters,

             struct ipt_table_info *newinfo,

             int *error)

{

    struct ipt_table_info *oldinfo;

 

    write_lock_bh(&table->lock);

 

    if (num_counters != table->private->number) {

        duprintf("num_counters != table->private->number (%u/%u)\n",

            num_counters, table->private->number);

 

        /* ipt_register_table函數中,replace_table函數之前有一句 table->private = &bootstrap;private初始化為bootstrap,即{ 0,0,0,{0},{0},{}}  */

        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.6  list_prepend()函數   listhelp.h

當所有的初始化工作結束,就調用list_prepend來(lái)構建鏈表了。

 

static inline void

list_prepend(struct list_head *head, void *new)

{

    ASSERT_WRITE_LOCK(head);                /*設置寫(xiě)互斥*/

    list_add(new, head);                        /*將當前表節點(diǎn)添加進(jìn)鏈表*/

}

 

list_add就是一個(gè)構建雙向鏈表的過(guò)程:

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;

}

 

四、nf_hook_ops 鉤子的注冊

filter表的初始化函數static int __init init(void)中除了有一個(gè)nf_register_hook函數注冊一個(gè)tables外,還由nf_register_hook函數注冊了3個(gè)hook

 

4.1 nf_hook_ops數據結構 netfilter.h

struct nf_hook_ops

{

    struct list_head list;                        //鏈表成員

    /* User fills in from here down. */

    nf_hookfn *hook;                        //鉤子函數指針

    struct module *owner;

    int pf;                                        //協(xié)議簇,對于ipv4而言,是PF_INET

    int hooknum;                                //hook類(lèi)型

    /* Hooks are ordered in ascending priority. */

    int priority;                                //優(yōu)先級

};

 

list成員用于維護Netfilter hook的列表。

hook成員是一個(gè)指向nf_hookfn類(lèi)型的函數的指針,該函數是這個(gè)hook被調用時(shí)執行的函數。nf_hookfn同樣在linux/netfilter.h中定義。

pf這個(gè)成員用于指定協(xié)議族。有效的協(xié)議族在linux/socket.h中列出,但對于IPv4我們使用協(xié)議族PF_INET。

 

hooknum這個(gè)成員用于指定安裝的這個(gè)函數對應的具體的hook類(lèi)型:

    NF_IP_PRE_ROUTING    在完整性校驗之后,選路確定之前

    NF_IP_LOCAL_IN        在選路確定之后,且數據包的目的是本地主機

    NF_IP_FORWARD        目的地是其它主機地數據包

    NF_IP_LOCAL_OUT        來(lái)自本機進(jìn)程的數據包在其離開(kāi)本地主機的過(guò)程中

    NF_IP_POST_ROUTING    在數據包離開(kāi)本地主機上線(xiàn)之前

 

再看看它的初始化,仍以filter表為例

 

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 }

};

 

4.2   int nf_register_hook函數   netfilter.c

注冊實(shí)際上就是在一個(gè)nf_hook_ops鏈表中再插入一個(gè)nf_hook_ops結構

int nf_register_hook(struct nf_hook_ops *reg)

{

    struct list_head *i;

 

    spin_lock_bh(&nf_hook_lock);

    list_for_each(i, &nf_hooks[reg->pf][reg->hooknum]) {

 

    if (reg->priority < ((struct nf_hook_ops *)i)->priority)

        break;

    }

 

    list_add_rcu(&reg->list, i->prev);

    spin_unlock_bh(&nf_hook_lock);

    synchronize_net();

    return 0;

}

 

list_for_each 函數遍歷當前待注冊的鉤子的協(xié)議pfHook類(lèi)型所對應的鏈表,其首地址是&nf_hooks[reg->pf][reg->hooknum],如果當前待注冊鉤子的優(yōu)先級小于匹配的的節點(diǎn)的優(yōu)先級,則找到了待插入的位置,也就是說(shuō),按優(yōu)先級的升序排列。

list_add_rcu把當前節點(diǎn)插入到查到找的適合的位置,這樣,完成后,所有pf協(xié)議下的hooknum類(lèi)型的鉤子,都被注冊到&nf_hooks[reg->pf][reg->hooknum]為首的鏈表當中了。

 

4.3  ipt_hook鉤子函數   iptable_raw.c

注冊nf_hook_ops,也就向內核注冊了一個(gè)鉤子函數,這些函數有ipt_hook,ipt_local_hook,ipt_route_hook,ipt_local_out_hook等。

前面在nf_iterate()里調用的鉤子函數就是它了

下面是ipt_hook函數的定義:

 

static unsigned int

ipt_hook(unsigned int hook,          /* hook點(diǎn) */

       struct sk_buff **pskb,

       const struct net_device *in,

       const struct net_device *out,

       int (*okfn)(struct sk_buff *))     /* 默認處理函數 */

{

    /* 參數&packet_filter是由注冊該nf_hook_ops的表(filter)決定的,也有可能是&packet_raw */

    return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);

 

}

 

實(shí)際上是直接調用ipt_do_table(ip_tables.c)函數,接下來(lái)就是根據table里面的entry來(lái)處理數據包了,一個(gè)table就是一組防火墻規則的集合,而一個(gè)entry就是一條規則,每個(gè)entry由一系列的matches和一個(gè)target組成,一旦數據包匹配了該某個(gè)entry的所有matches,就用target來(lái)處理它,Match又分為兩部份,一部份為一些基本的元素,如來(lái)源/目的地址,進(jìn)/出網(wǎng)口,協(xié)議等,對應了struct ipt_ip,我們常常將其稱(chēng)為標準的match,另一部份match則以插件的形式存在,是動(dòng)態(tài)可選擇,也允許第三方開(kāi)發(fā)的,常常稱(chēng)為擴展的match,如字符串匹配,p2p匹配等。同樣,規則的target也是可擴展的。這樣,一條規則占用的空間,可以分為:struct ipt_ip+n*match+n*target,(n表示了其個(gè)數,這里的match指的是可擴展的match部份)。

五、 ipt_do_table()函數,數據包的過(guò)濾

5.1 ipt_entry 相關(guān)結構  ip_tables.h

ipt_entry結構前面有過(guò)了,再看一遍

struct ipt_entry

{

    struct ipt_ip ip;

    /* 所要匹配的報文的IP頭信息 */

    unsigned int nfcache;

    /* 位向量,標示本規則關(guān)心報文的什么部分,暫未使用 */

    u_int16_t target_offset;

    /* target區的偏移,通常target區位于match區之后,而match區則在ipt_entry的末尾;

    初始化為sizeof(struct ipt_entry),即假定沒(méi)有match */

    u_int16_t next_offset;

    /* 下一條規則相對于本規則的偏移,也即本規則所用空間的總和,

    初始化為sizeof(struct ipt_entry)+sizeof(struct ipt_target),即沒(méi)有match */

    unsigned int comefrom;

    /* 位向量,標記調用本規則的HOOK號,可用于檢查規則的有效性 */

    struct ipt_counters counters;

    /* 記錄該規則處理過(guò)的報文數和報文總字節數 */

    unsigned char elems[0];

    /*target或者是match的起始位置 */

}

 

ipt_ip結構  ip_tables.h

struct ipt_ip {

    struct in_addr src, dst;         /* 來(lái)源/目的地址 */

    struct in_addr smsk, dmsk;     /* 來(lái)源/目的地址的掩碼 */

    char iniface[IFNAMSIZ], outiface[IFNAMSIZ];    /*輸入輸出網(wǎng)絡(luò )接口*/

    unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];

    u_int16_t proto;    /* 協(xié)議, 0 = ANY */

    u_int8_t flags;      /* 標志字段 */

    u_int8_t invflags;    /* 取反標志 */

};

 

5.2  ipt_do_table函數   ip_tables.c

unsigned int

ipt_do_table(struct sk_buff **pskb,

           unsigned int hook,

           const struct net_device *in,

           const struct net_device *out,

           struct ipt_table *table,

           void *userdata)

{

 

    static const char nulldevname[IFNAMSIZ]     \

             __attribute__((aligned(sizeof(long))));

 

    u_int16_t offset;

    struct iphdr *ip;

    u_int16_t datalen;

    int hotdrop = 0;

 

    /* Initializing verdict to NF_DROP keeps gcc happy. */

    unsigned int verdict = NF_DROP;

    const char *indev, *outdev;

    void *table_base;

    struct ipt_entry *e, *back;

 

    /* Initialization */

    ip = (*pskb)->nh.iph;                /* 獲取IP */

    datalen = (*pskb)->len - ip->ihl * 4;   /*指向數據區*/

    indev = in ? in->name : nulldevname;     /*取得輸入設備名*/

    outdev = out ? out->name : nulldevname;    /*取得輸出設備名*/

    offset = ntohs(ip->frag_off) & IP_OFFSET;      /*設置分片包的偏移*/

    read_lock_bh(&table->lock);     /*設置互斥鎖*/

    IP_NF_ASSERT(table->valid_hooks & (1 << hook));

     

    /*檢驗HOOK,debug用的*/

    /*獲取當前表的當前CPU的規則入口*/

    table_base = (void *)table->private->entries

                + TABLE_OFFSET(table->private, smp_processor_id());

 

    /*獲得當前表的當前Hook的規則的起始偏移量*/

    e = get_entry(table_base, table->private->hook_entry[hook]);

 

    /*獲得當前表的當前Hook的規則的上限偏移量*/

    /* For return from builtin chain */

    back = get_entry(table_base, table->private->underflow[hook]);

 

    /*  do ……  while(!hotdrop)進(jìn)行規則的匹配   */

    do {

        IP_NF_ASSERT(e);

        IP_NF_ASSERT(back);

        (*pskb)->nfcache |= e->nfcache;

        /*

 

           匹配IP包,成功則繼續匹配下去,否則跳到下一個(gè)規則 

           ip_packet_match匹配標準match, 也就是ip報文中的一些基本的元素,如來(lái)源/目的地址,進(jìn)/出網(wǎng)口,協(xié)議等,因為要匹配的內容是固定的,所以具體的函數實(shí)現也是固定的。

           IPT_MATCH_ITERATE (應該猜到實(shí)際是調用第二個(gè)參數do_match函數)匹配擴展的match,如字符串匹配,p2p匹配等,因為要匹配的內容不確定,所以函數的實(shí)現也是不一樣的,所以do_match的實(shí)現就和具體的match模塊有關(guān)了。

           這里的&e->ip就是上面的ipt_ip結構

 

        */

 

        if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {

            struct ipt_entry_target *t;

 

            if (IPT_MATCH_ITERATE(e, do_match,

                                  *pskb, in, out,

                                  offset, &hotdrop) != 0)

                goto no_match;    /*不匹配則跳到 no_match,往下一個(gè)規則*/

 

                /* 匹配則繼續執行 */

                /* 這個(gè)宏用來(lái)分別處理字節計數器和分組計數器這兩個(gè)計數器 */

                ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);

 

                /*獲取規則的target的偏移地址*/

                t = ipt_get_target(e);

                IP_NF_ASSERT(t->u.kernel.target);

 

                /* 下面開(kāi)始匹備target */

                /* Standard target? */

 

                if (!t->u.kernel.target->target) {

                    int v;

 

                    v = ((struct ipt_standard_target *)t)->verdict;

                   

                    if (v < 0) {

                        /* Pop from stack? */

                        if (v != IPT_RETURN) {

                            verdict = (unsigned)(-v) - 1;

                            break;

                    }

                       

                    e = back;

                    back = get_entry(table_base, back->comefrom);

                    continue;

                }

 

                if (table_base + v != (void *)e + e->next_offset) {

                    /* Save old back ptr in next entry */

                    struct ipt_entry *next = (void *)e + e->next_offset;

 

                    next->comefrom = (void *)back - table_base;

 

                    /* set back pointer to next entry */

                    back = next;

                }

 

                e = get_entry(table_base, v);

                }

                else {

                    verdict = t->u.kernel.target->target(pskb,

                                                  in, out,

                                                  hook,

                                                  t->data,

                                                  userdata);

 

                    /* Target might have changed stuff. */

                    ip = (*pskb)->nh.iph;

                    datalen = (*pskb)->len - ip->ihl * 4;

 

                    if (verdict == IPT_CONTINUE)

                        e = (void *)e + e->next_offset;

                    else

                        /* Verdict */

                        break;

                    }

            }

        else

        {

            no_match:

            e = (void *)e + e->next_offset;  /* 匹配失敗,跳到下一個(gè)規則 */

        }

    } while (!hotdrop);

 

    read_unlock_bh(&table->lock);

 

#ifdef DEBUG_ALLOW_ALL

    return NF_ACCEPT;

#else

    if (hotdrop)

        return NF_DROP;

    else return verdict;

#endif

}

 

5.3   標準的match   ip_packet_match函數  ip_tables.c

 

static inline int

ip_packet_match(const struct iphdr *ip,

           const char *indev,

           const char *outdev,

           const struct ipt_ip *ipinfo,

           int isfrag)

{

    size_t i;

    unsigned long ret;

 

    /*定義一個(gè)宏,當boolinvflg的是一真一假的情況時(shí),返回真。注意這里使用兩個(gè)!的目的是使得這樣計算后的值域只取01兩個(gè)值*/

    #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))

 

    /*處理源和目標ip地址,這個(gè)if語(yǔ)句的意義是:到達分組的源ip地址經(jīng)過(guò)掩碼處理后與規則中的ip不匹配并且規則中沒(méi)有包含對ip地址的取反,或者規則中包含了對匹配地址的取反,但到達分組的源ip與規則中的ip地址匹配,if的第一部分返回真,同樣道理處理到達分組的目的ip地址。這兩部分任意部分為真時(shí),源或者目標地址不匹配。*/

    if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,

            IPT_INV_SRCIP)

            || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,

            IPT_INV_DSTIP)) {

        dprintf("Source or dest mismatch.\n");

        dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",

             NIPQUAD(ip->saddr),

             NIPQUAD(ipinfo->smsk.s_addr),

             NIPQUAD(ipinfo->src.s_addr),

             ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");

        dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",

             NIPQUAD(ip->daddr),

             NIPQUAD(ipinfo->dmsk.s_addr),

             NIPQUAD(ipinfo->dst.s_addr),

             ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");

        return 0;

    }

 

    /*接著(zhù)處理輸入和輸出的接口,for語(yǔ)句處理接口是否與規則中的接口匹配,不匹配時(shí),ret返回非零,離開(kāi)for語(yǔ)句后,處理接口的取反問(wèn)題:當接口不匹配并且接口不取反,或者接口匹配,但是接口取反,說(shuō)明接口不匹配。*/

    /* Look for ifname matches; this should unroll nicely. */

 

    /*輸入接口*/

    for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {

        ret |= (((const unsigned long *)indev)[i]

                 ^ ((const unsigned long *)ipinfo->iniface)[i])

                 & ((const unsigned long *)ipinfo->iniface_mask)[i];

    }

 

    if (FWINV(ret != 0, IPT_INV_VIA_IN)) {

        dprintf("VIA in mismatch (%s vs %s).%s\n",

                 indev, ipinfo->iniface,

                 ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");

        return 0;

    }

 

    /*輸出接口*/

    for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {

        ret |= (((const unsigned long *)outdev)[i]

                 ^ ((const unsigned long *)ipinfo->outiface)[i])

                 & ((const unsigned long *)ipinfo->outiface_mask)[i];

    }

 

    if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {

        dprintf("VIA out mismatch (%s vs %s).%s\n",

                 outdev, ipinfo->outiface,

                 ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");

        return 0;

    }

 

    /* 檢查協(xié)議是否匹配 */

    /* Check specific protocol */

    if (ipinfo->proto

          && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {

        dprintf("Packet protocol %hi does not match %hi.%s\n",

                 ip->protocol, ipinfo->proto,

                 ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");

        return 0;

    }

 

    /*處理分片包的匹配情況*/

    /* If we have a fragment rule but the packet is not a fragment

       * then we return zero */

 

    if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {

        dprintf("Fragment rule but not fragment.%s\n",

                 ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");

        return 0;

    }

 

    return 1;       /* 以上所有都匹配則返回1 */

}

 

六、 擴展的match

6.1 do_match函數  ip_tables.c

do_match通過(guò)IPT_MATCH_ITERATE宏來(lái)調用,

IPT_MATCH_ITERATE是在ipt_do_table函數中調用的宏

IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, &hotdrop)

 

定義如下:

#define IPT_MATCH_ITERATE(e, fn, args...)      \

({                               \

      unsigned int __i;               \

      int __ret = 0;                    \

      struct ipt_entry_match *__match;  \

                                  \

      for (__i = sizeof(struct ipt_entry);  \

           __i < (e)->target_offset;        \

           __i += __match->u.match_size) {      \

           __match = (void *)(e) + __i;    \

                                  \

           __ret = fn(__match , ## args);     \

           if (__ret != 0)              \

                 break;             \

      }                          \

      __ret;                         \

})

 

下面就是do_match函數:

static inline

int do_match(struct ipt_entry_match *m,

             const struct sk_buff *skb,

             const struct net_device *in,

             const struct net_device *out,

             int offset,

             const void *hdr,

             u_int16_t datalen,

             int *hotdrop)

{

        /* Stop iteration if it doesn't match */

        if (!m->u.kernel.match->match(skb, in, out, m->data,

                                      offset, hdr, datalen, hotdrop))

                return 1;

        else

                return 0;

}

 

實(shí)際上就是調用了m->u.kernel.match->match,這個(gè)東西應該就是調用后面解釋

這里還出現了一個(gè)ipt_entry_match結構,它用來(lái)把match的內核態(tài)與用戶(hù)態(tài)關(guān)連起來(lái)

 

6.2 ipt_xxx.c文件

 

我們在編譯內核的netfilter選項時(shí),有ah、esp、length……等一大堆的匹配選項,他們既可以是模塊的形式注冊,又可以是直接編譯進(jìn)內核,所以,他們應該是以單獨的文件形式,以:

module_init(init);

module_exit(cleanup);

這樣形式存在的,我們在源碼目錄下邊,可以看到Ipt_ah.c、Ipt_esp.c、Ipt_length.c等許多文件,這些就是我們所要關(guān)心的了,另一方面,基本的TCP/UDP 的端口匹配,ICMP類(lèi)型匹配不在此之列,所以,應該有初始化的地方,

我們注意到Ip_tables.cinit中,有如下語(yǔ)句:

        /* Noone else will be downing sem now, so we won't sleep */

        down(&ipt_mutex);

        list_append(&ipt_target, &ipt_standard_target);

        list_append(&ipt_target, &ipt_error_target);

        list_append(&ipt_match, &tcp_matchstruct);

        list_append(&ipt_match, &udp_matchstruct);

        list_append(&ipt_match, &icmp_matchstruct);

        up(&ipt_mutex);

 

可以看到,這里注冊了standard_target、error_target兩個(gè)targettcp_matchstruct等三個(gè)match。這兩個(gè)地方,就是涉及到match在內核中的注冊了,以Ipt_*.c為例,它們都是以下結構:

#include XXX

 

MODULE_AUTHOR()

MODULE_DESCRIPTION()

MODULE_LICENSE()

 

static int match()       /* ipt_match中的匹配函數 */

{

}

 

static int checkentry()     /* 檢查entry有效性 */

{

}

 

static struct ipt_match XXX_match = { { NULL, NULL }, "XXX", &match,

                &checkentry, NULL, THIS_MODULE };

 

static int __init init(void)

{

    return ipt_register_match(&XXX_match);

}

 

static void __exit fini(void)

{

    ipt_unregister_match(&XXX_match);

}

 

module_init(init);

module_exit(fini);

 

其中,init函數調用ipt_register_match對一個(gè)struct ipt_match結構的XXX_match進(jìn)行注冊,另外,有兩個(gè)函數matchcheckentry。

 

6.3 ipt_match,內核中的match結構    ip_tables.h

struct  ipt_match

{

    struct list_head list;             /* 可見(jiàn)ipt_match也由一個(gè)鏈表來(lái)維護 */

    const char name[IPT_FUNCTION_MAXNAMELEN];  /* match名稱(chēng) */

     

    /* 匹配函數,最重要的部分,返回非0表示匹配成功,如果返回0hotdrop設為1,則表示該報文應當立刻丟棄。 */

    /* Arguments changed since 2.4, as this must now handle

           non-linear skbs, using skb_copy_bits and

           skb_ip_make_writable. */

 

    int (*match)(const struct sk_buff *skb,

                const struct net_device *in,

                const struct net_device *out,

               const void *matchinfo,

                int offset,

                int *hotdrop);

 

    /*在使用本Match的規則注入表中之前調用,進(jìn)行有效性檢查,如果返回0,規則就不會(huì )加入iptables. */

    int (*checkentry)(const char *tablename,

                   const struct ipt_ip *ip,

                   void *matchinfo,

                   unsigned int matchinfosize,

                   unsigned int hook_mask);

 

    /* 刪除包含本matchentry時(shí)調用,與checkentry配合可用于動(dòng)態(tài)內存分配和釋放 */

    void (*destroy)(void *matchinfo, unsigned int matchinfosize);

 

    /* 是否為模塊 */

    struct module *me;

};

 

有了對這個(gè)結構的認識,就可以很容易地理解init函數了。我們也可以猜測,ipt_register_match的作用可能就是建立一個(gè)雙向鏈表的過(guò)程,到時(shí)候要用某個(gè)match的某種功能,調用其成員函數即可。

當然,對于分析filter的實(shí)現,每個(gè)match/target的匹配函數才是我們關(guān)心的重點(diǎn),但是這里為了不中斷分析系統框架,就不再一一分析每個(gè)matchmatch函數

 

6.4 iptables_match,用戶(hù)態(tài)的match結構    ip_tables.h

struct iptables_match

{

    /* Match鏈,初始為NULL */

    struct iptables_match *next;

 

    /* Match名,和核心模塊加載類(lèi)似,作為動(dòng)態(tài)鏈接庫存在的Iptables Extension的命名規則為libipt_'name'.so */

    ipt_chainlabel name;

 

    /*版本信息,一般設為NETFILTER_VERSION */

    const char *version;

 

    /* Match數據的大小,必須用IPT_ALIGN()宏指定對界*/

    size_t size;

 

    /*由于內核可能修改某些域,因此size可能與確切的用戶(hù)數據不同,這時(shí)就應該把不會(huì )被改變的數據放在數據區的前面部分,而這里就應該填寫(xiě)被改變的數據區大??;一般來(lái)說(shuō),這個(gè)值和size相同*/

    size_t userspacesize;

 

    /*iptables要求顯示當前match的信息時(shí)(比如iptables-m ip_ext -h),就會(huì )調用這個(gè)函數,輸出在iptables程序的通用信息之后. */

    void (*help)(void);

 

    /*初始化,在parse之前調用. */

    void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);

 

    /*掃描并接收本match的命令行參數,正確接收時(shí)返回非0,flags用于保存狀態(tài)信息*/

    int (*parse)(int c, char **argv, int invert, unsigned int *flags,

                 const struct ipt_entry *entry,

                 unsigned int *nfcache,

                 struct ipt_entry_match **match);

 

    /* 前面提到過(guò)這個(gè)函數,當命令行參數全部處理完畢以后調用,如果不正確,應該

    退出(exit_error()*/

    void (*final_check)(unsigned int flags);

 

    /*當查詢(xún)當前表中的規則時(shí),顯示使用了當前match的規則*/

    void (*print)(const struct ipt_ip *ip,

                  const struct ipt_entry_match *match, int numeric);

 

    /*按照parse允許的格式將本match的命令行參數輸出到標準輸出,用于iptables-save命令. */

    void (*save)(const struct ipt_ip *ip,

                 const struct ipt_entry_match *match);

 

    /* NULL結尾的參數列表,struct optiongetopt(3)使用的結構相同*/

    const struct option *extra_opts;

 

    /* Ignore these men behind the curtain: */

    unsigned int option_offset;

    struct ipt_entry_match *m;

    unsigned int mflags;

    unsigned int used;

#ifdef NO_SHARED_LIBS

    unsigned int loaded; /* simulate loading so options are merged properly */

#endif

};

 

6.5 ipt_entry_match結構   ip_tables.h

ipt_entry_match將內核態(tài)與用戶(hù)態(tài)關(guān)聯(lián)起來(lái),按我的理解,內核和用戶(hù)在注冊和維護match時(shí)使用的是各自的match結構ipt_matchiptables_match,但在具體應用到某個(gè)規則時(shí)則需要統一成ipt_entry_match結構。

 

前面說(shuō)過(guò),match區存儲在ipt_entry的末尾,target在最后,結合ipt_entry_match的定義,可以知道一條具體的規則中存儲的數據結構不是:

 

ipt_entry + ipt_match1 + ipt_match2 + ipt_match3 + … + target

 

而是:

 

ipt_entry + ipt_entry_match1 + ipt_entry_match2 + ipt_entry_match3 + … + target

 

struct ipt_entry_match

{

    union {

        struct {

            u_int16_t match_size;

 

            /* 用戶(hù)態(tài) */

            char name[IPT_FUNCTION_MAXNAMELEN];

        } user;

        struct {

            u_int16_t match_size;

 

            /* 內核態(tài) */

            struct ipt_match *match;

        } kernel;

 

        /* 總長(cháng)度 */

        u_int16_t match_size;

    } u;

 

    unsigned char data[0];

};

里面定義了兩個(gè)數據結構,userkernel,很明顯,是分別為iptables_matchipt_match準備的

前面在do_match函數中出現的m->u.kernel.match->match()函數,也就是調用ipt_match里的match函數了,接下來(lái)要關(guān)心的就是如何將ipt_entry_matchipt_match關(guān)聯(lián)起來(lái)。換句話(huà)說(shuō),注冊時(shí)還是ipt_match結構的match是何時(shí)變成ipt_entry_match結構的?

還記得注冊table時(shí)調用的translate_table()函數嗎

 

IPT_ENTRY_ITERATE宏出現三次,分別調用了

 

check_entry_size_and_hooks,check_entry,  cleanup_entry,三個(gè)函數

 

check_entry_size_and_hooks用來(lái)做一些邊界檢查,檢查數據結構的長(cháng)度之類(lèi)的,略過(guò)

 

cleanup_entry,很明顯,釋放空間用的

 

下面看看check_entry

 

6.6  check_entrycheck_match函數   ip_tables.c

顧名思義,對entry結構進(jìn)行檢查

 

check_entry(struct ipt_entry *e, const char *name, unsigned int size,

          unsigned int *i)

{

    struct ipt_entry_target *t;

    struct ipt_target *target;

    int ret;

    unsigned int j;

 

    /* 檢查flaginvflag … */

    if (!ip_checkentry(&e->ip)) {

        duprintf("ip_tables: ip check failed %p %s.\n", e, name);

        return -EINVAL;

    }

 

    /* 先別看后面,這里是重點(diǎn),之前遍歷時(shí)用了IPT_ENTRY_ITERATE宏,這里又出現了用來(lái)遍歷matchIPT_MATCH_ITERATE宏,兩個(gè)很像。

    另外IPT_MATCH_ITERATE宏前面看到過(guò)一次,在調用鉤子函數時(shí)的ipt_do_table()函數里出現過(guò),那里是用來(lái)遍歷match并調用do_match()函數的。怎么樣,思路又回到開(kāi)頭擴展的match那里了吧,那里是調用階段,而這里正好是之前的初始化階段。應該說(shuō)這里才是IPT_MATCH_ITERATEipt_entry_match的第一次出現。

    遍歷該entry里的所有match,并對每一個(gè)match調用檢查函數check_match() */

    j = 0;

   

    ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);

 

    if (ret != 0)

        goto cleanup_matches;

 

    /* 下面是關(guān)于target的部分 */

    t = ipt_get_target(e);

    target = ipt_find_target_lock(t->u.user.name, &ret, &ipt_mutex);

 

    if (!target) {

        duprintf("check_entry: `%s' not found\n", t->u.user.name);

        goto cleanup_matches;

    }

 

    if (!try_module_get(target->me)) {

        up(&ipt_mutex);

        ret = -ENOENT;

        goto cleanup_matches;

    }

 

    t->u.kernel.target = target;

    up(&ipt_mutex);

 

    if (t->u.kernel.target == &ipt_standard_target) {

        if (!standard_check(t, size)) {

             ret = -EINVAL;

             goto cleanup_matches;

        }

    }

    else if (t->u.kernel.target->checkentry

              && !t->u.kernel.target->checkentry(name, e, t->data,

                                        t->u.target_size

                                        - sizeof(*t),

                                        e->comefrom)) {

        module_put(t->u.kernel.target->me);

        duprintf("ip_tables: check failed for `%s'.\n",

              t->u.kernel.target->name);

        ret = -EINVAL;

        goto cleanup_matches;

    }

 

    (*i)++;

    return 0;

 

 cleanup_matches:

    IPT_MATCH_ITERATE(e, cleanup_match, &j);

    return ret;

}

 

再看一下IPT_MATCH_ITERATE宏的定義:

 

#define IPT_MATCH_ITERATE(e, fn, args...)       \

({                                              \

    unsigned int __i;                           \

    int __ret = 0;                          \

    struct ipt_entry_match *__match;        \

                                            \

    for (__i = sizeof(struct ipt_entry);    \

       __i < (e)->target_offset;            \

       __i += __match->u.match_size) {      \

       __match = (void *)(e) + __i;         \

       __ret = fn(__match , ## args);       \

 

       if (__ret != 0)                      \

             break;                         \

    }                                       \

 

    __ret;                                  \

})

 

可以看到,在這個(gè)宏里,ipt_entry_match結構出現了,就是說(shuō),到這里為止,entry結構中的match結構已經(jīng)由ipt_match替換成了ipt_entry_match,當然這只是形式上,因為具體結構還是有區別,所以還要對新的ipt_entry_match做一些初始化,也就是把ipt_match里的實(shí)際內容關(guān)聯(lián)過(guò)來(lái)

 

check_match()match結構進(jìn)行檢查:

 

static inline int

 

check_match(struct ipt_entry_match *m,

          const char *name,

          const struct ipt_ip *ip,

          unsigned int hookmask,

          unsigned int *i)

{

    int ret;

    struct ipt_match *match;

 

    /*根據規則中Match的名稱(chēng),在已注冊好的ipt_match雙向鏈表中查找對應結點(diǎn)

 

    可能有一點(diǎn)疑問(wèn)就是為什么用m->u.user.name作為名字來(lái)查找一個(gè)ipt_match,在定義ipt_entry_match的時(shí)候應該只是把它的指針指向了ipt_match的開(kāi)頭位置,并沒(méi)有對里面的name變量賦值吧。

 

    我猜想是這兩個(gè)結構里第一個(gè)變量分別是一個(gè)list_head結構體和一個(gè)u_int16_t,它們都應該是一個(gè)(還是兩個(gè)?)地址變量,所以占用同樣的空間,那么兩個(gè)作為結構里第二個(gè)參數的字符串name[IPT_FUNCTION_MAXNAMELEN] 就剛好重合了 */

 

    match = find_match_lock(m->u.user.name, &ret, &ipt_mutex);

 

    if (!match) {

        duprintf("check_match: `%s' not found\n", m->u.user.name);

        return ret;

    }

 

    if (!try_module_get(match->me)) {

        up(&ipt_mutex);

        return -ENOENT;

    }

 

    /* 再回到開(kāi)頭的do_match()函數,這下全部聯(lián)系起來(lái)了吧 */

    m->u.kernel.match = match;

    up(&ipt_mutex);

 

    /* 調用match里的checkentry做一些檢查 */

 

    if (m->u.kernel.match->checkentry

        && !m->u.kernel.match->checkentry(name, ip, m->data,

        m->u.match_size - sizeof(*m),

        hookmask)) {

        module_put(m->u.kernel.match->me);

        duprintf("ip_tables: check failed for `%s'.\n",

              m->u.kernel.match->name);

        return -EINVAL;

    }

   

    (*i)++;

    return 0;

}

 

還有一點(diǎn),這里并沒(méi)有講到具體的match的實(shí)現,包括每個(gè)match是如何放進(jìn)entry里,entry又是如何放進(jìn)table里的。也就是說(shuō),分析了半天,實(shí)際上我們的table里的entry部分根本就是空的,不過(guò)也對,內核在初始化netfilter時(shí)只是注冊了3個(gè)表(filter,nat,mangle),而里面的規則本來(lái)就是空的。至于具體的entrymatch是如何加入進(jìn)來(lái)的,就是netfilter在用戶(hù)空間的配置工具iptables的任務(wù)了。

 

七、 target 匹配

7.1 ipt_targetipt_entry_target結構      ip_tables.h

ipt_targetipt_match結構類(lèi)似:

 

struct ipt_target

{

    struct list_head list;

    const char name[IPT_FUNCTION_MAXNAMELEN];

 

    /* 在使用本Match的規則注入表中之前調用,進(jìn)行有效性檢查,如果返回0,規則就不會(huì )加入iptables */

 

    int (*checkentry)(const char *tablename,

                   const struct ipt_entry *e,

                   void *targinfo,

                   unsigned int targinfosize,

                   unsigned int hook_mask);

 

    /* 在包含本Target的規則從表中刪除時(shí)調用,與checkentry配合可用于動(dòng)態(tài)內存分配和釋放 */

    void (*destroy)(void *targinfo, unsigned int targinfosize);

 

    /* target的模塊函數,如果需要繼續處理則返回IPT_CONTINUE-1),否則返回NF_ACCEPT、NF_DROP等值,它的調用者根據它的返回值來(lái)判斷如何處理它處理過(guò)的報文*/

 

    unsigned int (*target)(struct sk_buff **pskb,

                    const struct net_device *in,

                    const struct net_device *out,

                    unsigned int hooknum,

                    const void *targinfo,

                    void *userdata);

 

    /* 表示當前Target是否為模塊(NULL為否) */

    struct module *me;

};

 

ipt_entry_targetipt_entry_match也幾乎一模一樣:

struct ipt_entry_target

{

    union {

        struct {

            u_int16_t target_size;

            char name[IPT_FUNCTION_MAXNAMELEN];

        } user;

 

        struct {

            u_int16_t target_size;

            struct ipt_target *target;

        } kernel;

 

        u_int16_t target_size;

    } u;

 

    unsigned char data[0];

};

 

看上去targetmatch好像沒(méi)有區別,但當然,一個(gè)是條件,一個(gè)是動(dòng)作,接著(zhù)往下看是不是真的一樣

之前有兩個(gè)地方出現了ipt_target,一次是在ipt_do_table()函數里,當匹配到match后開(kāi)始匹配target,另一次是在check_entry()里,檢查完match后開(kāi)始檢查target

先看前一個(gè)

 

7.2  ipt_standard_target結構    ip_tables.h

 

再看一次ipt_do_table這個(gè)函數,前面匹配match的部分略過(guò),從匹配match成功的地方開(kāi)始:

 

ipt_do_table( )

{

    ………   /* 略去 */

    if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {

            struct ipt_entry_target *t;

 

        if (IPT_MATCH_ITERATE(e, do_match,

                                  *pskb, in, out,

                                  offset, &hotdrop) != 0)

            goto no_match;

 

        /* 這里開(kāi)始說(shuō)明匹配match成功了,開(kāi)始匹配target */

        ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);

 

        /* ipt_get_target獲取當前target,t是一個(gè)ipt_entry_target結構,這個(gè)函數就是簡(jiǎn)單的返回e+e->target_offset

        每個(gè)entry只有一個(gè)target,所以不需要像match一樣遍歷,直接指針指過(guò)去了*/

        t = ipt_get_target(e);

 

        IP_NF_ASSERT(t->u.kernel.target);

 

        /* 這里都還是和擴展的match的匹配很像,但是下面一句

        有句注釋?zhuān)?span lang="EN-US">Standard target? 判斷當前target是否標準的target?

        而判斷的條件是u.kernel.target->target,就是ipt_target結構里的target函數是否為空,而下面還出現了ipt_standard_target結構和verdict變量,好吧,先停下,看看ipt_standard_target結構再說(shuō) */

 

        if (!t->u.kernel.target->target) {

            int v;

 

            v = ((struct ipt_standard_target *)t)->verdict;

 

            if (v < 0) {

            ……      /* 略去 */

}

 

ipt_standard_target的定義:

struct ipt_standard_target

{

    struct ipt_entry_target target;

    int verdict;

};

 

也就比ipt_entry_target多了一個(gè)verdict(判斷),請看前面的nf_hook_slow()函數,里面也有verdict變量,用來(lái)保存hook函數的返回值,常見(jiàn)的有這些2008/12/5

#define NF_DROP 0

#define NF_ACCEPT 1

#define NF_STOLEN 2

#define NF_QUEUE 3

#define NF_REPEAT 4

#define RETURN     IPT_RETURN

#define IPT_RETURN     (-NF_MAX_VERDICT - 1)

#define NF_MAX_VERDICT NF_REPEAT

我們知道chain(鏈)是某個(gè)檢查點(diǎn)上檢查的規則的集合。除了默認的chain外,用戶(hù)還可以創(chuàng )建新的chain。在iptables中,同一個(gè)chain里的規則是連續存放的。默認的chain的最后一條規則的targetchainpolicy。用戶(hù)創(chuàng )建的chain的最后一條規則的target的調用返回值是NF_RETURN,遍歷過(guò)程將返回原來(lái)的chain。規則中的target也可以指定跳轉到某個(gè)用戶(hù)創(chuàng )建的chain上,這時(shí)它的targetipt_stardard_target,并且這個(gè)targetverdict值大于0。如果在用戶(hù)創(chuàng )建的chain上沒(méi)有找到匹配的規則,遍歷過(guò)程將返回到原來(lái)chain的下一條規則上。

事實(shí)上,target也是分標準的和擴展的,但前面說(shuō)了,畢竟一個(gè)是條件,一個(gè)是動(dòng)作,target的標準和擴展的關(guān)系和match還是不太一樣的,不能一概而論,而且在標準的target里還可以根據verdict的值再劃分為內建的動(dòng)作或者跳轉到自定義鏈

簡(jiǎn)單的說(shuō),標準target就是內核內建的一些處理動(dòng)作或其延伸

擴展的當然就是完全由用戶(hù)定義的處理動(dòng)作

再看if (!t->u.kernel.target->target) 就明白了,如果target函數是空的,就是標準target,因為它不需要用戶(hù)再提供target函數了,而反之是就是擴展的target,那么再看ipt_do_table()吧,還是只看一部分,否則眼花

if (!t->u.kernel.target->target) {

    /* 如果target為空,是標準target */

    int v;

 

    v = ((struct ipt_standard_target *)t)->verdict;

 

    if (v < 0) {

 

        /*v小于0,動(dòng)作是默認內建的動(dòng)作,也可能是自定義鏈已經(jīng)結束而返回return標志*/

        if (v != IPT_RETURN) {    /*如果不是Return,則是內建的動(dòng)作*/

            verdict = (unsigned)(-v) - 1;

            break;

        }

 

        e = back;

 

        /* eback分別是當前表的當前Hook的規則的起始偏移量和上限偏移量,即entry的頭和尾,e=back */

        back = get_entry(table_base,back->comefrom);

        continue;

    }

    /* v大于等于0,處理用戶(hù)自定義鏈,如果當前鏈后還有規則,而要跳到自定義鏈去執行,那么需要保存一個(gè)back點(diǎn),以指示程序在匹配完自定義鏈后,應當繼續匹配的規則位置,自然地, back點(diǎn)應該為當前規則的下一條規則(如果存在的話(huà))

    至于為什么下一條規則的地址是table_base+v, 就要去看具體的規則是如何添加的了 */

    if (table_base + v!= (void *)e + e->next_offset) {

        /* 如果還有規則 */

        /* Save old back ptr in next entry */

        struct ipt_entry *next= (void *)e + e->next_offset;

 

        next->comefrom= (void *)back - table_base;

        /* set back pointer to next entry */

        back = next;

    }

 

    e = get_entry(table_base, v);

 

    }

    else

    {

        /* 如果是擴展的target,則調用target函數,返回值給verdict */

        verdict = t->u.kernel.target->target(pskb,

                                            in, out,

                                            hook,

                                            t->data,

                                            userdata);

        /*Target函數有可能已經(jīng)改變了stuff,所以這里重新定位指針*/

        ip = (*pskb)->nh.iph;

        datalen = (*pskb)->len - ip->ihl * 4;

        /*如果返回的動(dòng)作是繼續檢查下一條規則,則設置當前規則為下一條規則,繼續循環(huán),否則,就跳出循環(huán),因為在ipt_do_table函數末尾有return verdict;表明,則將target函數決定的返回值返回給調用函數nf_iterate,由它來(lái)根據verdict決定數據包的命運*/

        if (verdict == IPT_CONTINUE)

            e = (void *)e + e->next_offset;

        else

            /* Verdict */

            break;

    }

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
ipt_do_table解析
netfilter: Linux 防火墻在內核中的實(shí)現
netfilter整體架構解析初步
Linux中Netfilter的原理介紹
netfilter中iptables表的實(shí)現
iptables 源碼分析
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久