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


  分类: linux 网络 1032人阅读 评论(0) 收藏 举报    
基于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));

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;

}

这个函数还是很简单的,就是申请内存空间

 

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

 

1.2.1 check_entry_size_and_hooks

该函数完成以下功能:

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;

}

 

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 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<<NF_IP_NUMHOOKS);

#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.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 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,下面分析下这两个函数。

1.2.3.1 check_match

/*

功能:根据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.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 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;

}

 


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链表中,若

   存在,则返回存在标志;若不存在,则执行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;

}

 

 

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 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表的注册与规则添加还是比较顺手的,看来理解数据结构真的很重要啊。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值