您的位置:首页 > 其它

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

2013-01-29 14:29 423 查看
转贴自: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_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已经存在,释放空间 并且递减模块计数 */

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

/* 遍历所有规则,检查所有偏量,检查的工作都是由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_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中不存在规则环*/

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_hooks,check_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,即{
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来构建链表了。


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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: