Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103429
  • 博文数量: 18
  • 博客积分: 1425
  • 博客等级: 上尉
  • 技术积分: 236
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-21 20:38
文章分类
文章存档

2011年(6)

2009年(10)

2008年(2)

我的朋友

分类: LINUX

2011-01-05 15:06:34

FILTER表顾名思义,用来对数据包进行做过滤,它是Netfilter中的核心表,提供了Netfilter防火墙的主要功能。

本章将以以ipv4filter表为例,从FILTER表的建立,注册以及hook操作对表的匹配过程对FILTER进行详细的分析。

、初始化与注册

首先是iptable_filter模块初始化时注册一个packet_filter表,及相应的hook操作

static struct xt_table packet_filter = {

       .name             = "filter",//表名

       .valid_hooks   = FILTER_VALID_HOOKS,//有效的hooks#define FILTER_VALID_HOOKS ((1 <<                             

                                      //NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) |

                                      // (1 << NF_IP_LOCAL_OUT))

       .lock              = RW_LOCK_UNLOCKED,  //读写锁

       .me         = THIS_MODULE,           //模块

       .af          = AF_INET,                //所属协议族

};

然后初始化相应的hook操作,由于FILTER表只在三个挂载点注册了函数,故在ipt_ops中我们设置了三个hook操作。

static struct nf_hook_ops ipt_ops[] = {

       {

              .hook             = ipt_hook,//具体的操作

              .owner           = THIS_MODULE,//模块

              .pf          = PF_INET,       //协议族

              .hooknum       = NF_IP_LOCAL_IN,  //hook的标号

              .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,

       },

};

表和hook操作初始化完成后,调用相应的注册函数注册到相应的全局标量当中去,这个注册函数分别为:

ipt_register_tablenf_register_hooks,其中ipt_register_table会调用xt_register_table,完成对表的注册。

表注册完成后是还不够的,这相当于我们有了仓库,还没工具,各种各样的targetmatch就是我们需要的工具,各种各样的targetmatch注册后,一个防火墙的过滤报文功能就可以实现了。

hook操作流程

相应的注册和初始化工作完成后,当有报文进入Netfilter时,我们就可以对报文进行处理了。图3.3中,没有详细的列出hook操作如何对报文进行处理,在这里将对图3.3中的最后一步,以IPV4filter表为例进行扩展。挂载点被触发,调用hook操作,hook操作会根据对应的规则来对报文进行处理。从hook操作开始后的函数调用流程如下:

4.1 FILTER表对数据包处理流程

、重要函数分析

unsigned int

ipt_do_table(struct sk_buff *skb,

            unsigned int hook,

            const struct net_device *in,

            const struct net_device *out,

            struct xt_table *table)

{

       static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));

       u_int16_t offset;

       struct iphdr *ip;

       u_int16_t datalen;

       bool hotdrop = false;

       /* 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 = ip_hdr(skb);//获取ip

       datalen = skb->len - ip->ihl * 4;

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

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

       IP_NF_ASSERT(table->valid_hooks & (1 << hook));//检验hook是否有效

       private = table->private;//获取table的数据区

       table_base = (void *)private->entries[smp_processor_id()];//获取相应cputable的所有规则,                              //match,target

       e = get_entry(table_base, private->hook_entry[hook]);//获取特定hook相对应的所有规则

 

       /* For return from builtin chain */

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

       do {

              IP_NF_ASSERT(e);

              IP_NF_ASSERT(back);

// 标准的匹配,详细分析见下面

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

       //  代码分析

                     struct ipt_entry_target *t;

                     if (IPT_MATCH_ITERATE(e, do_match,//匹配所有的match

                                         skb, in, out,      

                                         offset, &hotdrop) != 0)

                            goto no_match;

 

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

 

                     t = ipt_get_target(e);//获得match对应的target

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

 

#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \

    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)

                     /* The packet is traced: log it */

                     if (unlikely(skb->nf_trace))

                            trace_packet(skb, hook, in, out,

                                        table->name, private, e);

#endif

                     /* Standard target? */

                     if (!t->u.kernel.target->target) {//判断是否是标准的target

                            int v;

 

                     v = ((struct ipt_standard_target *)t)->verdict;//获得相应的target

                            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

                                && !(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,也就是说match定义了自己的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(skb,//获得target标准的target

                                                             in, out,

                                                             hook,

                                                             t->u.kernel.target,

                                                             t->data);

 

#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 = ip_hdr(skb);

                            datalen = skb->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

}

/*

*标准的包匹配函数

*/

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;

 

#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))//boolinvflg不同时为真//两个!为了使后面的表达式只能为1或0

       //判断源地址和目的地址是否相等

  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;

       }

//接口是否匹配

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

}

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

chinaunix网友2011-01-06 14:55:11

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com