Chinaunix首页 | 论坛 | 博客
  • 博客访问: 483094
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 508
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-10 13:18
个人简介

Keep looking Donot settle

文章分类

全部博文(144)

文章存档

2019年(1)

2016年(31)

2015年(51)

2014年(61)

分类: 嵌入式

2014-12-22 10:00:41

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

1. 概述


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


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



  •  


    • NF_IP_LOCAL_IN



    • NF_IP_FORWARD



    • NF_IP_LOCAL_OUT


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


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


2. filter表的定义和初始化


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


static struct ipt_table
packet_filter


= {
{ NULL, NULL }, "filter", &initial_table.repl,


FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED,
NULL, THIS_MODULE };


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




  • 链表初始化为空



  • 表名为filter



  • 初始化的模板为&initial_table.repl




    • 初始化的模板表定义于net/ipv4/netfilter/iptable_filter.cLine30,是一个很简单的数据结构,只是赋值有些复杂,因为要对所涉及的各个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, { } },


{


 


{ {
{ { 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 } },


 


{ {
{ { 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 } },


 


{ {
{ { 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 } }


},


 


{ {
{ { 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.cLine9


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


我们可以看到,实际上就是INFORWARDOUT




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



  • 实际数据区ipt_table_info为空



  • 定义为模块


3. filter表的实现


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


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


static struct nf_hook_ops
ipt_ops[]


= {
{ { NULL, NULL }, ipt_hook, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_FILTER
},


{ {
NULL, NULL }, ipt_hook, PF_INET, NF_IP_FORWARD, NF_IP_PRI_FILTER },


{ {
NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,


NF_IP_PRI_FILTER }


};


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


   
其中,对应INFORWARD的处理函数均为ipt_hookOUT的处理函数则为ipt_local_out_hook,下面依次分析之:




  • ipt_hook,定义于net/ipv4/netfilter/iptable_filter.cLine89


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


}


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


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




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



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



  3. 如果数据报匹配,则下面开始继续匹配matchestarget,首先利用宏IPT_MATCH_ITERATE调用do_match()函数(下面单独分析)对扩展的match进行匹配,若不匹配则跳到下一条规则;



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


 


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


if
(!m->u.kernel.match->match(skb, in, out, m->data, offset, hdr, datalen,
hotdrop))


return 1;


else


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.cLine640),在这个函数中,有一个对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.hLine94;另一个是`struct ipt_target`的一个实例,位于net/ipv4/netfilter/IPtables.cLine1684,而在target的匹配过程中,它是这样处理的(ipt_do_tables()net/ipv4/netfilter/ip_tables.cLine329):


 


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


   
从这里看来,它应该是当 t->u.kernel.targettarget函数为空时,表明其为标准的target。那么结合上述两者的定义,似乎用的是后者,因为后者的定义及初始化如下:


 


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.cLine99其功能与ipt_hook()相似,只不过为了防止DOS攻击而增加了对ratelimit的检查。


   
这样,到这里,filter表的实现已经分析完毕,至于具体的过滤功能如何实现,那就是每个HOOK处理函数的问题了。
from: http://blog.sina.com.cn/s/blog_a31ff26901013n0d.html

 

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

上一篇:netfilter分析-3

下一篇:netfilter分析-1

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