五、数据报过滤模块——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