Keep looking Donot settle
分类: 嵌入式
2014-12-22 10:00:41
filter表的功能仅仅是对数据报进行过滤,并不对数据报进行任何的修改。
filter模块在Netfilter中是基于下列HOOK点的:
NF_IP_LOCAL_IN
NF_IP_FORWARD
NF_IP_LOCAL_OUT
这几个HOOK分别对应着filter表中的INPUT、FORWARD、OUTPUT三条规则链,对于任何一个数据报都会经过这3个HOOK之一。
filter模块的接口位于文件net/ipv4/netfilter/iptables_filter.c中。
filter表是前面所述数据结构ipt_table的一个实例,它的定义和初始化位于net/ipv4/netfilter/iptable_filter.c,Line84:
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.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, { } },
{
{ {
{ { 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.c,Line9:
#define FILTER_VALID_HOOKS ((1 <<
NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 <<
NF_IP_LOCAL_OUT))
我们可以看到,实际上就是IN,FORWARD和OUT。
读写锁为RW_LOCK_UNLOCKED,即为打开状态
实际数据区ipt_table_info为空
定义为模块
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:
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的各个成员,不难确定这些初始化值的意义。
其中,对应IN和FORWARD的处理函数均为ipt_hook,OUT的处理函数则为ipt_local_out_hook,下面依次分析之:
ipt_hook,定义于net/ipv4/netfilter/iptable_filter.c,Line89:
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.c,Line254,是一个很长的函数,其主要功能是对数据报进行各种匹配、过滤(包括基本规则、matches以及target),具体些说,其工作大致为:
初始化各种变量,如IP头、数据区、输入输出设备、段偏移、规则入口及偏移量等等;
进行规则的匹配,首先调用ip_packet_match()函数(位于net/ipv4/netfilter/ip_tables.c,Line121)确定IP数据报是否匹配规则,若不匹配则跳到下一条规则(这个函数的主要工作大致为:依次处理源/目的IP地址、输入输出接口,然后对基本的规则进行匹配);
如果数据报匹配,则下面开始继续匹配matches和target,首先利用宏IPT_MATCH_ITERATE调用do_match()函数(下面单独分析)对扩展的match进行匹配,若不匹配则跳到下一条规则;
扩展match匹配后,首先调用ipt_get_target()获得target的偏移地址,然后对target进行匹配,这个匹配的过程要比match的匹配过程复杂一些,同样在下面单独分析。
下面首先来分析do_match()函数,它位于net/ipv4/netfilter/ip_tables.c,Line229,它的实现只有一个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.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):
if
(!t->u.kernel.target->target) {……}
从这里看来,它应该是当 t->u.kernel.target的target函数为空时,表明其为标准的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.c,Line99其功能与ipt_hook()相似,只不过为了防止DOS攻击而增加了对ratelimit的检查。
这样,到这里,filter表的实现已经分析完毕,至于具体的过滤功能如何实现,那就是每个HOOK处理函数的问题了。
from: http://blog.sina.com.cn/s/blog_a31ff26901013n0d.html