Netfilter源码分析(2)

2020-05-27 00:00:00 规则 模块 初始化 结构 链表

二、表的注册
表的注册由函数ipt_register_table来完成,
ipt_register_table(&packet_filter);
其参数packet_filter包含了待注册表的各个参数:
static struct ipt_table packet_filter
= { { NULL, NULL }, "filter", &initial_table.repl,
FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };

可见,内核中,表是以结构struct ipt_table来表示的:
struct ipt_table
{
struct list_head list;
/* 用于构造,维护链表的结构 */
char name[IPT_TABLE_MAXNAMELEN];
/* 表名,如"filter"、"nat"等,为了满足自动模块加载的设计,包含该表的模块应命名为iptable_'name'.o */
struct ipt_replace *table;
/* 表的初始化模板,初始为initial_table.repl */
unsigned int valid_hooks;
/* 位向量,表示当前表所影响的HOOK */
rwlock_t lock;
/* 读写锁,初始为打开状态 */
struct ipt_table_info *private;
/* iptable的数据区*/
struct module *me;
/* 是否在模块中定义,若否,则为NULL */
};

对照这一结构分析,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,即为打开状态
数据区: NULL
是否在模块中定义:THIS_MODULE,见如下宏定义:

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

先来看维护表的链表的结构:
struct list_head {
struct list_head *next, *prev;
};
很简单,它是一个双向链表。

另一个重要的东东就是表的模板和数据区。表模板定义了一个初始化用的该表的所默认的HOOK所包含的规则等信息,它被初始化成了
&initial_table.repl。而初始化的数据区struct ipt_table_info *private为空。这样,ipt_register_table()函数会用repl成员的
内容去填充private成员.

struct ipt_table_info是实际描述表的数据结构(net/ipv4/netfilter/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相对应的规则表上限偏移量,当无规则录入时,相应的hook_entry和underflow均为0 */
char entries[0] ____cacheline_aligned;
/* 规则表入口 */
};

再来看模板的定义,这个结构很简单,不过长了点:

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"
}
}
};

结构长了点,我们先来关心注册表时的初始值:
&initial_table.repl
这是一个struct ipt_replace结构,该结构做为初始化模版被使用,同样用户通过系统调用更换
表时也要用到这个结构。定义如下:

/* 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的规则入口相对于下面的entries变量的偏移量 */
unsigned int hook_entry[NF_IP_NUMHOOKS];

/* 与hook_entry相对应的规则表上限偏移量,当无规则录入时,相应的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];
};

对照结构,可以分析各个成员的初始化值了:
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]={ };


了解了这些结构后,再来看表的注册函数:

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已经存在,释放空间 并且递减模块计数 */
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);

/* 保存初始规则计数器 */
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内核中的内存管理、
信号量设置等等,不过其实注册也就完成两件事:初始化表,将表添加进表的链表。


文章来源CU社区:[原创]Netfilter源码分析-我来抛砖,望能引玉

相关文章