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

 

posted @ 2012-09-23 19:51  虔诚的学习者  阅读(507)  评论(0编辑  收藏  举报