Netfilter实现机制分析

1. 前言
Netfilter作为目前进行包过滤,连接跟踪,地址转换等的主要实现框架,了解其内部机制对于我们更好的利用Netfilter进行设 计至关重要,因此本文通过阅读内核源码2.6.21.2,根据自身的分析总结出Netfilter的大致实现机制,由于自身水平有限,且相关的参考资料较 少,因此其中的结论不能保证完全正确,如果在阅读本文的过程中发现了问题欢迎及时与作者联系。
2. 规则的存储与遍历机制
       规则的存储机制
在Netfilter中规则是顺序存储的,一条规则主要包括三个部分:ipt_entry、ipt_entry_matches、 ipt_entry_target。ipt_entry_matches由多个ipt_entry_match组成,ipt_entry结构主要保存标准 匹配的内容,ipt_entry_match结构主要保存扩展匹配的内容,ipt_entry_target结构主要保存规则的动作。在 ipt_entry中还保存有与遍历规则相关的变量target_offset与next_offset,通过target_offset可以找到规则中 动作部分ipt_entry_target的位置,通过next_offset可以找到下一条规则的位置。规则的存储如下图2-1所示。


图2-1 规则的存储

    ipt_entry结构如下图2-2所示,其成员ip指向结构ipt_ip,该结构主要保存规则中标准匹配的内容(IP、mask、interface、 proto等),target_offset的值等于ipt_entry的长度与ipt_entry_matches的长度之和,next_offset 的值等于规则中三个部分的长度之和。通过target_offset与next_offset可以实现规则的遍历。


                                                 图2-2 ipt_entry结构

       ipt_entry_match主要保存规则中扩展匹配内容(tos、ttl、time等),其是Netfilter中内核与用户态交互的关键数据结构, 在其内核部分由一个函数指针指向一个ipt_match结构,该结构体中包含了对包做匹配的函数,是真正对包做匹配的地方。 ipt_entry_target结构与ipt_entry_match结构很类似。

图2-3 ipt_entry_match结构
            

                                                 图2-4 ipt_entry_target结构
      
    规则的遍历机制
在Netfilter中,函数ipt_do_table()实现了规则的遍历,该函数根据传入的参数table和hook找到相应的规则起点,即第一个ipt_entry的位置,主要通过函数get_entry()实现。

private = table- > private ;
table_base = ( void * ) private - > entries[ smp_processor_id( ) ] ;
e = get_entry( table_base, private - > hook_entry[ hook] ) ;


       标准匹配是通过函数ip_packet_match()实现的,该函数主要对包的五元组信息进行匹配,扩展匹配则通过宏IPT_MATCH_ITERATE实现,该宏的定义为:

# define IPT_MATCH_ITERATE( e, fn, args. . . ) \
( {                      \
unsigned int __i;          \
int __ret = 0;             \
struct ipt_entry_match * __match; \
                     \
for ( __i = sizeof ( struct ipt_entry) ; \
    __i < ( e) - > target_offset;        \
    __i + = __match- > u. match_size) { \
       __match = ( void * ) ( e) + __i; \
                     \
       __ret = fn( __match , # # args) ; \
      if ( __ret ! = 0)          \
         break ;          \
}                    \
__ret;                    \
} )


       宏IPT_MATCH_ITERATE依次调用各个ipt_entry_match所指向的ipt_match中match()处理数据包,在for循环中使用了terget_offset位置变量查找match的位置。
       在对数据包进行了匹配后,接着需要进行相应的动作处理,通过函数ipt_get_target()获取规则动作ipt_entry_target的位置:

static __inline__ struct ipt_entry_target *
ipt_get_target( struct ipt_entry * e)
{
return ( void * ) e + e- > target_offset;
}


       如果还需要继续遍历下一条规则,则继续执行以下语句以找到下一条规则的开始位置:

e = ( void * ) e + e- > next_offset;





3.   表、匹配、动作存储及管理机制
    表、匹配、动作的存储机制
规则中所使用到的match、target、table使用全局变量xt_af所指向的相应链表保存,这些链表是在对Netfilter进行初始化或匹配 模块扩展时进行更新的,在初始化时,默认的表及动作则添加到相应的链表中。Netfilter实现了很好的扩展性,如需要对数据包的时间进行匹配,则在 match的链表中需要首先增加time扩展匹配模块,在相应的规则中则通过指向该time模块所对应的函数match()以进行时间的匹配。xt_af 是个一维数组,其按照协议族的不同分别存储,目前我们常用的协议族主要是AF_INET。

                                                      图3-1 match,target,table的全局存储
      
match、target、table的全局存储如上图3-1所示,以下为各部分的详细的结构表示。当扩展一个匹配模块时,其会注册一个 ipt_match结构到match链表中,该结构的主要变量值如下图所示,name表示扩展模块的名字,match()是该模块最主要的函数,其主要对 数据包进行相应的比较,checkentry()主要对包进行相应的完整性检验,destroy()在对模块进行撤销时调用。如果需要自己新加一个扩展模 块,则需要构造一个ipt_match结构并注册到相应的链表中。ipt_target的结构与ipt_match相似,其最主要的函数是 target()。


                                                     图3-2 ipt_match结构的存储


                                                      图3-3 ipt_target结构的存储

table主要是用来对规则进行管理,通过table中的相应参数可以找到相应的规则所处的入口位置。

                                                              图3-4 ipt_table结构的存储

    表、匹配、动作的管理机制
match、target、table的注册分别调用xt_register_match()、 xt_register_target()、xt_register_table()实现,前两个注册函数很相 似,xt_register_table()则稍微复杂些。撤销时则分别调用相应的unregister函数实现。 xt_register_match()函数的定义如下(xt_match与ipt_match是一样的):

int
xt_register_match( struct xt_match * match)
{
int ret, af = match- > family;

ret = mutex_lock_interruptible( & xt[ af] . mutex) ;
if ( ret ! = 0)
      return ret;

list_add( & match- > list , & xt[ af] . match) ;
mutex_unlock( & xt[ af] . mutex) ;

return ret;
}


xt_register_table()函数的定义如下(xt_table与ipt_table是一样的),因为一个xt_table结构中还指向另一结构xt_table_info,该结构主要描述表的相关信息,所以对表注册时需要对这两类结构体进行定义。

int xt_register_table( struct xt_table * table,
      struct xt_table_info * bootstrap,
      struct xt_table_info * newinfo)
{
int ret;
struct xt_table_info * private ;
struct xt_table * t;

ret = mutex_lock_interruptible( & xt[ table- > af] . mutex) ;
if ( ret ! = 0)
      return ret;

/* Don't autoload: we'd eat our tail... */
list_for_each_entry( t, & xt[ table- > af] . tables, list ) {
      if ( strcmp ( t- > name, table- > name) = = 0) {
         ret = - EEXIST;
         goto unlock;
      }
}

/* Simplifies replace_table code. */
table- > private = bootstrap;
rwlock_init( & table- > lock) ;
if ( ! xt_replace_table( table, 0, newinfo, & ret) )
      goto unlock;

private = table- > private ;
duprintf( "table->private->number = %u\n" , private - > number) ;

/* save number of initial entries */
private - > initial_entries = private - > number;

list_add( & table- > list , & xt[ table- > af] . tables) ;

ret = 0;
unlock:
mutex_unlock( & xt[ table- > af] . mutex) ;
return ret;
}



4. 钩子函数的存储及管理机制
钩子函数的存储机制
钩子函数由一个全局二维链表nf_hooks保存,其按照协议族归类存储,在每个协议族中,根据钩子点顺序排列,在钩子点内则根据钩子函数的优先级依次排 列。钩子函数的存储图如下图4-1所示,链表中的每个元素都是指向结构体nf_hook_ops中的hook()函数的指针,nf_hook_ops实际 存储了钩子函数的内容,其结构如图4-2所示。在相应的钩子点调用钩子函数时,则根据协议族和钩子点找到相应的链表入口,然后依次调用该链中的每一个钩子 函数对数据包进行操作。

                                                                 图4-1 钩子函数的全局存储



                                                                         图4-2 钩子函数的链表
       钩子函数的管理机制
如果需要在相应的钩子点挂载钩子函数,则需要首先定义一个nf_hook_ops结构,在其中实现实际的钩子函数,再调用函数nf_register_hook()将该钩子函数注册到图4-1所示的二维链表中,nf_register_hook()函数的定义如下:



5. Netfilter的流程框架
   在Netfilter中的不同钩子点调用了不同的钩子函数,这些钩子函数的调用如图4-1所示,其调用的流程框架如下图5-1所示。



                                                           图5-1 Netfilter中hook函数的调用流程

       Netfilter中默认表filter在建立时则在NF_IP_LOCAL_IN,NF_IP_FORWARD钩子点注册了钩子函数 ipt_hook(),在NF_IP_LOCAL_OUT这个点注册了钩子函数ipt_local_out_hook(),两个钩子函数都会调用 ipt_do_table()对相对应的表和钩子点的规则进行遍历。调用的流程如下图5-2所示。

                                                                 图5-2 Netfilter中规则的调用流程
6.总结
以上只是简单分析了Netfilter的整体框架,主要描述了其中的实现机制。在这个机制上已经实现了很多功能,除了对基本的功能进行完善和改进外,还出 现了很多新的扩展功能。如在此架构上实现的连接跟踪机制和NAT机制,以及结合连接跟踪机制与Netfilter框架实现的Layer7扩展匹配模块等。 对此框架的了解,有助于我们更好的利用Netfilter框架实现我们的设计,鉴于自身水平有限,因此以上的分析不能保证全部正确。希望各位批评指正。

由于主要是为了描绘出整个Netfilter的框架,故对其中较细节的的内容有所忽略而未深入分析,如规则的另外一个动作 ipt_standard_target,table表注册时的初始化等,但这并不影响对整个框架的了解。至于Netfilter在链路层的实现机制此处 也并未分析,因为其实现较简单,且我们大部分是在网络层利用Netfilter架构。分析中也未涉及到用户态规则与内核态规则之间的关系,对于 iptables如何操作内核中的规则并未介绍,以后有机会再做详细分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值