首页 > 代码库 > netfilter分析

netfilter分析

转自:http://blog.sina.com.cn/s/blog_a31ff26901013n07.html一、概述1. Netfilter/IPTables框架简介 Netfilter/IPTables是继2.0.x的IPfwadm、2.2.x的IPchains之后,新一代的Linux防火墙机制。Netfilter采用模块化设计,具有良好的可扩充性。其重要工具模块IPTables连接到Netfilter的架构中,并允许使用者对数据报进行过滤、地址转换、处理等操作。 Netfilter提供了一个框架,将对网络代码的直接干涉降到最低,并允许用规定的接口将其他包处理代码以模块的形式添加到内核中,具有极强的灵活性。2. 主要源代码文件Linux内核版本:2.4.21Netfilter主文件:net/core/netfilter.c Netfilter主头文件:include/linux/netfilter.hIPv4相关: c文件:net/ipv4/netfilter#define PACKET_LOOPBACK 5 // 本机发给自己的报文#define PACKET_FASTROUTE 6 // 快速路由报文indev:输入设备,收到数据报的网络设备的net_device数据结构指针,即数据报到达的接口。用于NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN两个HOOKoutdev:输出设备,数据报离开本地所要使用的网络设备的net_device数据结构指针。用于NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING两个HOOK注意:在通常情况下,在一次HOOK调用中,indev和outdev中只有一个参数会被使用okfn:下一步要处理的函数。即如果有HOOK函数,则处理完所有的HOOK函数,且所有向该HOOK注册过的筛选函数都返回NF_ACCEPT时,调用这个函数继续处理;如果没有注册任何HOOK,则直接调用此函数。其5个参数将由宏NF_HOOK传入。3. HOOK点的实现 对应于各个不同协议的不同HOOK点是由一个二维数组nf_hooks存储的(位于net/core/netfilter.c,Line 47),具体的HOOK点则由数据结构nf_hook_ops(位于include/linux/netfilter.h,Line 44)实现。如图http://blog.chinaunix.net/photo/24896_061206192528.jpgnetfilter分析-1所示: 其中,nf_hook_ops成员中:`int priority;`priority值越小,优先级越高,相关优先级在include/linux/netfilter_ipv4.h,Line52中枚举定义:enum NF_IP_hook_priorities {NF_IP_PRI_FIRST = INT_MIN,NF_IP_PRI_CONNTRACK= -200,NF_IP_PRI_MANGLE = -150,NF_IP_PRI_NAT_DST = -100,NF_IP_PRI_FILTER = 0,NF_IP_PRI_NAT_SRC = http://www.mamicode.com/100,>target_offset;每一个match的大小为:ipt_entry_match->u.match_size。 target的定位则为: 起始地址为match的结束地址,即:当前规则(起始)地址+ipt_entry-> target_offset;结束地址为下一条规则的起始地址,即:当前规则(起始)地址+ipt_entry-> next_offset;每一个target的大小为:ipt_entry_target->u.target_size。 这些对于理解match以及target相关函数的实现是很有必要明确的。 同时,include/linux/netfilter_ipv4/ip_tables.h中提供了三个“helper functions”,可用于使对于entry、tartget和match的操作变得方便,分别是:函数ipt_get_target():Line274,作用是取得target的起始地址,也就是上面所说的当前规则(起始)地址+ipt_entry-> target_offset;宏IPT_MATCH_ITERATE():Line281,作用是遍历规则的所有match,并执行同一个(参数中)给定的函数。其参数为一个ipt_entry_match结构和一个函数,以及函数需要的参数。当返回值为0时,表示遍历以及函数执行顺利完成;返回非0值时则意味着出现问题已终止。宏IPT_ENTRY_ITERATE():Line300,作用是遍历一个表中的所有规则,并执行同一个给定的函数。其参数为一个ipt_entry结构、整个规则表的大小,以及一个函数和其所需参数。其返回值的意义与宏IPT_MATCH_ITERATE()类似。那么如何保证传入的ipt_entry结构是整个规则表的第一个结构呢?据源码看来,实际调用这个宏的时候传入的第一个参数都是某个ipt_table_info结构的实例所指向的entries成员,这样就保证了对整个规则表的完整遍历。4. 规则的使用 当一个特定的HOOK被激活时,数据报就开始进入Netfilter/IPTables系统进行遍历,首先检查`struct ipt_ip ip`,然后数据报将依次遍历各个match,也就是`struct ipt_entry_match`,并执行相应的match函数,即ipt_match结构中的*match所指向的函数。当match函数匹配不成功时返回0,或者hotdrop被置为1时,遍历将会停止。 对match的遍历完成后,会开始检查`struct ipt_entry_target`,其中如果是一个标准的target,那么会检查`struct ipt_standard_target`中的verdict,如果verdict值是正的而偏移却指向不正确的位置,那么ipt_entry中的comefrom成员就有了用武之地——数据报返回所经历的上一个规则。对于非标准的target呢,就会调用target()函数,然后根据其返回值进行后面的处理。5. 规则的扩展 Netfilter/IPTables提供了对规则进行扩展的机制:可以写一个LKM来扩展内核空间的功能,也可以写一个共享库来扩展用户空间中IPTables的功能。内核的扩展 要对内核空间的功能进行扩展,实际上就是写一个具有表、match以及target增加功能的模块,相关的函数为(位于net/ipv4/netfilter/ip_tables.c,Line1318 to 1444): ipt_register_table()、ipt_unregister_table(),参数为struct ipt_table *。ipt_register_table()函数是这三对函数中最复杂的一个,涉及了内存、信号量等方方面面的东西,但总起来说就做了初始化表以及加入双向链表两件事。其复杂一是因为涉及到多CPU的处理(每个CPU拥有各自独立的“规则空间”),需要首先将新的entries放入第一个CPU空间,在检查完毕后再复制到其他CPU中;二是就是上面所说对新table各个entry的检查,包括边界检查以及完整性检查等。其中的重要函数有这么几个:translate_table()(位于net/ipv4/netfilter/ip_tables.c,Line797):这个函数的主要作用是检查并应用用户空间传来的规则 : 对新表进行边界检查(由宏IPT_ENTRY_ITERATE()调用函数check_entry_size_and_blocks(),位于net/ipv4/netfilter/ip_tables.c,Line732),包括对齐、过大过小等,特别是保证赋给hook_entries和underflows值的正确性。调用函数make_source_chains()(位于net/ipv4/netfilter/ip_tables.c,Line499)检查新的表中是否存在规则环,同时将HOOK的规则遍历顺序存入comefrom变量。(这个函数我没有仔细看,只是大概略了一下)对ipt_entry依次进行ipt_ip头、match以及target的完整性检查(由宏IPT_ENTRY_ITERATE()调用函数check_entry(),位于net/ipv4/netfilter/ip_tables.c,Line676),保证ipt_entry的正确性。将正确的 ipt_tables复制给其他的CPU。这个函数另外还在do_replace()函数(仅在一个源码中没有被调用过的函数中被调用,不予分析)中被调用。 replace_table()(位于net/ipv4/netfilter/ip_tables.c,Line877):这个函数的主要作用是:将得到模块初始值的ipt_table_info结构(newinfo)中的值传给ipt_table中的private,并返回ip_table中旧的private。list_prepend()(位于include/linux/netfilter_ipv4/listhelp.h,Line75):在这个函数被调用之前,整个初始化的过程就已经结束了,这个函数的主要作用是:互斥地调用Linux源码中的list_add()函数(位于include/linux/list.h,Line55),将新的table加入到双向链表之中。 ipt_register_match()、ipt_unregister_match(),参数为struct ipt_match *。ipt_register_target()、ipt_unregister_target(),参数为struct ipt_target *。 这三对函数除了ipt_register_table()外的5个函数主要就是互斥地将table/match/target加入到双向链表中或者从双向链表中删除。 其中向双向链表中加入新节点是通过调用list_named_insert()函数(位于include/linux/netfilter_ipv4/listhelp.h,Line101)实现的。这个函数的主要工作是首先确定待插入的match名字是否已经存在,只有不存在时才进行插入的操作。用户空间的扩展 用户空间中的扩展用的是共享库配合libiptc库的机制,但这种机制是在单独的IPTbales程序中提供的,内核源码中并没有提供,这里就不做分析了。

netfilter分析