zoukankan      html  css  js  c++  java
  • Netfilter学习(三)

    五、数据报过滤模块——filter表

    1、概述

      filter表的功能仅仅是对数据报进行过滤,并不对数据报进行任何的修改。

      filter模块在Netfilter中是基于下列Hook点的:

        * NF_IP_LOCAL_IN

        * NF_IP_FORWARD

        * NF_IP_LOCAL_OUT

      这几个Hook分别对应着filter表中的INPUT、FORWARD、OUTPU三条规则链,对于任何一个数据报都会经过这3个Hook之一。

      filter模块的接口位于文件net/ipv4/netfilter/iptables_filter.c中。

     

    2、filter表的定义和初始化

      filter表是前面所述数据结构的ipt_table的一个实例,它的定义和初始化位于net/ipv4/netfilter/iptable_filter.c,Line84。

     1 static struct ipt_table packet_filter = 
     2 { 
     3     { NULL, NULL }, 
     4     "filter", 
     5     &initial_table.repl,    
     6     FILTER_VALID_HOOKS, 
     7     RW_LOCK_UNLOCKED, 
     8     NULL, 
     9     THIS_MODULE 
    10 };

      对照结构ipt_table的定义,我们可以发现,filter表的初始化数据为:

      1> 链表初始化为空;

      2> 表名位filter;

      3> 初始化的模板为&initial_table.repl:

      初始化化的模板表定义于net/ipv4/netfilter/iptable_filter.c,Line30,是一个很简单的数据结构,只是赋值有些复杂,因为要对所涉及的各个所涉及的Hook进行不同的处理:

    static struct
    {
    struct ipt_replace repl;
    struct ipt_standard entries[3];
    struct ipt_error term;
    } initial_table __initdata
    = { 
      { "filter", FILTER_VALID_HOOKS, 4, sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
      { [NF_IP_LOCAL_IN]
    0,[NF_IP_FORWARD] sizeof(struct ipt_standard),[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },{ [NF_IP_LOCAL_IN] 0,[NF_IP_FORWARD] sizeof(struct ipt_standard), [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },0, NULL, { }
      },   {
    /* LOCAL_IN */   { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },    0,    sizeof(struct ipt_entry),    sizeof(struct ipt_standard),    0,

       { 0, 0 },
       { }
      },   { { { { ipt_ALIGN(
    sizeof(struct ipt_standard_target)), "" } }, { } },-NF_ACCEPT - 1 }
      },
    /* FORWARD */   { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },    0,    sizeof(struct ipt_entry),    sizeof(struct ipt_standard),    0,

       { 0, 0 },
       { }
      },  { { { { ipt_ALIGN(
    sizeof(struct ipt_standard_target)), "" } }, { } },-NF_ACCEPT - 1 }
     },
    /* LOCAL_OUT */ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },   0,   sizeof(struct ipt_entry),   sizeof(struct ipt_standard),   0,

      { 0, 0 },
      { }
    }, { { { { ipt_ALIGN(
    sizeof(struct ipt_standard_target)), "" } }, { } },-NF_ACCEPT - 1 } } }, /* ERROR */ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },  0,
     sizeof(struct ipt_entry),  sizeof(struct ipt_error),  0,

     { 0, 0 },
     { }
    }, { { { { ipt_ALIGN(
    sizeof(struct ipt_error_target)), ipt_ERROR_TARGET } },{ }
    },
    "ERROR" } }
    };

      《说实话,这个赋值有点不懂啊!!》

      我们可以的看到,一个initial_table包含三个成员:

        * struct ipt_replace repl; :是对一个表进行初始化的最主要部分,这个ipt_replace结构在前面已经分析了;

        * struct ipt_standard entries[3]; :是对这个表所监听的各个Hook上对应的初始化信息,实际上就是一个ipt_entry结构加上一个ipt_standard_target结构;

        * struct ipt_error_term; :是这个表出错时对应的信息,实际上就是一个ipt_entry结构、一个ipt_entry_target结构再加上一个errorname。

      当前表所监听的Hook位图为FILTER_VALID_HOOKS,位于net/ipv4/netfilter/iptable_filter.c,Line9:

    1 #define FILTER_VALID_HOOKS ((1 << NF_IP_LOCAL_IN) | (1 <<2 NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))

      我们可以看到,实际上就是IN、FORWARD和OUT。

        * 读写锁RW_LOCK_UNLOCKED,即为打开状态

        * 实际数据区ipt_table_info为空

        * 定义为模块

    3、filter表的实现

      filter表的实现函数实际上就是模块iptable_filter.o的init函数,位于net/ipv4/netfilter/iptable_filter.c,Line128。其主要工作是首先通过ipt_register_table()函数进行表的注册,然后用nf_register_hook()函数注册表所监听的各个Hook。

      其中,对Hook进行注册时,是通过对数据结构nf_hook_ops的一个实例ipt_ops进行操作来实现的,这个实例的定义和初始化位于net/ipv4/netfilter/iptable_filter.c,Line117。

    1 static struct nf_hook_ops ipt_ops[]
    2 = { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_LOCAL_IN,
    3 NF_IP_PRI_FILTER },
    4 { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_FORWARD,
    5 NF_IP_PRI_FILTER },
    6 { { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,
    7 NF_IP_PRI_FILTER }
    8 };

      对应前面所分析nf_hook_ops的各个成员,不难确定这些初始化值的意义。

      其中,对应IN和FORWARD的处理函数均为ipt_hook,OUT的处理函数则为ipt_local_out_hook,下面依次分析之。

      * ipt_hook,定义于net/ipv4/netfilter/iptable_filter.c,Line89。

    1 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 *)) 2 { 3   return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL); 4 }

      实际上,它就是调用了ipt_do_table()函数,也就说,注册时首先注册一个ipt_hook()函数,然后ipt_hook()通过调用ipt_do_table()函数对传入的数据进行真正的处理。下面我们来看看ipt_do_table()这个函数。

      它位于net/ipv4/netfilter/iptables.c,Line254,是一个很长的函数,其主要功能是对数据报进行各种匹配、过滤(包括基本规则、matches以及target),具体些说,其工作大致为:

      1> 初始化各种变量,如IP头、数据区、输入输出设备、段偏移、规则入口等;

      2> 进行规则的匹配,首先调用ip_packet_match()函数(位于net/ipv4/netfilter/ip_tables.c,Line121)确定IP数据报是否匹配规则,若不是匹配则跳到下一条规则(这个函数的主要工作大致为:依次处理源/目的IP地址、输入输出接口,然后对基本的规则进行匹配);

      3> 如果数据报匹配,则下面继续匹配matches和target,首先利用宏IPT_MATCH_ITERATE调用do_match()函数对扩展的match进行匹配,若不匹配则跳到下一条规则;

      4> 扩展match匹配后,首先调用ipt_get_target()获得target的偏移地址,然后对target进行匹配,这个匹配的过程要比match的匹配过程复杂一些,同样在下面单独分析。

      下面首先来分析do_match()函数,它位于net/ipv4/netfilter/ip_tables.c,Line229,它的实现只有一个if语句:

    1 if (!m->u.kernel.match->match(skb, in, out, m->data, offset, hdr, datalen, hotdrop))
    2     return 1;
    3 else
    4     return 0;

      其中的`m->u.kernel.match->match(skb, in, out, m->data, offset, hdr, datalen,hotdrop)`是用来定位match 的。

      因为如果仅仅是根据match的名字遍历链表的话进行查找的话,效率会非常低下。Netfilter源码中采用的方法是在进行match的检测之前,也就是在ipt_register_table()函数中通过translate_table()函数由宏IPT_ENTRY_ITERATE 调用函数check_entry()时,在check_entry()中通过宏IPT_MATCH_ITERATE 调用了check_match()函数(位于net/ipv4/netfilter/ip_tables.c,Line640),在这个函数中,有一个对m->u.kernel.match 的赋值。

      m->u.kernel.match = match;

      这样,每条规则的u.kernel.match就与内核模块中的struct ipt_match链表关联起来了,也就是说,这样一来,根据match的名字,其对应的match函数就与链表中对应的函数关联起来了。于是,上面的那条定位match的语句的意义也就开始明白了:

      利用宏IPT_MATCH_ITERATE 来遍历规则中的所有mach,然后直接调用`m->u.kernel.match->match`来进行对数据报的匹配工作——这样的效率显然要比简单的遍历要高许多。

      然后我们来看下对target的匹配,从数据结构的实现来看,似乎这个过程与match的匹配应该是相似的,但实际上,target存在标准的和非标准的两种,其中标准的target与非标准的target的处理是不一样的。

      在这里我遇到了问题(由于现在还没看源码呢,先把前辈的问题写下来吧),如下所示。

      首先,在Netfilter 的源码中,存在两个ipt_standard_target,其中一个是一个struct,位于include/linux/netfilter_ipv4/ip_tables.h,Line94;另一个是`struct

    ipt_target`的一个实例,位于net/ipv4/netfilter/IPtables.c,Line1684,而在target的匹配过程中,它是这样处理的(ipt_do_tables(),net/ipv4/netfilter/ip_tables.c,
    Line329):
      /* Standard target? */
      if (!t->u.kernel.target->target)

      {……}
      从这里看来,它应该是当t->u.kernel.target 的target 函数为空时,表明其为标准的target。那么结合上述两者的定义,似乎用的是后者,因为后者的定义及初始化如下:
      /* The built-in targets: standard (NULL) and error. */
      static struct ipt_target ipt_standard_target= { { NULL, NULL }, IPT_STANDARD_TARGET, NULL, NULL, NULL };
      但是问题出现在:初始化中的IPT_STANDARD_TARGET 被定义为””!!并且在整个源码中,用到实例化的ipt_standard_target 的地方仅有两处,即上面的这个定义以及ip_tables.c 中将ipt_standard_target 加入到target 链表之中。也就是说这个实例的名字一直为空,这一点如何理解?

      * ipt_local_out_hook,定义于net/ipv4/netfilter/iptable_filter.c,Line99,其功能与ipt_hook相似,只不过为了防止DOS攻击而增加了对ratelimit的检查。

      

      这样,到这里,filter的实现已经分析完毕,至于具体的过滤功能如何实现的,那就是每个Hook处理的函数的问题了。

     

     

      感谢前辈,地址为:http://www.cnblogs.com/iceocean/articles/1594196.html。希望不会搞错哦。。

     

                                                  梦醒潇湘

                                                2012-09-23 19:51:02

  • 相关阅读:
    setTimeout和setInterval的区别(面试题)
    什么是跨域?列出几种JS跨域解决方法?(前端面试题)
    建网站的流程
    CSS Sprite(雪碧图)简单使用
    前端不得不说的性能优化
    面试题
    前端如何做好SEO优化
    JavaScript string字符串对象常见方法
    微信号复制跟跳转——clipboard.js
    微信号复制跟跳转——execCommand()
  • 原文地址:https://www.cnblogs.com/iloveyouforever/p/2699132.html
Copyright © 2011-2022 走看看