Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819057
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: 网络与安全

2013-03-06 22:09:34

七、 target 匹配

7.1 ipt_targetipt_entry_target结构      ip_tables.h

ipt_targetipt_match结构类似:

struct ipt_target

{

      struct list_head list;

 

      const char name[IPT_FUNCTION_MAXNAMELEN];

 

/* 在使用本Match的规则注入表中之前调用,进行有效性检查,如果返回0,规则就不会加入iptables */

      int (*checkentry)(const char *tablename,

                   const struct ipt_entry *e,

                   void *targinfo,

                   unsigned int targinfosize,

                   unsigned int hook_mask);

 

/* 在包含本Target的规则从表中删除时调用,与checkentry配合可用于动态内存分配和释放 */

      void (*destroy)(void *targinfo, unsigned int targinfosize);

 

/* target的模块函数,如果需要继续处理则返回IPT_CONTINUE-1),否则返回NF_ACCEPTNF_DROP等值,它的调用者根据它的返回值来判断如何处理它处理过的报文*/

      unsigned int (*target)(struct sk_buff **pskb,

                        const struct net_device *in,

                        const struct net_device *out,

                        unsigned int hooknum,

                        const void *targinfo,

                        void *userdata);

 

/* 表示当前Target是否为模块(NULL为否) */

      struct module *me;

};

 

 

 

ipt_entry_targetipt_entry_match也几乎一模一样:

struct ipt_entry_target

{

      union {

           struct {

                 u_int16_t target_size;

                 char name[IPT_FUNCTION_MAXNAMELEN];

           } user;

 

           struct {

                 u_int16_t target_size;

                 struct ipt_target *target;

           } kernel;

 

           u_int16_t target_size;

      } u;

 

      unsigned char data[0];

};

 

看上去targetmatch好像没有区别,但当然,一个是条件,一个是动作,接着往下看是不是真的一样

 

之前有两个地方出现了ipt_target,一次是在ipt_do_table()函数里,当匹配到match后开始匹配target,另一次是在check_entry()里,检查完match后开始检查target

先看前一个

 

7.2  ipt_standard_target结构    ip_tables.h

再看一次ipt_do_table这个函数,前面匹配match的部分略过,从匹配match成功的地方开始:

ipt_do_table( )

{

………   /* 略去 */

           if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {

                 struct ipt_entry_target *t;

 

                 if (IPT_MATCH_ITERATE(e, do_match,

                                  *pskb, in, out,

                                  offset, &hotdrop) != 0)

                      goto no_match;

/* 这里开始说明匹配match成功了,开始匹配target */

 

                 ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);

 

/* ipt_get_target获取当前targett是一个ipt_entry_target结构,这个函数就是简单的返回e+e->target_offset

每个entry只有一个target,所以不需要像match一样遍历,直接指针指过去了*/

                 t = ipt_get_target(e);

                 IP_NF_ASSERT(t->u.kernel.target);

/* 这里都还是和扩展的match的匹配很像,但是下面一句

有句注释:Standard target? 判断当前target是否标准的target

而判断的条件是u.kernel.target->target,就是ipt_target结构里的target函数是否为空,而下面还出现了ipt_standard_target结构和verdict变量,好吧,先停下,看看ipt_standard_target结构再说 */

                 if (!t->u.kernel.target->target) {

                      int v;

 

                      v = ((struct ipt_standard_target *)t)->verdict;

                      if (v < 0) {

      ……      /* 略去 */

}

 

 

ipt_standard_target的定义:

struct ipt_standard_target

{

      struct ipt_entry_target target;

      int verdict;

};

也就比ipt_entry_target多了一个verdict(判断),请看前面的nf_hook_slow()函数,里面也有verdict变量,用来保存hook函数的返回值,常见的有这些

#define NF_DROP 0

#define NF_ACCEPT 1

#define NF_STOLEN 2

#define NF_QUEUE 3

#define NF_REPEAT 4

#define RETURN     IPT_RETURN

#define IPT_RETURN     (-NF_MAX_VERDICT - 1)

#define NF_MAX_VERDICT NF_REPEAT 

我们知道chain(链)是某个检查点上检查的规则的集合。除了默认的chain外,用户还可以创建新的chain。在iptables中,同一个chain里的规则是连续存放的。默认的chain的最后一条规则的targetchainpolicy。用户创建的chain的最后一条规则的target的调用返回值是NF_RETURN,遍历过程将返回原来的chain。规则中的target也可以指定跳转到某个用户创建的chain上,这时它的targetipt_stardard_target,并且这个targetverdict值大于0。如果在用户创建的chain上没有找到匹配的规则,遍历过程将返回到原来chain的下一条规则上。

 

事实上,target也是分标准的和扩展的,但前面说了,毕竟一个是条件,一个是动作,target的标准和扩展的关系和match还是不太一样的,不能一概而论,而且在标准的target里还可以根据verdict的值再划分为内建的动作或者跳转到自定义链

简单的说,标准target就是内核内建的一些处理动作或其延伸

扩展的当然就是完全由用户定义的处理动作

再看if (!t->u.kernel.target->target) 就明白了,如果target函数是空的,就是标准target,因为它不需要用户再提供target函数了,而反之是就是扩展的target,那么再看ipt_do_table()吧,还是只看一部分,否则眼花。

 

if (!t->u.kernel.target->target) {

           /* 如果target为空,是标准target */

                 int v;

                 v = ((struct ipt_standard_target *)t)->verdict;

                 if (v < 0) {

/*v小于0,动作是默认内建的动作,也可能是自定义链已经结束而返回return标志*/

                      if (v != IPT_RETURN) {    /*如果不是Return,则是内建的动作*/

                            verdict = (unsigned)(-v) - 1;

                            break;

                      }

                      e = back; 

/* eback分别是当前表的当前Hook的规则的起始偏移量和上限偏移量,即entry的头和尾,e=back */

 

                      back = get_entry(table_base,back->comefrom);

                      continue;

                 }

/* v大于等于0,处理用户自定义链,如果当前链后还有规则,而要跳到自定义链去执行,那么需要保存一个back点,以指示程序在匹配完自定义链后,应当继续匹配的规则位置,自然地, back点应该为当前规则的下一条规则(如果存在的话)

至于为什么下一条规则的地址是table_base+v, 就要去看具体的规则是如何添加的了 */

                 if (table_base + v!= (void *)e + e->next_offset) {

           /* 如果还有规则 */

                            /* Save old back ptr in next entry */

                            struct ipt_entry *next= (void *)e + e->next_offset;

                            next->comefrom= (void *)back - table_base;

                            /* set back pointer to next entry */

                            back = next;

                      }

 

                      e = get_entry(table_base, v);

                 } else {

           /* 如果是扩展的target,则调用target函数,返回值给verdict */

                      verdict = t->u.kernel.target->target(pskb,

                                                  in, out,

                                                  hook,

                                                  t->data,

                                                  userdata);

 

           /*Target函数有可能已经改变了stuff,所以这里重新定位指针*/

                      ip = (*pskb)->nh.iph;

                      datalen = (*pskb)->len - ip->ihl * 4;

        /*如果返回的动作是继续检查下一条规则,则设置当前规则为下一条规则,继续循环,否则,就跳出循环,因为在ipt_do_table函数末尾有return verdict;表明,则将target函数决定的返回值返回给调用函数nf_iterate,由它来根据verdict决定数据包的命运*/

                      if (verdict == IPT_CONTINUE)

                            e = (void *)e + e->next_offset;

                      else

                            /* Verdict */

                            break;

                 }


阅读(306) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~