分类: 嵌入式
2016-06-01 11:32:49
版权声明:本文为博主原创文章,转载请注明出处。
基于linux2.6.21
既然我们都已经将xt_table、rule、match、target的结构体之间的联系都已经分析清楚了,那我们接下来分析表的注册、表中规则的添加、表中规则的删除、表中规则的替换也应该比较容易了。
在上节分析时,我们应该有注意到一个细节,即xt_table_info->entries[]是指向每一个CPU对应于该xt_table的所有规则的首地址,而一个表里面所有的规则是按照连续内存存取的。由于表里的规则是存储在连续内存里,那么将一个新的规则插入到一个规则链中就变得比较麻烦,所以现有的做法基本上是重新申请一块内存,根据应用层传递过来的新的规则分布,对新的内存进行规则的赋值等操作,最后将原有规则内存释放掉。
在分析这个函数之前,我们还需要介绍一个结构体,在我们创建一个xt_table,或者替换掉一个xt_table里的规则时,就会用到这个结构体。
struct ipt_replace
{
/* Which table. */
/*表的名称*/
char name[IPT_TABLE_MAXNAMELEN];
/*该表所支持的hook点*/
unsigned int valid_hooks;
/*规则个数*/
unsigned int num_entries;
/*所有规则的内存大小*/
unsigned int size;
/*每条规则链相对于第一条规则链的偏移量*/
unsigned int hook_entry[NF_IP_NUMHOOKS];
/*这个具体用在什么地方我还没有搞懂,看别人的解释为每一条规则链范围的最大
上限,不过我看初始化时,其与hook_entry在对应规则链上的值是相等的,目前我在代码里发现对underflow的调用,基本上都是将其与hook_entry在对应规则链上的值设置为
相同的,还需要深入分析后确认*/
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 xt_counters __user *counters;
/* The entries (hang off end: not really an array). */
/*
表中的每一个规则的结构为ipt_entry+ipt_entry_match(大于等于0个)+ipt_standard_target而ipt_standard_target由xt_entry_target与verdict 组成
可变长数组,下面内存里存放的就是需要替换到表中的新的规则。
*/
struct ipt_entry entries[0];
};
我们知道内存在初始化时,注册了filter、nat、mangle、raw等xt_table表,注册这些表时,均需要实例化一个ipt_table结构、一个ipt_replace结构,然后调用函数ipt_register_table完成注册,我们现在就开始分析这个函数。该函数的流程图如下:
我们结合数据结构之间的关系,可以大致先猜想一下这个函数的执行流程。
A)需要申请一个xt_table_info结构体,还需要申请一块内存块用于存放所有的规则
B)根据ipt_replace变量,对于创建的xt_table_info及规则内存块进行赋值,创建规则链以及每个规则的match、target的设置
C)将xt_table_info.entries[num_cpu]都执行规则内存块的首地址
D)将该xt_table插入到xt_af[pf].tables链表中。
这个只是大致的步骤,内核在注册时,肯定要进行一系统的检查,以保证添加数据的稳定性。
int ipt_register_table(struct xt_table *table, const struct ipt_replace *repl)
{
int ret;
struct xt_table_info *newinfo;
static struct xt_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
void *loc_cpu_entry;
/*分别申请sizeof(xt_table_info)与repl->size两个内存块。*/
newinfo = xt_alloc_table_info(repl->size);
if (!newinfo)
return -ENOMEM;
/*获取当前cpu所对应的entries指针,
并将repl->entries中的值拷贝到loc_cpu_entry所指向的内存空间中*/
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
memcpy(loc_cpu_entry, repl->entries, repl->size);
/*表注册时,根据repl的值,为xt_table_info *newinfo变量进行赋值操作*/
ret = translate_table(table->name, table->valid_hooks,
newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
repl->underflow);
if (ret != 0) {
xt_free_table_info(newinfo);
return ret;
}
/*注册表,并将指针table->private指向newinfo*/
if (xt_register_table(table, &bootstrap, newinfo) != 0) {
xt_free_table_info(newinfo);
return ret;
}
return 0;
}
这个函数的逻辑还是比较简单的, 主要是调用3个函数来完成上面我们所说的功能,下面一一分析之。
该函数的作用就是申请一个xt_table_info,然后根据传递的size值,为指针数组xt_table_info.entries[num_cpu]中的每一个指针申请size大小的内存空间,用于存放规则链中的所有规则。
功能:申请一个新的xt_table_info结构大小的内存,且根据size的大小,
为xt_table_info->entries[cpu]数组的每一个成员申请size大小的内存
(即为每一个cpu都申请一个size大小的内存)
size:一个xt_table_info在每一个cpu中所对应的的规则数的总内存。
struct xt_table_info *xt_alloc_table_info(unsigned int size)
{
struct xt_table_info *newinfo;
int cpu;
/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
return NULL;
newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL);
if (!newinfo)
return NULL;
newinfo->size = size;
for_each_cpu(cpu) {
if (size <= PAGE_SIZE)
newinfo->entries[cpu] = kmalloc_node(size,
GFP_KERNEL,
cpu_to_node(cpu));
else
newinfo->entries[cpu] = vmalloc_node(size,
cpu_to_node(cpu));
if (newinfo->entries[cpu] == NULL) {
xt_free_table_info(newinfo);
return NULL;
}
}
return newinfo;
}
这个函数还是很简单的,就是申请内存空间
这个函数是ipt_register_table最重要的函数,该函数主要实现以下功能:(ipt_entry *)entry0中的值,对每一个规则进行合法性检查,对规则链进行是否有环路检查;
A)初始化newinfo->hook_entry[]、newinfo->underflow[]
B)调用IPT_ENTRY_ITERATE,遍历从entry0开始的所有ipt_entry,对每一个ipt_entry调用函数check_entry_size_and_hooks,判断其首地址边界对其是否正确、该ipt_entry的大小是否超过了最大值、当前ipt_entry相对于首个ipt_entry首地址的偏移量是否与某一个newinfo->hook_entry[]的值相等。
C)调用mark_source_chains链中的规则是否存在检查环路
D)调用IPT_ENTRY_ITERATE,遍历从entry0开始的所有ipt_entry,对每一个ipt_entry调用函数check_entry,对该规则对应的match与target结构进行设置。
E)若以上3步均执行成功,则将该cpu对应的entriers指向的内存空间的内容,拷贝到其他cpu的entries指针指向的内存空间中。
static int
translate_table(const char *name,
unsigned int valid_hooks,
struct xt_table_info *newinfo,
void *entry0,
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;
/*初始化xt_table_info->hook_entry、xt_table_info->underflow中的偏移量*/
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:
1、检查从entry0到entry0 + size之间每一个ipt_entry变量的大小是否符 合要求,以及每一个ipt_entry的起始地址的对齐
2、判断相邻的ipt_entry的offset值是否正确,在正确的情况下将offset 值设置到相应的newinfo->hook_entry[]、newinfo->underflow[]
*/
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry_size_and_hooks,
newinfo,
entry0,
entry0 + 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_entry[i]未被赋值
则返回出错*/
for (i = 0; i < NF_IP_NUMHOOKS; i++) {
/* Only hooks which are valid */
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;
}
}
/*检查首地址从entry0处开始的所有规则链中,是否存在环路链,
若存在,则返回0;
若不存在,则返回1。*/
if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */
i = 0;
/*
遍历链表中的所有ipt_entry,对每一个ipt_entry,执行以下动作
1、遍历该ipt_entry下的所有match,根据每个match的用户层的match名称,
在kernel的xt[af].match链表中查找名称相同的match变量,将地址赋值 给m->u.kernel.match ,当满足合法性检查时,继续操作;若不满足合法性 检查,则会返回失败,并释放创建的struct xt_table_info newinfo,说明 创建ipt_table失败
2、遍历xt[af].target,查找符合条件的target,并赋值给t->u.kernel.target
*/
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i);
if (ret != 0) {
IPT_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/*在经过了规则链的环路检查,规则的边界检查,match与target的赋值后,
对于每一个cpu所对应的newinfo->entries,将新值进行拷贝*/
/* And one copy for every other CPU */
for_each_cpu(i) {
if (newinfo->entries[i] && newinfo->entries[i] != entry0)
memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
}
上面的函数分别调用了函数check_entry_size_and_hooks、mark_source_chains、check_entry,下面分析这几个函数。
该函数完成以下功能:
A)判断其首地址边界对其是否正确
B)该ipt_entry的大小是否超过了最大值
C)当前ipt_entry相对于首个ipt_entry首地址的偏移量是否与某一个hook_entry[]或者underflows[]的值相等
static inline int
check_entry_size_and_hooks(struct ipt_entry *e,
struct xt_table_info *newinfo,
unsigned char *base,
unsigned char *limit,
const unsigned int *hook_entries,
const unsigned int *underflows,
unsigned int *i)
{
unsigned int h;
/*首先判断ipt_entry e的取值是否正确*/
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;
}
/*
判断ipt_entry->next_offset,是否满足要求。对于一个ipt_entry来说,至少要包含一个
ipt_entry和一个ipt_entry_target。
*/
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;
}
/* Check hooks & underflows */
/*为当前ipt_entry对应的hook_entries[h]进行赋值,这个主要是
用来检查hook_entries里的偏移量是否真的是指向了这个ipt_entry的首地址
如果是指向了就对newinfo->hook_entry[h]进行赋值,否则就不赋值。*/
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 xt_counters) { 0, 0 });
e->comefrom = 0;
(*i)++;
return 0;
}
功能:判断一个规则链中的所有规则有没有出现环路。
在这个函数里,e->comefrom被用来存储是否成环标志,出了这个函数以后
这个值就不是用来存储是否成环的标志了。
原理:在一个规则链中,在从头到尾遍历所有规则时,将每一个ipt_entry->comefrom 的 NF_IP_NUMHOOKS位置1;当遍历到链的最后一个规则后,则从尾到头遍历所有规则,并依次清空ipt_entry->comefrom 的 NF_IP_NUMHOOKS位。这样的话,如果规则链中有环路的话,则第一次从头到尾遍历时,就成环了,无法执行从尾到头遍历清空ipt_entry->comefrom 的 NF_IP_NUMHOOKS位,这样在第二次到达某一个ipt_entry时,判断ipt_entry->comefrom 的 NF_IP_NUMHOOKS时就为1了,此时就知道该规则链出现环路了。
static int
mark_source_chains(struct xt_table_info *newinfo,
unsigned int valid_hooks, void *entry0)
{
unsigned int hook;
/* No recursion; use packet counter to save back ptrs (reset
to 0 as we leave), and comefrom to save source hook bitmask */
for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
unsigned int pos = newinfo->hook_entry[hook];
/*获取当前要遍历的规则链的首条规则地址*/
struct ipt_entry *e
= (struct ipt_entry *)(entry0 + pos);
/*仅遍历该表支持的hook链*/
if (!(valid_hooks & (1 << hook)))
continue;
/* Set initial back pointer. */
/*初始返回地址即设置为首条规则的偏移地址*/
e->counters.pcnt = pos;
/*遍历该规则链的所有规则*/
for (;;) {
struct ipt_standard_target *t
= (void *)ipt_get_target(e);
/*该规则链中存在闭环,返回0*/
if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
printk("iptables: loop hook %u pos %u %08X.\n",
hook, pos, e->comefrom);
return 0;
}
/*进入规则前置位NF_IP_NUMHOOKS*/
e->comefrom
|= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
/* Unconditional return/END. */
/*
我的理解:
首先根据t->target.u.user.name为IPT_STANDARD_TARGET,能确定 这是一个标准target(对于标准的ipt_standard_target,可以根据 ipt_standard_target->verdict的值来确定这是
一个标准的target,还是跳转到用户自定义链的target)
此处verdict 小于0,说明这是一个标准target,而 unconditional(&e->ip),说明没有匹配规则,即为内建链最后一条默 认规则。(对于每一个表的每一个内建链来说,最后一条规则链为默认 链,且为DROP或者ACCEPT规则,而对于用户自定义链来说,最后一 条规则一般为RETURN规则)
*/
if (e->target_offset == sizeof(struct ipt_entry)
&& (strcmp(t->target.u.user.name,
IPT_STANDARD_TARGET) == 0)
&& t->verdict < 0
&& unconditional(&e->ip)) {
unsigned int oldpos, size;
/* Return: backtrack through the last
big jump. */
do {
/*清除NF_IP_NUMHOOKS*/
e->comefrom ^= (1<
#ifdef DEBUG_IP_FIREWALL_USER
if (e->comefrom
& (1 << NF_IP_NUMHOOKS)) {
duprintf("Back unset "
"on hook %u "
"rule %u\n",
hook, pos);
}
#endif
oldpos = pos;
pos = e->counters.pcnt;
e->counters.pcnt = 0;
/* We're at the start. */
/*
此处的判断很巧妙,
1、在for (;;)开始前,我们将初始的规则的counters.pcnt 设置为其自己的偏移
2、然后,在for循环中,就是一直遍历规则链的最后一条规 则,并置位每条规则的NF_IP_NUMHOOKS。
3、在确定为规则链的最后一条规则时,则根据pos指针与 e->counters.pcnt 从最后一条规则链,递减遍历每一条 规则,并清除规则的NF_IP_NUMHOOKS
4、当递减遍历到第一条规则时,根据第二条的 e->counters.pcnt 与起始规则的e->counters.pcnt 相 等的关系,则成功退出do while循环,且完成对每一条 规则是否为循环的判断。
按照我们的分析, 如果一条链中的规则出现了环,则不会是 在最后一条规则里出现了环路,因为最后一条规则都是默认 规则,是没有跳转的。这样的话,上面的判断思路就合理了, 当链中出现环路时,则for (;;)只就不会进入结尾规则的处 理语句内,这样的话,若成环,则在对某个规则进行第二次 访问时,便会进入if (e->comefrom & (1 << NF_IP_NUMHOOKS) 代码段,发现环路,函数返回0;若没有环路,则最后都会进 入结尾规则的处理语句内,清除每条规则的NF_IP_NUMHOOKS,
程序返回1。
*/
if (pos == oldpos)
goto next;
e = (struct ipt_entry *)
(entry0 + pos);
} while (oldpos == pos + e->next_offset);
/* Move along one */
size = e->next_offset;
e = (struct ipt_entry *)
(entry0 + pos + size);
e->counters.pcnt = pos;
pos += size;
} else {
/*对于非规则链尾的规则,主要有两种,分类方法为:
1、跳转到用户自定义链的规则
2、不需要跳转的标准target规则与扩张target规则
*/
int newpos = t->verdict;
if (strcmp(t->target.u.user.name,
IPT_STANDARD_TARGET) == 0
&& newpos >= 0) {
/* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n",
pos, newpos);
} else {
/* ... this is a fallthru */
/*对于不需要跳转到用户自定义链的规则来说,下一条规则的地址
即可根据e->next_offset计算出来*/
newpos = pos + e->next_offset;
}
e = (struct ipt_entry *)
(entry0 + newpos);
e->counters.pcnt = pos;//设置返回地址
pos = newpos;
}
}
next:
duprintf("Finished chain %u\n", hook);
}
return 1;
}
该函数完成的功能如下:
1. 调用ip_checkentry对ipt_entry->ip的flag与invflag的值进行合法性检查
2. 遍历ipt_entry中的所有ipt_entry_match,调用函数check_match, 查找match,并对
m->u.kernel.match 进行赋值,若m->u.kernel.match->checkentry存在,则调用其进行合法性检查
3. 调用ipt_get_target获取该ipt_entry的ipt_entry_target,根据target的name与revision调用xt_find_target查找
符合条件的xt_target,若没有找到则返回失败;若查找到则:
a)对t->u.kernel.target 进行赋值
b) 若是标准target,则调用standard_check检查该标准target进行合法性检查;
若是扩展target,则当t->u.kernel.target->checkentry存在时,则调用t->u.kernel.target->checkentry
进行合法性检查。
*/
static inline int
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;
if (!ip_checkentry(&e->ip)) {
duprintf("ip_tables: ip check failed %p %s.\n", e, name);
return -EINVAL;
}
j = 0;
ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
if (ret != 0)
goto cleanup_matches;
t = ipt_get_target(e);
target = try_then_request_module(xt_find_target(AF_INET,
t->u.user.name,
t->u.user.revision),
"ipt_%s", t->u.user.name);
if (IS_ERR(target) || !target) {
duprintf("check_entry: `%s' not found\n", t->u.user.name);
ret = target ? PTR_ERR(target) : -ENOENT;
goto cleanup_matches;
}
t->u.kernel.target = target;
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;
}
上面的函数调用了函数check_match与standard_check,下面分析下这两个函数。
/*
功能:根据m->u.user.name,在kernel的xt[af].match链表中,查找符合要求的match,
若查找到,则将match的首地址赋给m->u.kernel.match
*/
static inline int
check_match(struct ipt_entry_match *m,
const char *name,
const struct ipt_ip *ip,
unsigned int hookmask,
unsigned int *i)
{
struct ipt_match *match;
match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
m->u.user.revision),
"ipt_%s", m->u.user.name);
if (IS_ERR(match) || !match) {
duprintf("check_match: `%s' not found\n", m->u.user.name);
return match ? PTR_ERR(match) : -ENOENT;
}
m->u.kernel.match = match;
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;
}
/*
功能:合法性检查
1. 首先判断ipt_entry_target->u.target_size 的大小是否等于sizeof(struct ipt_standard_target)
2. 满足1是则对ipt_standard_target.verdict(这个值的作用我们在分析数据结构时已经分析
的很详细了)进行判断。
若verdict大于0,则说明该规则为跳转到一个用户自定义链的规则,此时
则需要判断该verdict的值是否大于max_offset值,若大于说明有错误。
若verdict小于0,则判断其值是否小于 -NF_MAX_VERDICT - 1(因为目前支持的最大跳转target
为NF_MAX_VERDICT,所以此处作此判断),若小于的话,说明用户层传递的target有问题。
返回0(0为检查失败)
*/
static inline int
standard_check(const struct ipt_entry_target *t,
unsigned int max_offset)
{
struct ipt_standard_target *targ = (void *)t;
/* Check standard info. */
if (t->u.target_size
!= IPT_ALIGN(sizeof(struct ipt_standard_target))) {
duprintf("standard_check: target size %u != %u\n",
t->u.target_size,
IPT_ALIGN(sizeof(struct ipt_standard_target)));
return 0;
}
if (targ->verdict >= 0
&& targ->verdict > max_offset - sizeof(struct ipt_entry)) {
duprintf("ipt_standard_check: bad verdict (%i)\n",
targ->verdict);
return 0;
}
if (targ->verdict < -NF_MAX_VERDICT - 1) {
duprintf("ipt_standard_check: bad negative verdict (%i)\n",
targ->verdict);
return 0;
}
return 1;
}
该函数实现xt_table插入到xt[table->af].tables链表,并更新xt_table->private指针
功能: 将一个新的xt_table插入到xt[table->af].tables链表中。
1.获取xt[table->af].mutex信号量
2.调用list_named_find,查找新的xt_table是否已存在xt[table->af].tables链表中,若
存在,则返回存在标志;若不存在,则执行3
3.调用xt_replace_table,替换xt_table中的private
4.更新新的xt_table->private->initial_entries的值
5.将该xt_table插入到xt[table->af].tables链表中
*/
int xt_register_table(struct xt_table *table,
struct xt_table_info *bootstrap,
struct xt_table_info *newinfo)
{
int ret;
struct xt_table_info *private;
ret = down_interruptible(&xt[table->af].mutex);
if (ret != 0)
return ret;
/* Don't autoload: we'd eat our tail... */
if (list_named_find(&xt[table->af].tables, table->name)) {
ret = -EEXIST;
goto unlock;
}
/* Simplifies replace_table code. */
table->private = bootstrap;
if (!xt_replace_table(table, 0, newinfo, &ret))
goto unlock;
private = table->private;
duprintf("table->private->number = %u\n", private->number);
/* save number of initial entries */
private->initial_entries = private->number;
rwlock_init(&table->lock);
list_prepend(&xt[table->af].tables, table);
ret = 0;
unlock:
up(&xt[table->af].mutex);
return ret;
}
当用户层通过iptables -A添加一条规则或者通过iptables -D删除一条规则时,最终都会调用函数do_replace函数实现新的规则的添加或者规则的删除(其实就是替换xt_table->private,即需要创建一个新的xt_table_info和一个新的内存块,用于存放新的所有的规则)。
do_replace的定义如下,主要就是拷贝用户侧传递的ipt_replace值(注册新的xt_table时,也是传递ipt_replace的变量);然后调用函数xt_alloc_table_info申请内存空间;接着调用函数translate_table对新申请的内存空间中的每一个规则进行设置操作;最后调用xt_replace_table替换xt_table->private。
与表的注册函数相比,少了将xt_table插入到xt[table->af].tables链表的步骤,其他的与表注册大致是相同的,所以此处不对do_replace进行详细分析。
static int
do_replace(void __user *user, unsigned int len)
{
int ret;
struct ipt_replace tmp;
struct ipt_table *t;
struct xt_table_info *newinfo, *oldinfo;
struct xt_counters *counters;
void *loc_cpu_entry, *loc_cpu_old_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
/* Hack: Causes ipchains to give correct error msg --RR */
if (len != sizeof(tmp) + tmp.size)
return -ENOPROTOOPT;
/* overflow check */
if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
SMP_CACHE_BYTES)
return -ENOMEM;
if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
return -ENOMEM;
newinfo = xt_alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
/* choose the copy that is our node/cpu */
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
tmp.size) != 0) {
ret = -EFAULT;
goto free_newinfo;
}
counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
if (!counters) {
ret = -ENOMEM;
goto free_newinfo;
}
ret = translate_table(tmp.name, tmp.valid_hooks,
newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
goto free_newinfo_counters;
duprintf("ip_tables: Translated table\n");
t = try_then_request_module(xt_find_table_lock(AF_INET, tmp.name),
"iptable_%s", tmp.name);
if (!t || IS_ERR(t)) {
ret = t ? PTR_ERR(t) : -ENOENT;
goto free_newinfo_counters_untrans;
}
/* You lied! */
if (tmp.valid_hooks != t->valid_hooks) {
duprintf("Valid hook crap: %08X vs %08X\n",
tmp.valid_hooks, t->valid_hooks);
ret = -EINVAL;
goto put_module;
}
oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
if (!oldinfo)
goto put_module;
/* Update module usage count based on number of rules */
duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
oldinfo->number, oldinfo->initial_entries, newinfo->number);
if ((oldinfo->number > oldinfo->initial_entries) ||
(newinfo->number <= oldinfo->initial_entries))
module_put(t->me);
if ((oldinfo->number > oldinfo->initial_entries) &&
(newinfo->number <= oldinfo->initial_entries))
module_put(t->me);
/* Get the old counters. */
get_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
xt_free_table_info(oldinfo);
if (copy_to_user(tmp.counters, counters,
sizeof(struct xt_counters) * tmp.num_counters) != 0)
ret = -EFAULT;
vfree(counters);
xt_table_unlock(t);
return ret;
put_module:
module_put(t->me);
xt_table_unlock(t);
free_newinfo_counters_untrans:
IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
free_newinfo_counters:
vfree(counters);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
}
至此,完成了xt_table表的注册的函数的分析和规则添加的简要分析,在我们熟悉xt_table、ipt_entry_match、ip_standard_target、ip_entry_target等数据结构之间的联系的基础上,分析xt_table表的注册与规则添加还是比较顺手的,看来理解数据结构真的很重要啊。