作者:jerry_chg
来源:CSDN
原文:https://blog.csdn.net/lickylin/article/details/33347301
版权声明:本文为博主原创文章,转载请附上博文链接!
/* 注册过滤表 */
struct xt_table *ipt_register_table(struct net *net, struct xt_table *table,
const struct ipt_replace *repl)
{
int ret;
struct xt_table_info *newinfo;
struct xt_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
void *loc_cpu_entry;
struct xt_table *new_table;
/* 为filter表申请存储空间,包括xt_table_info结构和存储规则的entry内存块 */
newinfo = xt_alloc_table_info(repl->size);
if (!newinfo) {
ret = -ENOMEM;
goto out;
}
/* choose the copy on our node/cpu, but dont care about preemption */
/* 将filter表的规则入口地址赋值给loc_cpu_entry */
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
/* 将repl中的所有规则全部拷贝到entries中 */
memcpy(loc_cpu_entry, repl->entries, repl->size);
/* 对规则进行边界检查,并对内存块首地址赋值 */
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)
goto out_free;
/* 注册filter表,将xt_table插入到xt_af[af]->tables中,并更新xt_table->private指针 */
new_table = xt_register_table(net, table, &bootstrap, newinfo);
if (IS_ERR(new_table)) {
ret = PTR_ERR(new_table);
goto out_free;
}
return new_table;
out_free:
xt_free_table_info(newinfo);
out:
return ERR_PTR(ret);
}
/* Checks and translates the user-supplied table segment (held in
newinfo) */
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;
/* Init all hooks to impossible value. */
/* 对hook_entry和underflow赋初始值 */
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
newinfo->hook_entry[i] = 0xFFFFFFFF;
newinfo->underflow[i] = 0xFFFFFFFF;
}
duprintf("translate_table: size %u\n", newinfo->size);
i = 0;
/* Walk through entries, checking offsets. */
/* 遍历所有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;
}
/* Check hooks all assigned */
/* 对于本表支持的规则链,若出现规则链相应的hook_entry[i]未被赋值则返回出错 */
for (i = 0; i < NF_INET_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 */
/* 遍历链表中的所有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
* */
i = 0;
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
find_check_entry, name, size, &i);
if (ret != 0) {
IPT_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/* And one copy for every other CPU */
/* 在经过了规则链的环路检查,规则的边界检查,match与target的赋值后,
* 对于每一个cpu所对应的newinfo->entries,将新值进行拷贝 */
for_each_possible_cpu(i) {
if (newinfo->entries[i] && newinfo->entries[i] != entry0)
memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
}
功能:
判断一个规则链中的所有规则有没有出现环路。
在这个函数里,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_INET_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);
int visited = e->comefrom & (1 << hook);
/* 该规则链中存在闭环,返回0 */
if (e->comefrom & (1 << NF_INET_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_INET_NUMHOOKS));
/* Unconditional return/END. */
if ((e->target_offset == sizeof(struct ipt_entry)
&& (strcmp(t->target.u.user.name, IPT_STANDARD_TARGET) == 0)
&& t->verdict < 0
&& unconditional(&e->ip))
|| visited)
{
unsigned int oldpos, size;
if ((strcmp(t->target.u.user.name, IPT_STANDARD_TARGET) == 0)
&& t->verdict < -NF_MAX_VERDICT - 1)
{
duprintf("mark_source_chains: bad "
"negative verdict (%i)\n",
t->verdict);
return 0;
}
/* Return: backtrack through the last
big jump. */
do {
/* 清除NF_IP_NUMHOOKS */
e->comefrom ^= (1<<NF_INET_NUMHOOKS);
#ifdef DEBUG_IP_FIREWALL_USER
if (e->comefrom & (1 << NF_INET_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相等的关系,则成功退出dowhile循环,
* 且完成对每一条规则是否为循环的判断。
*
* 按照我们的分析,如果一条链中的规则出现了环,则不会是在最后一条规则里出现了环路,
* 因为最后一条规则都是默认规则,是没有跳转的。
* 这样的话,上面的判断思路就合理了,当链中出现环路时,则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)
{
if (newpos > newinfo->size - sizeof(struct ipt_entry)) {
duprintf("mark_source_chains: " "bad verdict (%i)\n", newpos);
return 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;
}
当用户层通过iptables -A添加一条规则或者通过iptables -D删除一条规则时,最终都会调用函数do_release函数实现新的规则的添加或者规则删除(其实就是替换xt_table->private,即需要创建一个新的xt_table_info和一个新的内存块,用于存放新的所有的规则)。
与表的注册相比,少了将xt_table插入到xt[table->af].tables链表的步骤。
static int do_replace(struct net *net, void __user *user, unsigned int len)
{
int ret;
struct ipt_replace tmp;
struct xt_table_info *newinfo;
void *loc_cpu_entry;
/* 拷贝用户侧传递的ipt_replace值 */
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
/* overflow check */
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 on 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;
}
/* 对新申请的内存空间中的每一个规则进行设置操作 */
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;
duprintf("ip_tables: Translated table\n");
/* 替换xt_table->private */
ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
tmp.num_counters, tmp.counters);
if (ret)
goto free_newinfo_untrans;
return 0;
free_newinfo_untrans:
IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
}