摘 要: 本文主要介绍在Linux内核中实现IP包过滤的基本流程,同时对IP伪装和网络地址转换(NAT)的实现作简要的介绍。
关键词 包过滤 防火墙 网络地址转换(NAT) IP伪装
随着因特网的迅猛发展,信息安全、网络安全已经成为人们日益关注的焦点。越来越多的企业和机关已经开始利用防火墙技术来保障网络不受外部黑客的入侵。包过滤技术是防火墙技术的技术核心,更好的理解包过滤的机制有助于我们更深刻的理解和应用防火墙来保障网络信息的安全。
一、 Linux网络部分代码分析
Linux网络层采用统一的缓冲区结构skbuff,一个个单独的skbuff被组织成双向链表的形式。网卡接收到数据帧后,系统内核为接收到的数据帧分配一块内存,然后将数据整理成skbuff的结构.在网络协议处理的时候,数据均以skbuff的形式在各层之间传递、处理。
skbuff的强大功能在于它提供了众多指针,可以快速的定位协议头位置;它也同时保留了许多数据包信息(如使用的网络设备等),以便协议层根据需要灵活应用.
在IP协议层有三个关键函数:ip_rcv( )、ip_forward( )、ip_output( ),分别处理IP层的接收、转发和发送工作。防火墙的功能函数将在此三个函数中调用。
二、 包过滤.
包过滤主要工作于IP层。Linux在应用层利用ipchains( )来实现对包过滤的实现。可以用该函数实现过滤规则的添加、删除、设置、更改,在对包过滤规则进行设置的同时,还可以指定对数据包进行IP伪装。在Linux 内核中有三条内置的规则链(input chain, forward chain, output chain),分别对应接收检测,转发检测和发送检测(内置链不可删除),每一条chain 包含一系列过滤规则及链的缺省策略.利用各规则链可以对输入、转发、和输出的数据包进行过滤。
其实现过程如下:
*在不同检测点进入相应过滤链.
*顺序检查每一条过滤规则,找出与之匹配的规则(ACCEPT, REJECT, DENY, MASQ,REDICT,RETURN).
*当遇到第一条匹配的规则时采取以下行动:
a. 将规则应用于此数据包;
b. 每条规则都包含有packet和byte数的计数器,当匹配时,此计数器加
c. 如果设置记录功能,则记录.
*当没有规则匹配时,采用链的缺省策略.
具体流程结合代码来说:
三种内置规则链分别作用于对应的三个函数ip_rcv( )、ip_forward( )和ip_output( )。
1、ip_rcv()是IP层的接收函数,由它来处理网卡接收到的数据包,它首先检查:
a.长度是否正确;
b..版本号是否正确(是IPV4还是IPV6?);
c.校验和是否正确.
在确定这些信息无误后,则调用包过滤检测:
fwres = call_in_firewall(PF_INET, dev, iph, &rport, &skb);
call_in_firewall 会对输入的数据包进行规则检查,fwres返回的便是匹配出来的规则.如果对应的规则为不接受该数据包,则立即将此数据包丢弃:
if (fwres
goto drop;
如果规则允许接受则查找路由表,对输入的数据包进行路由:
ip_route_input(skb, iph-daddr, iph-saddr, iph-tos, dev)
此时路由信息已包括在了skbuff数据结构的dst 项中,紧接着调用
skb-dst-input(skb) 继续处理 .
对发往本地高层协议的包,则调用ip_local_deliver(),进行处理。
对转往其他主机的包,则实际调用ip_forward()处理。
值得注意的是,经伪装的包在回来时,其目的IP是防火墙的IP,经路由后,也送入ip_local_deliver( )处理,在ip_local_deliver( )内部先解伪装,然后再查一次路由,发往本地的直接送往高层,,否则依然调用ip_forward( )。
2、ip_forward( )用来处理发往其他主机的数据包,其函数流程为:
1)、因为ip_forward()接收的参数是一个skbuff,它首先利用skbuff的指针,把IP头找出:
iph = skb-nh.iph
2)、因为ip_forward()由ip_rcv()调用,而在ip_rcv()中已查过了路由,此处只需利用skbuff的指针定位路由信息即可:
struct rtable *rt; /* Route we use */
rt = (struct rtable*)skb-dst;
3)、如果此IP包的生存时间(ttl)已到,则丢弃.
if (iph-ttl
goto too_many_hops;
4)、如果在选项中指定了严格的源路由功能(strict source routing) ,且此处无法达到,也丢弃:
if (opt-is_strictroute && rt-rt_dst != rt-rt_gateway)
goto sr_failed;
5)、如果指定的伪装功能,且上层协议是ICMP,则在此处处理一部分,且跳过后面的包过滤处理
#ifdef CONFIG_IP_MASQUERADE
if(!(IPCB(skb)-flags&IPSKB_MASQUERADED)) {
if (iph-protocol == IPPROTO_ICMP) {
........
fw_res = ip_fw_masq_icmp(&skb, maddr);
if (fw_res)
/* ICMP matched - skip firewall */
goto skip_call_fw_firewall;
........
}
}
#endif
6)、如果上一步的前提不成立,则要经过一次包过滤.
fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb);
7)、在当前linux版本中,包过滤与伪装功能在许多地方是紧密联系在一起的,如采用同样的配置工具ipchains,同样的配置接口setsocketopt(),其中是否启动伪装的标志也在防火墙的chains中.
8)、因为伪装可能改变了skbuff的一些信息,此时要重新定位一下IP头及其选项:
iph = skb-nh.iph;
opt = &(IPCB(skb)-opt);
9)、因为转发的数据总是要送出的,紧接着会调用call_out_firewall(),并把数据送出去.
3、在ip_local_deliver()中.
1).如果需要,首先重组IP包:
if (sysctl_ip_always_defrag == 0 &&
(iph-frag_off & htons(IP_MF|IP_OFFSET))) {
skb = ip_defrag(skb);
if (!skb)
return 0;
iph = skb-nh.iph;
2).然后调用ip_fw_demasquerade解开IP伪装。
ret = ip_fw_demasquerade(&skb);
3)、再次调用路由查找,根据真正的IP来发送此包.
ip_route_input(skb, iph-daddr, iph-saddr, iph-tos, skb-dev)
4).根据路由发往上层或是转发(略).
在对数据包进行处理的全部过程中,分别调用了以下三个函数:
call_in_firewall(), call_fw_firewall()和 call_out_firewall()。察看这三个函数的具体实现发现,其核心过程都在于ip_fw_check() 这个函数,它完成了数据包与规则的实际匹配.
ip_fw_check()函数分析:
防火墙的规则链由ip_chain数据结构描述,其中包含了指向链中第一条规则的指针和链的缺省策略。规则链中的每条规则由ip_fwkernel数据结构描述。ip_fw_check()所做的就是将每一个ip包与规则链中的每一条规则(实际就是ip_fw数据结构所描述的内容)按照链表的组织顺序一一比较,若匹配则并返回规则的行动项。
结束语
以上简单的介绍了linux下实现包过滤的基本方法,希望对大家起到抛砖引玉的作用。