netfilter源码分析(3)-ipt_table表的注册

来源:互联网 发布:软件系统升级改造方案 编辑:程序博客网 时间:2024/04/30 13:16
转贴自:http://alexanderlaw.blog.hexun.com/8968782_d.html

三、ipt_table表的注册

 

init()函数初始化时调用了ipt_register_table函数进行表的注册

 

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;  

 

/*为每个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;

       }

 

/*将规则项拷贝到新表项的第一个cpu空间里面*/

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

 

/*translate_table函数将newinfo表示的table的各个规则进行边界检查,然后对于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已经存在,释放空间 并且递减模块计数 */

       /* 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;

/*将表添加进链表*/

       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:
表名称;
*   valid_hooks
:当前表所影响的hook
*   newinfo
:包含当前表的所有信息的结构
*  size
:表的大小
*  number
:表中的规则数
*   hook_entries
:记录所影响的HOOK的规则入口相对于下面的entries变量的偏移量
*   underflows
:与hook_entry相对应的规则表上限偏移量
作用:
*   translate_table
函数将newinfo表示的table的各个规则进行边界检查,然后对于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这个宏来完成,并且它的最后一个参数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;

        /*实际计算得到的规则数与指定的不符*/
        if (i != number) {
                duprintf("translate_table: %u not %u entries\n",
                         i, number);
                return -EINVAL;
        }

        /* 因为函数一开始将HOOK的偏移地址全部初始成了不可能的值,而在上一个宏的遍历中设置了hook_entriesunderflows的值,这里对它们进行检查 */
        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中不存在规则环*/
        if (!mark_source_chains(newinfo, valid_hooks))
                return -ELOOP;

        /* tables中的规则项进行完整性检查,保证每一个规则项在形式上是合法的*/
        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复制一个完整的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

用来遍历每一个规则,然后调用其第三个参数(函数指针)进行处理,前两个参数分别表示规则的起始位置和规则总大小,后面的参数则视情况而定。

#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时分别调用

check_entry_size_and_hookscheck_entry,  cleanup_entry,三个函数

check_entry有大用处,后面解释

 

 

3.4   list_named_find()函数   listhelp.h
在注册函数中,调用
        list_named_find(&ipt_tables, table->name) 
来检查当前表是否已被注册过了。可见,第一个参数为链表首部,第二个参数为当前表名。
其原型如下:
#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;                                        \
})

前面提过,表是一个双向链表,在宏当中,以while进行循环,以__i = __i->next;
进行遍历,然后调用比较函数进行比较,传递过来的比较函数是__list_cmp_name

比较函数很简单:
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;表示实际数据区。但是在初始化赋值的时候,被设为NULL,而表的初始变量都以模版的形式,放在struct ipt_replace *table;中。

注册函数一开始,就声明了:struct ipt_table_info *newinfo;
然后对其分配了空间,将模块中的初值拷贝了进来。所以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,即{ 000{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来构建链表了。


static inline void
list_prepend(struct list_head *head, void *new)
{
        ASSERT_WRITE_LOCK(head);                /*设置写互斥*/
        list_add(new, head);                        /*将当前表节点添加进链表*/
}


list_add就是一个构建双向链表的过程:
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;
}