Chinaunix首页 | 论坛 | 博客
  • 博客访问: 107731
  • 博文数量: 41
  • 博客积分: 2520
  • 博客等级: 少校
  • 技术积分: 440
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-22 16:25
文章分类

全部博文(41)

文章存档

2010年(41)

我的朋友

分类: LINUX

2010-01-22 16:34:19

本章介绍了表的过滤操作:

1.         从挂载点函数如何找到表包含的规则;

2.         如何遍历规则;

3.         如何对规则的基本match和扩展match进行匹配与否的判定,涉及到.match函数指针;

4.         以及匹配后,如何调用target进行包的处理,涉及到.target函数指针;

 

/* Returns one of the generic firewall policies, like NF_ACCEPT. */

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;

              struct xt_table_info *private;

 

              /* Initialization */

              ip = (*pskb)->nh.iph;                                                                    // IP头指针

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

              indev = in ? in->name : nulldevname;                  // net_device指针

              outdev = out ? out->name : nulldevname;

              /* We handle fragments by dealing with the first fragment as

               * if it was a normal packet.  All other fragments are treated

               * normally, except that they will NEVER match rules that ask

               * things we don't know, ie. tcp syn flag or ports).  If the

               * rule is also a fragment-specific rule, non-fragments won't

               * match it. */

              offset = ntohs(ip->frag_off) & IP_OFFSET;

 

              read_lock_bh(&table->lock);                                           // 对此table加锁

              IP_NF_ASSERT(table->valid_hooks & (1 << hook));                      // 调式语句,查看此hook点对合法否

              private = table->private;                                                                                         // 取得table的实际数据区xt_table_info

              table_base = (void *)private->entries[smp_processor_id()];              // CPU的规则基趾

              e = get_entry(table_base, private->hook_entry[hook]);              // 根据本HOOK规则的偏移量找到本链规则基趾

 

              /* For return from builtin chain */

              back = get_entry(table_base, private->underflow[hook]);//对这个underflow不是很理解

 

              do {

                            IP_NF_ASSERT(e);

                            IP_NF_ASSERT(back);

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

参数:

ip是本次要匹配的skb里面的包的ip头;

e是每个ipt_entry(每条规则的最初部分),所以&e->ip就是ipt_ip结构体。基础匹配内容。

offset先不看。

概要:

确定IP数据报是否匹配规则,若不匹配则跳到下一条规则(这个函数的主要工作大致为:依次处理源/目的IP地址、输入输出接口,然后对基本的规则进行匹配)。

返回值:

和扩展的ipt_match里面的match()函数一样,0表示不匹配,1(非0)表示匹配。

                                          struct ipt_entry_target *t;

 

                                          if (IPT_MATCH_ITERATE(e, do_match,                                     // 参考下面的详细说明

                                                                            *pskb, in, out,

                                                                            offset, &hotdrop) != 0)                                       // 0表示检查到某条扩展匹配不满足,

                                                        goto no_match;                                                                                        // 继续下条规则的检查

 

                                          ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);       // 更新bype计数和packet计数

 

                                          t = ipt_get_target(e);                                                                                              // return (void *)e + e->target_offset;

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

                                          /* Standard target? */

                                          if (!t->u.kernel.target->target) {                                                                 // ipt_standard_target没有设置.target

                                                        int v;

 

                                                        v = ((struct ipt_standard_target *)t)->verdict;     // 标准target或者jump

                                                        if (v < 0) {

说明是内建规则:

iptables:

TC_APPEND_ENTRY()

-> iptcc_map_target():

 

              else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)

                            return iptcc_standard_map(r, -NF_ACCEPT - 1);

                                                                      /* 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

                                                            && !(e->ip.flags & IPT_F_GOTO)) {

                                                                      /* 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()

                                                        /* Targets which reenter must return

                                   abs. verdicts */

#ifdef CONFIG_NETFILTER_DEBUG

                                                        ((struct ipt_entry *)table_base)->comefrom

                                                                      = 0xeeeeeeec;

#endif

                                                        verdict = t->u.kernel.target->target(pskb,            // 调用target()处理

                                                                                                                     in, out,

                                                                                                                     hook,

                                                                                                                     t->u.kernel.target,

                                                                                                                     t->data,

                                                                                                                     userdata);

 

#ifdef CONFIG_NETFILTER_DEBUG

                                                        if (((struct ipt_entry *)table_base)->comefrom

                                                            != 0xeeeeeeec

                                                            && verdict == IPT_CONTINUE) {

                                                                      printk("Target %s reentered!\n",

                                                                             t->u.kernel.target->name);

                                                                      verdict = NF_DROP;

                                                        }

                                                        ((struct ipt_entry *)table_base)->comefrom

                                                                      = 0x57acc001;

#endif

                                                        /* Target might have changed stuff. */

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

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

 

                                                        if (verdict == IPT_CONTINUE)                        // 如果target()函数返回的是IPT_CONTINUE

                                                                      e = (void *)e + e->next_offset;            // 那么继续下一条规则ipt_entry

                                                        else

                                                                      /* Verdict */

                                                                      break;                                                                           // 否则不在处理规则

                                          }

                            } else {

 

                            no_match:                                                                                                               // 如果包连基本规则部分都不匹配,或扩展部分

                                          e = (void *)e + e->next_offset;                                        // 全部不匹配,那么就直接检查下一条规则

                            }

              } while (!hotdrop);                                          // 这个hotdrop直接(最终)是传递到用户自定义的match()函数里面的

                                                                                                  // 过程IPT_MATCH_ITERATE()->do_match()->m->u.kernel.match->match()

                                                                                                  // 因为本来它是0。所以,看来是用户强制终止,设置为NF_DROP

                                                                                                  // 问题是之后ipt_do_table()的返回值,又当作ipt_hook()之类hook函数的

                                                                                                  // 返回值,这个返回值有什么用呢?看hook部分后再给出答案。

答案如下:

#define NF_DROP 0

okfn()将得不到执行。举例如下(ip_rcv_finish不能执行):

              return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,

                                   ip_rcv_finish);

#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)         \

({int __ret;                                                                                                                    \

if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh, 1)) == 1)\

              __ret = (okfn)(skb);                                                                                        \

__ret;})

              read_unlock_bh(&table->lock);

 

#ifdef DEBUG_ALLOW_ALL

              return NF_ACCEPT;

#else

              if (hotdrop)

                            return NF_DROP;

              else return verdict;

#endif

}

 

应用实例:

/* The work comes in here from netfilter.c. */

static unsigned int

ipt_hook(unsigned int hook,

               struct sk_buff **pskb,

               const struct net_device *in,

               const struct net_device *out,

               int (*okfn)(struct sk_buff *))

{

              return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);

}

filter表的NF_IP_LOCAL_INNF_IP_FORWARD点的钩子函数。

static struct nf_hook_ops ipt_ops[] = {

              {

                            .hook                    = ipt_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_LOCAL_IN,

                            .priority   = NF_IP_PRI_FILTER,

              },

              {

                            .hook                    = ipt_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_FORWARD,

                            .priority   = NF_IP_PRI_FILTER,

              },

              {

                            .hook                    = ipt_local_out_hook,

                            .owner                  = THIS_MODULE,

                            .pf                        = PF_INET,

                            .hooknum              = NF_IP_LOCAL_OUT,

                            .priority   = NF_IP_PRI_FILTER,

              },

};


                                          if (IPT_MATCH_ITERATE(e, do_match,

                                                                            *pskb, in, out,

                                                                            offset, &hotdrop) != 0)

                                                        goto no_match;                  // 返回0表示扩展规则全部匹配,所以继续下面的处理

 

 

/* fn returns 0 to continue iteration */

#define IPT_MATCH_ITERATE(e, fn, args...)     \                           // 某一条规则全部扩展匹配进行检查

eipt_entry结构体,参考下面

fn是函数do_match()。这个函数的返回值和具体进行匹配检查的函数的返回值是相反的。参考下面。

如果传进来的fn函数,对某个ipt_entry_match返回一个非0值,那么IPT_MATCH_ITERATE就直接以此值返回。所以可以说,IPT_MATCH_ITERATE返回的就是它的函数指针参数fn的返回值。

({                                                                                 \

              unsigned int __i;                                 \

              int __ret = 0;                                                    \

              struct ipt_entry_match *__match;        \

                                                                                    \

              for (__i = sizeof(struct ipt_entry);        \                                                       // 初始偏移量为sizeof(struct ipt_entry),第一个

                   __i < (e)->target_offset;                           \                                         // 如果还不到target_offset,说明还有扩展匹配

                   __i += __match->u.match_size) {             \                           // 满足某条扩展匹配后,找到下一条的地址

                            __match = (void *)(e) + __i; \                                                       // 获得当前要检查的ipt_entry_match

                                                                                    \

                            __ret = fn(__match , ## args);             \

                            if (__ret != 0)                                    \                           // 返回0继续别的扩展匹配检查。表明此条扩展匹配满足。

                                          break;                                 \                           // 返回1跳出别的扩展匹配检查。表明此条扩展匹配不满足。

              }                                                                    \                                         // 因为跳出别的也就是说,当前规则中只有有任何一个

              __ret;                                                              \                           // 扩展匹配不满足,就相当于这条规则不满足。

})

 

eipt_entry结构体:

struct ipt_entry

{

              struct ipt_ip ip;

 

              /* Mark with fields that we care about. */

              unsigned int nfcache;

 

              /* Size of ipt_entry + matches */

              u_int16_t target_offset;

              /* Size of ipt_entry + matches + target */

              u_int16_t next_offset;

 

              /* Back pointer */

              unsigned int comefrom;

 

              /* Packet and byte counters. */

              struct xt_counters counters;

 

              /* The matches (if any), then the target. */

              unsigned char elems[0];

};

 

static inline

int do_match(struct ipt_entry_match *m,

                   const struct sk_buff *skb,

                   const struct net_device *in,

                   const struct net_device *out,

                   int offset,

                   int *hotdrop)

对一个扩展匹配,进行匹配与否的检查,并返回检查结果。

返回0表明此条扩展匹配满足。

返回1表明此条扩展匹配不满足。

检查是通过调用此扩展匹配的函数指针m->u.kernel.match->match()来进行的。

两个扩展匹配的例子如下:

[root@RHEL5 yuzg]# iptables-save

# Generated by iptables-save v1.3.5 on Mon Jan 12 23:03:19 2009

*filter

:INPUT ACCEPT [177106:36087403]

:FORWARD ACCEPT [0:0]

:OUTPUT ACCEPT [38811:9792053]

-A INPUT -m addrtype --src-type BROADCAST -j ACCEPT

-A INPUT -s 192.168.0.1 -m addrtype --dst-type BROADCAST -j ACCEPT

-A INPUT -s 192.168.0.1 -p udp -m udp --sport 80 -m addrtype --dst-type BROADCAST -j ACCEPT

-A INPUT -s 192.168.0.1 -p udp -m udp --sport 400:65535 -m addrtype --dst-type BROADCAST -j ACCEPT

-A INPUT -s 192.168.0.1 -p udp -m udp --sport 0:5 -m addrtype --dst-type BROADCAST -j ACCEPT

{

              /* Stop iteration if it doesn't match */

              if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,

                                                              offset, skb->nh.iph->ihl*4, hotdrop))

具体match()函数的第四个参数是指函数所在的ipt_match

5个参数是指当前ipt_entry_match的数据域,也就是和用户空间约定好的数据结构。

具体参考下面的addrtypematch()函数

 

                            return 1;                              // 1表示不匹配。但m->u.kernel.match->match()返回的是0

              else

                            return 0;                              // 0表示匹配。但m->u.kernel.match->match()返回的是1

}

也就是说m->u.kernel.match->match()对匹配的返回值,和do_match()以及IPT_MATCH_ITERATE()是正好相反的。

 

static int match(const struct sk_buff *skb,

                             const struct net_device *in, const struct net_device *out,

                             const struct xt_match *match, const void *matchinfo,

                             int offset, unsigned int protoff, int *hotdrop)     // hotdrop的用法还不理解。

{

              const struct ipt_addrtype_info *info = matchinfo;                            // 这里直接取得了约定好的数据结构。

              const struct iphdr *iph = skb->nh.iph;

              int ret = 1;                                                                                                                             // 默认匹配

 

              if (info->source)

                            ret &= match_type(iph->saddr, info->source)^info->invert_source;

              if (info->dest)

                            ret &= match_type(iph->daddr, info->dest)^info->invert_dest;

             

              return ret;                                                                                                                                            // 0:不匹配;1:匹配

}

 

 

阅读(711) | 评论(0) | 转发(0) |
0

上一篇:Table Register

下一篇:Check of match/target (1)

给主人留下些什么吧!~~