一.Netfilter简介
从Linux Kernel 2.4开始,一个新的网络包过滤框架替代了原来的pchains/ipfwadm系统,那就是netfilter和iptables。作为内核网络协议堆的一个扩展子集,netfilter可以在内核空间非常高效的进行包过滤,网络地址转换(NAT)和包重组等功能。同时,新的Netfilter/Iptables框架设计采取了更优的软件设计策略,如模块化设计,使其具有良好的扩展性,让全世界的网络程序员可以针对各自的应用的开发各种不同的应用模块。
二.Netfilter框架分析
1. Netfilter提供了一个框架,将对网络代码的直接干涉降到最低,并允许用规定的接口将其他包处理代码以模块的形式添加到内核中,具有极强的灵活性。
2. Netfilter事实上是内核网络协议栈中(如 IPv4, IPv6 和 DECnet)中的一些HOOK集合,通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些监测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是用户自定义的功能)。
3. 一个理想的IPv4的遍历图如下:
IP层的五个HOOK点的位置如下图所示(copy from <packet filter howto>) :
--->[1]--->[ROUTE]--->[3]--->[4]--->
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
| |
v |
[local process]
[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验 和等检测), 源地址转换在此点进行;
[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;
[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪装)在此点进行;
[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。
在IP层代码中,有一些带有NF_HOOK宏的语句,如IP的转发函数中有:
<-ipforward.c ip_forward()->
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2, ip_forward_finish);
其中NF_HOOK宏的定义提炼如下:
<-/include/linux/netfilter.h->
#ifdef CONFIG_NETFILTER
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)
(list_empty(&nf_hooks[(pf)][(hook)])
? (okfn)(skb)
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
#endif /*CONFIG_NETFILTER*/
如果在编译内核时没有配置netfilter时,就相当于调用最后一个参数,此例中即执行ip_forward_finish函数;否则进入HOOK点,执行通过nf_register_hook()登记的功能
(这句话表达的可能比较含糊,实际是进入nf_hook_slow()函数,再由它执行登记的函数)。
NF_HOOK宏的参数分别为:
<1>.pf:协议族名,netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如 PF_INET6,PF_DECnet等名字。
<2>.hook:HOOK点的名字,对于IP层,就是取上面的五个值;
<3>.skb:套接字缓冲区的结构;
<4>.indev:进来的设备,以struct net_device结构表示;
<5>.outdev:出去的设备,以struct net_device结构表示;
(后面可以看到,以上五个参数将传到用nf_register_hook登记的处理函数中。)
<6>.okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。
这些点是已经在内核中定义好的,除非你是这部分内核代码的维护者,否则无权增加或修改,而在此检测点进行的处理,则可由用户指定。像packet filter,NAT,connection track这些功能,也是以这种方式提供的。正如netfilter的当初的设计目标--提供一个完善灵活的框架,为扩展功能提供方便。
如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为:
int nf_register_hook(struct nf_hook_ops *reg)