netfilter源码分析(5)- ipt_do_table()函数,数据包的过滤

五、 ipt_do_table()函数,数据包的过滤

 

5.1          ipt_entry 相关结构  ip_tables.h

ipt_entry结构前面有过了,再看一遍

struct ipt_entry
{
struct ipt_ip ip;
/* 所要匹配的报文的IP头信息 */
unsigned int nfcache;
/* 位向量,标示本规则关心报文的什么部分,暂未使用 */
u_int16_t target_offset;
/* target区的偏移,通常target区位于match区之后,而match区则在ipt_entry的末尾;
初始化为sizeof(struct ipt_entry),即假定没有match */
u_int16_t next_offset;
/* 下一条规则相对于本规则的偏移,也即本规则所用空间的总和,
初始化为sizeof(struct ipt_entry)+sizeof(struct ipt_target),即没有match */
unsigned int comefrom;
/* 位向量,标记调用本规则的HOOK号,可用于检查规则的有效性 */
struct ipt_counters counters;
/* 记录该规则处理过的报文数和报文总字节数 */
unsigned char elems[0];
/*target或者是match的起始位置 */
}

 

ipt_ip结构  ip_tables.h

struct ipt_ip {

      struct in_addr src, dst;         /* 来源/目的地址 */

      struct in_addr smsk, dmsk;     /* 来源/目的地址的掩码 */

 

      char iniface[IFNAMSIZ], outiface[IFNAMSIZ];    /*输入输出网络接口*/

      unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];

 

      u_int16_t proto;    /* 协议, 0 = ANY */

     

      u_int8_t flags;      /* 标志字段 */

      u_int8_t invflags;    /* 取反标志 */

};

 

 

5.2  ipt_do_table函数   ip_tables.c

 

unsigned int

ipt_do_table(struct sk_buff **pskb,

           unsigned int hook,

           const struct net_device *in,

           const struct net_device *out,

           struct ipt_table *table,

           void *userdata)

{

static const char nulldevname[IFNAMSIZ]     \

                 __attribute__((aligned(sizeof(long))));

      u_int16_t offset;

      struct iphdr *ip;

      u_int16_t datalen;

      int hotdrop = 0;

      /* Initializing verdict to NF_DROP keeps gcc happy. */

      unsigned int verdict = NF_DROP;

      const char *indev, *outdev;

      void *table_base;

      struct ipt_entry *e, *back;

 

      /* Initialization */

      ip = (*pskb)->nh.iph;                /* 获取IP */

      datalen = (*pskb)->len - ip->ihl * 4;   /*指向数据区*/

      indev = in ? in->name : nulldevname;     /*取得输入设备名*/

      outdev = out ? out->name : nulldevname;    /*取得输出设备名*/

      offset = ntohs(ip->frag_off) & IP_OFFSET;      /*设置分片包的偏移*/

 

      read_lock_bh(&table->lock);     /*设置互斥锁*/

      IP_NF_ASSERT(table->valid_hooks & (1 << hook));

/*检验HOOKdebug用的*/

/*获取当前表的当前CPU的规则入口*/

      table_base = (void *)table->private->entries

           + TABLE_OFFSET(table->private, smp_processor_id());

/*获得当前表的当前Hook的规则的起始偏移量*/ 

      e = get_entry(table_base, table->private->hook_entry[hook]);

 

/*获得当前表的当前Hook的规则的上限偏移量*/

      /* For return from builtin chain */

      back = get_entry(table_base, table->private->underflow[hook]);

/*  do ……  while(!hotdrop

      进行规则的匹配   */

      do {

           IP_NF_ASSERT(e);

           IP_NF_ASSERT(back);

           (*pskb)->nfcache |= e->nfcache;

 

/*

   匹配IP包,成功则继续匹配下去,否则跳到下一个规则  

   ip_packet_match匹配标准match, 也就是ip报文中的一些基本的元素,如来源/目的地址,进/出网口,协议等,因为要匹配的内容是固定的,所以具体的函数实现也是固定的。

   IPT_MATCH_ITERATE (应该猜到实际是调用第二个参数do_match函数)匹配扩展的match,如字符串匹配,p2p匹配等,因为要匹配的内容不确定,所以函数的实现也是不一样的,所以do_match的实现就和具体的match模块有关了。 

   这里的&e->ip就是上面的ipt_ip结构

*/

           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;    /*不匹配则跳到 no_match,往下一个规则*/

 

        /* 匹配则继续执行 */

      /* 这个宏用来分别处理字节计数器和分组计数器这两个计数器 */

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

 

      /*获取规则的target的偏移地址*/

                 t = ipt_get_target(e);

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

 

      /* 下面开始匹备target */

                 /* Standard target? */

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

                      int v;

 

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

                      if (v < 0) {

                            /* Pop from stack? */

                            if (v != IPT_RETURN) {

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

                                  break;

                            }

                            e = back;

                            back = get_entry(table_base,

                                        back->comefrom);

                            continue;

                      }

                      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 {

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

                                                  in, out,

                                                  hook,

                                                  t->data,

                                                  userdata);

 

                      /* Target might have changed stuff. */

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

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

 

                      if (verdict == IPT_CONTINUE)

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

                      else

                            /* Verdict */

                            break;

                 }

           } else {

 

           no_match:

                 e = (void *)e + e->next_offset;  /* 匹配失败,跳到下一个规则 */

           }

      } while (!hotdrop);

 

      read_unlock_bh(&table->lock);

 

#ifdef DEBUG_ALLOW_ALL

      return NF_ACCEPT;

#else

      if (hotdrop)

            return NF_DROP;

      else return verdict;

#endif

}

 

 

5.3   标准的match   ip_packet_match函数  ip_tables.c

 

static inline int

ip_packet_match(const struct iphdr *ip,

           const char *indev,

           const char *outdev,

           const struct ipt_ip *ipinfo,

           int isfrag)

{

      size_t i;

      unsigned long ret;

 

/*定义一个宏,当boolinvflg的是一真一假的情况时,返回真。注意这里使用两个的目的是使得这样计算后的值域只取01两个值*/

#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))

 

/*处理源和目标ip地址,这个if语句的意义是:到达分组的源ip地址经过掩码处理后与规则中的ip不匹配并且规则中没有包含对ip地址的取反,或者规则中包含了对匹配地址的取反,但到达分组的源ip与规则中的ip地址匹配,if的第一部分返回真,同样道理处理到达分组的目的ip地址。这两部分任意部分为真时,源或者目标地址不匹配。*/

      if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,

             IPT_INV_SRCIP)

          || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,

                IPT_INV_DSTIP)) {

           dprintf("Source or dest mismatch.\n");

 

           dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",

                 NIPQUAD(ip->saddr),

                 NIPQUAD(ipinfo->smsk.s_addr),

                 NIPQUAD(ipinfo->src.s_addr),

                 ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");

           dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",

                 NIPQUAD(ip->daddr),

                 NIPQUAD(ipinfo->dmsk.s_addr),

                 NIPQUAD(ipinfo->dst.s_addr),

                 ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");

           return 0;

      }

 

/*接着处理输入和输出的接口,for语句处理接口是否与规则中的接口匹配,不匹配时,ret返回非零,离开for语句后,处理接口的取反问题:当接口不匹配并且接口不取反,或者接口匹配,但是接口取反,说明接口不匹配。*/

      /* Look for ifname matches; this should unroll nicely. */

 

/*输入接口*/

      for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {

           ret |= (((const unsigned long *)indev)[i]

                 ^ ((const unsigned long *)ipinfo->iniface)[i])

                 & ((const unsigned long *)ipinfo->iniface_mask)[i];

      }

 

      if (FWINV(ret != 0, IPT_INV_VIA_IN)) {

           dprintf("VIA in mismatch (%s vs %s).%s\n",

                 indev, ipinfo->iniface,

                 ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");

           return 0;

      }

 

/*输出接口*/

      for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {

           ret |= (((const unsigned long *)outdev)[i]

                 ^ ((const unsigned long *)ipinfo->outiface)[i])

                 & ((const unsigned long *)ipinfo->outiface_mask)[i];

      }

 

      if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {

           dprintf("VIA out mismatch (%s vs %s).%s\n",

                 outdev, ipinfo->outiface,

                 ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");

           return 0;

      }

 

/* 检查协议是否匹配 */

      /* Check specific protocol */

      if (ipinfo->proto

          && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {

           dprintf("Packet protocol %hi does not match %hi.%s\n",

                 ip->protocol, ipinfo->proto,

                 ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");

           return 0;

      }

 

    /*处理分片包的匹配情况*/

      /* If we have a fragment rule but the packet is not a fragment

       * then we return zero */

      if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {

           dprintf("Fragment rule but not fragment.%s\n",

                 ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");

           return 0;

      }

 

      return 1;       /* 以上所有都匹配则返回1 */

}

 

你可能感兴趣的:(netfilter源码分析(5)- ipt_do_table()函数,数据包的过滤)