基于linux2.6.21
今天主要在前两节的基础上,分析filter表的创建,以及filter表的hook回调函数的分析。
1.Filter模块初始化
在前面分析表的注册时,我们知道要注册一个新的xt_table,需要实例化xt_table与ipt_replace这两个结构体。创建filter表时同样需要这样做。下面是filter表实例化的这两个结构体
1.1Filter表的初始化1.1.1xt_tablefilter表初始化
主要是设置创建的xt_table的表名,在哪些hook点起作用等
staticstructipt_tablepacket_filter={
.name ="filter",
.valid_hooks =FILTER_VALID_HOOKS,/*仅支持NF_LOCAL_INNF_LOCAL_OUTNF_FORWARD3个hook点,即该表只包含这个3个内建链*/
.lock =RW_LOCK_UNLOCKED,
.me =THIS_MODULE,
.af =AF_INET,
};
1.1.2ipt_replacerepl
repl的初始化定义如下:
staticstruct
{
structipt_replacerepl;
structipt_standardentries[3];/*创建了3个rule和一个error*/
structipt_errorterm;
}initial_table__initdata
={
{"filter",FILTER_VALID_HOOKS,4,
sizeof(structipt_standard)*3+sizeof(structipt_error),
{[NF_IP_LOCAL_IN]=0,
[NF_IP_FORWARD]=sizeof(structipt_standard),
[NF_IP_LOCAL_OUT]=sizeof(structipt_standard)*2},
{[NF_IP_LOCAL_IN]=0,
[NF_IP_FORWARD]=sizeof(structipt_standard),
[NF_IP_LOCAL_OUT]=sizeof(structipt_standard)*2},
0,NULL,{}},
{
/*LOCAL_IN*/
{{{{0},{0},{0},{0},"","",{0},{0},0,0,0},
0,
sizeof(structipt_entry),
sizeof(structipt_standard),
0,{0,0},{}},
{{{{IPT_ALIGN(sizeof(structipt_standard_target)),""}},{}},
-NF_ACCEPT-1}},
/*FORWARD*/
{{{{0},{0},{0},{0},"","",{0},{0},0,0,0},
0,
sizeof(structipt_entry),
sizeof(structipt_standard),
0,{0,0},{}},
{{{{IPT_ALIGN(sizeof(structipt_standard_target)),""}},{}},
-NF_ACCEPT-1}},
/*LOCAL_OUT*/
{{{{0},{0},{0},{0},"","",{0},{0},0,0,0},
0,
sizeof(structipt_entry),
sizeof(structipt_standard),
0,{0,0},{}},
{{{{IPT_ALIGN(sizeof(structipt_standard_target)),""}},{}},
-NF_ACCEPT-1}}
},
/*ERROR*/
{{{{0},{0},{0},{0},"","",{0},{0},0,0,0},
0,
sizeof(structipt_entry),
sizeof(structipt_error),
0,{0,0},{}},
{{{{IPT_ALIGN(sizeof(structipt_error_target)),IPT_ERROR_TARGET}},
{}},
"ERROR"
}
}
};
在分析这部分代码之前,需要说明一下,structipt_standard即为
ipt_entry+ipt_standard_target
下面我们就结合结构体的定义,分析下ipt_replace的初始化与第一条ipt_standard的初始化。
repl的初始化
repl->name"filter"
repl->vaild_hooks=FILTER_VALID_HOOKS
repl->num_entries=4
repl->size=sizeof(structipt_standard)*3+sizeof(structipt_error)即3个rule和一个error占用的内存大小
repl->hook_entry[]即设置3个rule与一个error对应于第一个rule的偏移量
repl->underflow[]即设置3个rule与一个error对应于第一个rule的偏移量
repl->num_countes=0
repl->counters=NULL
repl->entries={}
entries[3]的初始化中,我们只选取entries[0]进行分析,一个ipt_standard即为
ipt_entry+ipt_standard_target=ipt_entry+ipt_entry_target+verdict。
所以entries[0]的ipt_entry=
{{{0},{0},{0},{0},"","",{0},{0},0,0,0},
0,sizeof(structipt_entry),sizeof(structipt_standard),0,{0,0},{}}
即
ipt_entry.ip={{0},{0},{0},{0},"","",{0},{0},0,0,0}
ipt_entry.nfcache=0
ipt_entry.target_offset=sizeof(structipt_entry)
ipt_entry.next_offset=sizeof(structipt_standard),
而entries[0]的ipt_standard_target等于
{{{{IPT_ALIGN(sizeof(structipt_standard_target)),""}},{}},-NF_ACCEPT-1}
即ipt_entry_target= {{{IPT_ALIGN(sizeof(structipt_standard_target)),""}},{}}。从而可以得出pt_entry_target.u.user.size=IPT_ALIGN(sizeof(structipt_standard_target))
ipt_entry_target.u.user.name=""
veridct=-NF_ACCEPT-1即标准target为ACCEPT。
结合ipt_repleac、ipt_standard、ipt_error结构体的定义,我们知道在filter表初始化时需要创建NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT3条链,且为每条链创建一条target为NF_ACCEPT的默认规则和一条ipt_error规则。
在初始化了以上两个数据结构后,则调用ipt_register_table即可创建一个xt_table,并插入到链表xt_af[AF_INET].tables中
1.2hook函数的注册
注册了一个xt_table后,只是为iptables提供了一个添加过滤规则的表而已,而我们的最终目的是在执行NF_HOOK时,能够对iptables添加的过滤规则进行过滤检查操作,从而决定数据包的去向,那很明显我们需要在相应的hook点中注册hook函数,这样才能对数据包进行过滤检查。
通过以上的信息,我们可以大致进行如下猜想:
a)由上面的表注册,我们知道filter表主要对NF_LOCAL_IN、NF_LOCAL_OUT、NF_FORWAD,那我们也需要在这3个hook点注册相应的hook函数。
b)而我们通过iptables添加的过滤规则又是保存在xt_table->private.entries中,显然要想数据包能够匹配filter表的某条规则,就需要遍历xt_table并对表中的每一条规则都执行match与target操作,即我们编写的hook函数需要满足上述要求,而在第四节的分析中,我们提到函数ipt_do_table也是实现这种功能的,那是不是说我们编写的filter表的hook函数可以调用ipt_do_table呢?
下面就分析filter表的hook函数注册相关的代码,来验证我们的猜想是否正确。
1.2.1nf_hook_ops初始化
staticstructnf_hook_opsipt_ops[]={
{
.hook =ipt_hook,
.owner =THIS_MODULE,
.pf =PF_INET,
.hooknum =NF_IP_LOCAL_IN,
.priority =NF_IP_PRI_FILTER,
},
{
.hook =ipt_hook,
.owner =THIS_MODULE,
.pf =PF_INET,
.hooknum =NF_IP_FORWARD,
.priority =NF_IP_PRI_FILTER,
},
{
.hook =ipt_local_out_hook,
.owner =THIS_MODULE,
.pf =PF_INET,
.hooknum =NF_IP_LOCAL_OUT,
.priority =NF_IP_PRI_FILTER,
},
};
在iptables_filter.c中,我们可以看到,filter模块初始化了3个nf_hook_ops结构,且这3个nf_hook_ops挂载的hook点分别为NF_IP_LOCAL_IN、NF_IP_LOCAL_OUT、NF_IP_FORWARD,和我们的猜想的一样。
而3个hook回调函数分别为ipt_hook(NF_IP_LOCAL_IN与NF_IP_FORWARD点上的hook回调函数是相同的)、ipt_local_out_hook。那我们接下来分析下这两个函数,看它们的实现是怎样的额。
1.2.1.1ipt_hook
我们发现这个函数就是直接调用ipt_do_table,实现对数据包的过滤操作,看来我们的分析是正确的(关于ipt_do_table的详细分析,参看上一节的分析)。
主要是调用函数ipt_do_table,遍历该filter表的NF_LOCAL_IN链或者NF_LOCAL_FORWARD链的所有规则,对数据包进行规则检查,根据返回值确定对数据包的后续操作:
i)若是NF_ACCEPT,说明在filter表的hook检查通过了,接着对数据包进行其他的hook 检查。
ii)若是NF_DROP,则说明在filter表的hook检查时,检查的结果是丢掉数据包,则会 释放数据包的缓存,程序返回。
iii)若是NF_STOP,则后续不再进行hook检查,直接放行。
iiii)若是NF_QUEUE,则发送到应用层进行相应的操作。
staticunsignedint
ipt_hook(unsignedinthook,
structsk_buff**pskb,
conststructnet_device*in,
conststructnet_device*out,
int(*okfn)(structsk_buff*))
{
returnipt_do_table(pskb,hook,in,out,&packet_filter,NULL);
}
1.2.1.2ipt_local_out_hook
这个函数相比于ipt_hook,增加了对数据包长度的判断,即若本机发送出去的数据包ip头部长度不全时,则不进行过滤操作,直接返回ACCEPT。否则则调用ipt_do_table对数据包进行过滤操作。
staticunsignedint
ipt_local_out_hook(unsignedinthook,
structsk_buff**pskb,
conststructnet_device*in,
conststructnet_device*out,
int(*okfn)(structsk_buff*))
{
/*rootisplayingwithrawsockets.*/
if((*pskb)->len<sizeof(structiphdr)
||(*pskb)->nh.iph->ihl*4<sizeof(structiphdr)){
if(net_ratelimit())
printk("ipt_hook:happycracking.\n");
returnNF_ACCEPT;
}
returnipt_do_table(pskb,hook,in,out,&packet_filter,NULL);
}
2filter模块的初始化函数
该函数就是调用ipt_register_table注册filter表,以及调用nf_register_hook注册上面定义的ipt_ops[]。也就是把我们上面介绍的表的注册于hook函数的注册放在了同一个函数里执行而已。
这个函数里多了一句initial_table.entries[1].target.verdict=-forward-1;,而forward即为NF_ACCEPT,这句话的意思为设置entries[1]的target为NF_ACCEPT,而entries[1]的target初始化值已经是NF_ACCEPT了,所以我感觉这句代码目前是不需要的,可能以后增加NF_FORWARD时,就有必要了。。。
staticint__initinit(void)
{
intret;
if(forward<0||forward>NF_MAX_VERDICT){
printk("iptablesforwardmustbe0or1\n");
return-EINVAL;
}
/*Entry1istheFORWARDhook*/
initial_table.entries[1].target.verdict=-forward-1;
/*Registertable*/
ret=ipt_register_table(&packet_filter,&initial_table.repl);
if(ret<0)
returnret;
/*Registerhooks*/
ret=nf_register_hook(&ipt_ops[0]);
if(ret<0)
gotocleanup_table;
ret=nf_register_hook(&ipt_ops[1]);
if(ret<0)
gotocleanup_hook0;
ret=nf_register_hook(&ipt_ops[2]);
if(ret<0)
gotocleanup_hook1;
returnret;
cleanup_hook1:
nf_unregister_hook(&ipt_ops[1]);
cleanup_hook0:
nf_unregister_hook(&ipt_ops[0]);
cleanup_table:
ipt_unregister_table(&packet_filter);
returnret;
}
在熟悉了hook机制、数据结构之间的关系(ipt_entry、ipt_standard、ipt_standard_target、ipt_entry_match、ipt_entry_target、xt_table、xt_match、xt_target)、表的注册、表中规则的遍历与规则的匹配等机制后,就很容易理解iptables的filter表的注册与hook函数注册及hook函数的流程了。
由于nat表牵扯到连接跟踪,比filter表要复杂的多,下一节就开始分析连接跟踪,把连接跟踪分析完以后,再好好分析nat的功能。
鸟儿爱美,不仅需要羽毛之美,还需要鸣声婉转之美;