您的位置:首页 > 运维架构 > Linux

Linux netfilter 学习笔记 之四 ip层netfilter的table注册及规则的添加

2015-10-25 15:50 816 查看
分类:linux 网络 2014-06-22 19:021032人阅读 评论(0)收藏举报目录(?)[+]ipt_register_table1 xt_alloc_table_info2 translate_table21 check_entry_size_and_hooks22 mark_source_chains23 check_entry231 check_match232 standard_check3 xt_register_tabledo_replace
基于linux2.6.21
既然我们都已经将xt_table、rule、match、target的结构体之间的联系都已经分析清楚了,那我们接下来分析表的注册、表中规则的添加、表中规则的删除、表中规则的替换也应该比较容易了。
在上节分析时,我们应该有注意到一个细节,即xt_table_info->entries[]是指向每一个CPU对应于该xt_table的所有规则的首地址,而一个表里面所有的规则是按照连续内存存取的。由于表里的规则是存储在连续内存里,那么将一个新的规则插入到一个规则链中就变得比较麻烦,所以现有的做法基本上是重新申请一块内存,根据应用层传递过来的新的规则分布,对新的内存进行规则的赋值等操作,最后将原有规则内存释放掉。

1 ipt_register_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个函数来完成上面我们所说的功能,下面一一分析之。

1.1 xt_alloc_table_info

该函数的作用就是申请一个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));elsenewinfo->entries[cpu] = vmalloc_node(size,cpu_to_node(cpu));if (newinfo->entries[cpu] == NULL) {xt_free_table_info(newinfo);return NULL;}}return newinfo;}这个函数还是很简单的,就是申请内存空间

1.2 translate_table

这个函数是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 inttranslate_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,下面分析这几个函数。

1.2.1 check_entry_size_and_hooks

该函数完成以下功能:A)判断其首地址边界对其是否正确B)该ipt_entry的大小是否超过了最大值C)当前ipt_entry相对于首个ipt_entry首地址的偏移量是否与某一个hook_entry[]或者underflows[]的值相等static inline intcheck_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;}

1.2.2 mark_source_chains

功能:判断一个规则链中的所有规则有没有出现环路。在这个函数里,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 intmark_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 (resetto 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 lastbig jump. */do {/*清除NF_IP_NUMHOOKS*/e->comefrom ^= (1<<NF_IP_NUMHOOKS);#ifdef DEBUG_IP_FIREWALL_USERif (e->comefrom& (1 << NF_IP_NUMHOOKS)) {duprintf("Back unset ""on hook %u ""rule %u\n",hook, pos);}#endifoldpos = 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_NUMHOOKS4、当递减遍历到第一条规则时,根据第二条的 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.2.3 check_entry

该函数完成的功能如下: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 intcheck_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,下面分析下这两个函数。

1.2.3.1 check_match

/*功能:根据m->u.user.name,在kernel的xt[af].match链表中,查找符合要求的match,若查找到,则将match的首地址赋给m->u.kernel.match*/static inline intcheck_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.2.3.2 standard_check

/*功能:合法性检查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 intstandard_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;}

1.3 xt_register_table

该函数实现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链表中,若存在,则返回存在标志;若不存在,则执行33.调用xt_replace_table,替换xt_table中的private4.更新新的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;}

2 do_replace

当用户层通过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 intdo_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表的注册与规则添加还是比较顺手的,看来理解数据结构真的很重要啊。版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: