Linux netfilter 学习笔记 之六 ip层netfilter的filter表的创建

基于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的功能。

鸟儿爱美,不仅需要羽毛之美,还需要鸣声婉转之美;

Linux netfilter 学习笔记 之六 ip层netfilter的filter表的创建

相关文章:

你感兴趣的文章:

标签云: