dedecms织梦内容管理系统    
首页 | java | C/C++ | PHP | 操作系统 | ajax | 脚本编程 | 安全技术 | 本站下载页 | flex | CRM | 专题 | QQ群 | 测试中心 | 会员中心 | 积分规则
  当前位置:主页>操作系统>linux>文章内容
linux防火墙实现技术比较
来源:未知     作者:    
一 前言
此文是在aka(www.aka.org.cn)的一次讲座稿【12】基础之上修改而成(催稿,没办法),着重阐述linux下
的防火墙的不同实现之间的区别,以ipchains, iptables, checkpoint FW1为例。


二 基本概念

2.0
在进入正题之前,我将花少许篇幅阐述一些基本概念。尽管防火墙的术语这些年基本上没有太大的变化,但是如果你以前只看过90年代初的一些文献的话,有些概念仍然会让你混淆。此处只列出一些最实用的,它们不是准确的定义,我只是尽可能的让它们便于理解而已。

2.1 包过滤:
防火墙的一类。80年代便有论文来描述这种系统。传统的包过滤功能在路由器上常可看到,而专门的防火墙系统一般在此之上加了功能的扩展,如状态检测等。它通过检查单个包的地址,协议,端口等信息来决定是否允许此数据包
通过。

2.2 代理:
防火墙的一类。工作在应用层,特点是两次连接(browser与proxy之间,proxy与web server之间)。如果对原理尚有疑惑,建议用sniffer抓一下包。代理不在此文的讨论范围之内。

2.3 状态检测:
又称动态包过滤,是在传统包过滤上的功能扩展,最早由checkpoint提出。传统的包过滤在遇到利用动态端口的协议时会发生困难,如ftp。你事先无法知道哪些端口需要打开,而如果采用原始的静态包过滤,又希望用到的此服务的话,就需要实现将所有可能用到的端口打开,而这往往是个非常大的范围,会给安全带来不必要的隐患。 而状态检测通过检查应用程序信息(如ftp的PORT和PASS命令),来判断此端口是否允许需要临时打开,而当传输结束时,端口又马上恢复为关闭状态。

2.4 DMZ非军事化区:
为了配置管理方便,内部网中需要向外提供服务的服务器往往放在一个单独的网段,这个网段便是非军事化区。防火墙一般配备三块网卡,在配置时一般分别分别连接内部网,internet和DMZ。

2.5
由于防火墙地理位置的优越(往往处于网络的关键出口上),防火墙一般附加了NAT,地址伪装和VPN等功能,这些不在本文的讨论范围。


三 检测点

3.0 综述
包过滤需要检查IP包,因此它工作在网络层,截获IP包,并与用户定义的规则做比较。

3.1 ipchains
摘自【3】

----------------------------------------------------------------
| ACCEPT/ lo interface |
v REDIRECT _______ |
--> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ -->
h a |input | e {Routing } |Chain | |output |ACCEPT
e n |Chain | m {Decision} |_______| --->|Chain |
c i |______| a ~~~~~~~~ | | ->|_______|
k t | s | | | | |
s y | q | v | | |
u | v e v DENY/ | | v
m | DENY/ r Local Process REJECT | | DENY/
| v REJECT a | | | REJECT
| DENY d --------------------- |
v e -----------------------------
DENY

总体来说,分为输入检测,输出检测和转发检测。但具体到代码的时候,输出检测实际分散到了几处(不同的上层协议走IP层的不同的流程):
UDP/RAW/ICMP报文:ip_build_xmit
TCP报文:ip_queue_xmit
转发的包:ip_forward
其它:ip_build_and_send_pkt

正如ipchains项目的负责人Rusty Russell所说,在开始ipchians不久,便发现选择的检测点位置错了,最终只能暂时将错就错。一个明显的问题是转发的包在此结构中必须经过三条链的匹配。地址伪装功能与防火墙模块牵扯过于紧密,如果不详细了解其原理的话,配置规则很容易出错。

此部分详细的分析可参见我早期的一份文章【9】。


3.2 iptables

A Packet Traversing the Netfilter System:

--->PRE------>[ROUTE]--->FWD---------->POST------>
Conntrack | Filter ^ NAT (Src)
Mangle | | Conntrack
NAT (Dst) | [ROUTE]
(QDisc) v |
IN Filter OUT Conntrack
| Conntrack ^ Mangle
| | NAT (Dst)
v | Filter


2.4内核中的防火墙系统不是2.2的简单增强,而是一次完全的重写,在结构上发生了非常大的变化。相比2.2的内核,2.4的检测点变为了五个。

在每个检测点上登记了需要处理的函数(通过nf_register_hook()保存在全局变量nf_hooks中),当到达此检测点的时候,实现登记的函数按照一定的优先级来执行。严格的从概念上将,netfilter便是这么一个框架,你可以在适当的位置上登记一些你需要的处理函数,正式代码中已经登记了许多处理函数(在代码中搜nf_register_hook的调用),如在NF_IP_FORWARD点上登记了装发的包过滤功能。你也可以登记自己的处理,具体例子可参看【8】与【10】。

3.3 FW1
FW1是chekpoint推出的用于2.2内核的防火墙。由于其发布的模组文件带了大量的调试信息,可以从反的代码中窥探到到许多实现细节。
FW1通过dev_add_pack的办法加载输入过滤函数,如果对这个不熟悉,请参看【14】。但是此处有个问题:在net_bh()中,传往网络层的skbuff是克隆的,即
skb2=skb_clone(skb, GFP_ATOMIC);
if(skb2)
pt_prev->func(skb2, skb->dev, pt_prev);
这样的话如果你想丢弃此包的话,光将其free掉是不够的,因为它只是其中的一份拷贝而已。
FW1是怎么解决这个问题的呢?见下面的代码(从代码翻译成的C程序):

packet_type *fw_type_list=NULL;

static struct packet_type fw_ip_packet_type =
{
__constant_htons(ETH_P_IP),
NULL, /* All devices */
fw_filterin,
NULL,
NULL, /* next */
};

fwinstallin(int isinstall )
{
packet_type *temp;

/*安装*/
if(isinstall==0){
dev_add_pack(&fw_ip_packet_type);
fw_type_list = fw_ip_packet_type->next;

for(temp = fw_type_list; temp; temp=temp->temp)
dev_remove_pack(temp);
}
/*卸载*/
else {
dev_remove_pack(&fw_ip_packet_type);

for(temp = fw_ip_packet_type; temp; temp=temp->next)
dev_add_pack(temp);
}
}

不难看出,FW1把ip_packet_type歇载掉了,然后自己在自己的处理(fw_filterin)中调ip_recv。

输出的挂载和lkm的手法一样,更改dev->hard_start_xmit。dev结构在2.2版本的发展过程中变了一次,为了兼容FW1对这点也做了处理(通过检查版本号来取偏移)。

还有一款linux下的防火墙产品WebGuard(http://www.gennet.com.tw/b5/csub_webguard.html)采用的手法与FW1其非常类似。有兴趣的人可以自行研究一下。


四 规则

4.0 综述

4.1 ipchains

man ipfw可以看到这一段的详细解释。关键如下:

规则链用ip_chain结构来表示,缺省有input,ouptput,forward三条链。在配置规则的时候,也可以添加新的链。每条链事实上就是一组相关的规则,以链表的形式存储。
struct ip_chain
{
ip_chainlabel label; /* Defines the label for each block */
struct ip_chain *next; /* Pointer to next block */
struct ip_fwkernel *chain; /* Pointer to first rule in block */
__u32 refcount; /* Number of refernces to block */
int policy; /* Default rule for chain. Only *
* used in built in chains */
struct ip_reent reent[0]; /* Actually several of these */
};

每条规则用一个ip_fwkernel结构表示:
struct ip_fwkernel
{
struct ip_fw ipfw;
struct ip_fwkernel *next; /* where to go next if current
* rule doesn't match */
struct ip_chain *branch; /* which branch to jump to if
* current rule matches */
int simplebranch; /* Use this if branch == NULL */
struct ip_counters counters[0]; /* Actually several of these */
};

ip_fwkernel中的一个重要部分就是ip_fw,用来表示待匹配的数据包消息:
struct ip_fw
{
struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
__u32 fw_mark; /* ID to stamp on packet */
__u16 fw_proto; /* Protocol, 0 = ANY */
__u16 fw_flg; /* Flags word */
__u16 fw_invflg; /* Inverse flags */
__u16 fw_spts[2]; /* Source port range. */
__u16 fw_dpts[2]; /* Destination port range. */
__u16 fw_redirpt; /* Port to redirect to. */
__u16 fw_outputsize; /* Max amount to output to
NETLINK */
char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
__u8 fw_tosand, fw_tosxor; /* Revised packet priority */
};

2.2内核中网络包与规则的实际匹配在ip_fw_check中进行。

4.2 iptables

一条规则分为三部分:
struct ipt_entry file://主要用来匹配IP头
struct ip_match file://额外的匹配(tcp头,mac地址等)
struct ip_target file://除缺省的动作外(如ACCEPT,DROP),可以增加新的(如REJECT)。

man iptable:
>A firewall rule specifies criteria for a packet, and a
>target. If the packet does not match, the next rule in
>the chain is the examined; if it does match, then the next
>rule is specified by the value of the target, which can be
>the name of a user-defined chain, or one of the special
>values ACCEPT, DROP, QUEUE, or RETURN.

2.4内核中网络包与规则的实际匹配在ip_do_table中进行。这段代码的流程在
netfilter hacking howto 4.1.3描述的非常清楚。

简化代码如下:
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(struct sk_buff **pskb,
unsigned int hook,
const struct net_device *in,
const struct net_device *out,
struct ipt_table *table,
void *userdata)
{
struct ipt_entry *e;
struct ipt_entry_target *t;
unsigned int verdict = NF_DROP;

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

...
ip_packet_match(ip, indev, outdev, &e->ip, offset);

...
IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, protohdr, datalen, &hotdrop)

...
t = ipt_get_target(e);

...
verdict = t->u.kernel.target->target(pskb, hook, in, out, t->data, userdata);//非标准的target走这一步

...
return verdict;
}

流程:
--->NF_HOOK();(/include/linux/netfilter.h)
--->nf_hook_slow;(/net/core/netfilter.c)
--->nf_iterate();(/net/core/netfilter.c)
--->然后运行登记的;如果你希望有一套ipt_entry结构规则,并将它放到table里,你此时便可调用ipt_do_table来匹配。

在2.4内核中,规则本身也是可扩展的,体现可自己定义并添加新的ip_match和ip_target上。

4.2 FW1
未作分析。


五 与应用层的交互

5.0 综述

防火墙除了内核里的功能以外,还需要在应用层有相应的的配置工具,如添加修改规则等,这就涉及如何与内核通信的问题。
内核模块有三种办法与进程打交道:首先是系统调用,缺点是必须添加新的系统调用或修改原有的,造成对内核代码原有结构的变换;第二种办法是通过设备文件(/dev目录下的文件),不必修改编译原有的代码,但在使用之前要先用mknod命令产生一个这样的设备;第三个办法便是使用proc文件系统。

5.1 ipchains
由于ipchains是已经是内核的正式一部分,它采用了修改系统调用的办法来添加修改命令,采用的办法就是扩展setsockopt系统调用:
int setsockopt (int socket, IPPROTO_IP, int command, void *data, int length)

man ipfw可以获得这方面的细节。

ipchains应用程序首先要需要建立一个raw socket(libipfwc.c),然后在之上调用setsockopt。
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)

调用顺序:
ipchains在应用层调用setsockopt,进入内核:
--->sys_socketcall(net/socket.c)
--->sys_setsockopt(net/socket.c)
--->inet_setsockopt(net/ipv4/af_inet.c)
--->sock_setsockopt(net/core/sock.c)
--->raw_setsockopt(net/ipv4/raw.c)
--->ip_setsockopt(net/ipv4/ip_sockglue.c)
--->ip_fw_ctl(net/ipv4/ip_fw.c)

5.2 iptables
原理同ipchains, 但内部命令格式作了大幅简化。详见nf_setsockopt()。

5.3 FW1
FW1 登记了一个字符设备,通过它来进行用户空间与内核空间的交互。相关代码(从代码翻译成的C程序)如下:

static unsigned int fw_major=0;

static struct file_operations fw_fops=
{
NULL, /* lseek */
fw_read, /* read */
fw_write, /* write */
NULL, /* readdir */
fw_poll, /* poll */
fw_ioctl, /* ioctl */
NULL, /* mmap */
fw_open, /* open */
NULL, /* flush */
fw_release /* release */
NULL, /* fsync */
};

int init_module()
{
...
/*man register_chrdev
On success, register_chrdev returns 0 if major is a number
other then 0, otherwise Linux will choose a major number and
return the chosen value.*/
if(fw_major=register_chrdev(UNNAMED_MAJOR, “fw”, &fw_fops))
return -1;
...
}

void cleanup_module()
{
...
unregister_chrdev(fw_major, "fw");
...

}

fw_ioctl()用来做配置工作。


六 碎片的处理

6.0 综述
关于分片重组的实现可参看【13】。

6.1 ipchains
在2.2内核中除非设置了alway_defrag,否则包过滤模块不会对经过的包进行重组。它采用的办法是只看第一片,因为只有这一片中有完整的头信息,而后序的分片一律允许通过。为了防止分片欺骗(比如第一片极小,把传输层信息放到了第二片中),对这种正常情况中不可能出现的包做了而外的处理(太小的分片会被丢弃)。

6.2 iptables
在2.4内核有些变化,如果启动了conncetion track,所有到达防火墙的碎片都会重组,这点在以后可能会变化,正如howto 中说的,现在考虑的还只是功能的完备性,效率还要在以后的版本中改进。检测点也有了变化,输入检测在改在重组之后。

6.3 FW1
FW1对分片也做了额外处理,但目前尚未对其实现做仔细的分析。


七 状态检测

7.0 综述
基于状态的检测对管理规则提出了非常大的方便,现在已成了防火墙的一项基本要求。但目的明确之后,其实现可以选择多种不同的方法。

7.1 ipchains
ipchains本身不能完成状态检测,但有几份pacth为它做了一下这方面的补充,采用的是简单的动态添加规则的办法,这是作者对其的介绍:
> I believe it does exactly what I want: Installing a temporary
> "backward"-rule to let packets in as a response to an
> outgoing request.

7.2 iptables
在2.4内核中,基于状态的检测已经实现,利用的是connection track模块。此模块检查所有到来的数据包,将得到的状态(enum ip_conntrack_status)保留在sk_buff结构中(即skb->nfct,可通过ip_conntrack_get()得到)。
在规则中要指明状态信息(作为一个ipt_match),既实际上仍是比较每一条规则。从效率上,这种处理方式感觉不如下面FW1采用的方式好。

7.3 FW1
这段的代码没有做分析,但有一些文章通过黑箱操作的办法“猜测“出了它的实现原理,如【1】。除规则表以外,FW1另外维护一份状态表。当一个新的连接发生的时候,FW1与规则表配备,如果允许通过的话,则在状态表中建立相应表项。以后的数据过来的时候首先匹配状态表,如果它属于一个连接,便允许通过,而不再检查规则表。
草草看了一下BSD下的防火墙ipfilter的howto,感觉它的实现与FW1基本相同。


八 的问题
许多初读内核的人对函数的应用很不适应,在netfilter中更是用的非常广泛。大量register的应用,使得netfilter非常的模块化,但是给初学者带来的问题也不小。

这里是linuxforum上的一份帖子,如果看代码时对指针的指向总是糊里糊涂的话,可借鉴一下这个思路(当然关键还是要找到初始化的地方):

>Linux内核技术
>herze (stranger ) 01/15/01 02:54 PM
>高手指点:PPP的发送在那里?
>在Linux内核2.4.0中对于PPP数据包已经打好的包,内核中的ppp_generic.c文件中发送的流程好像如下
>ppp_file_write()->ppp_xmit_process()->ppp_push()(可能也由其它的发送流程,但是最后都是
>用到了ppp_push())这个函数,而这个调用了一个struct channel中struct ppp_channel中的
>struct ppp_channel_ops 中的一个
>int (*start_xmit)(struct ppp_channel *, struct sk_buff *)来进行发送的,但是下面我就不明白了。
>虽然在drivers/char/cyclades.c和drivers/char/serial167.c中找到了
>start_xmit( struct cyclades_port *info )但是说明都不相同。
>请教:
>int (*start_xmit)(struct ppp_channel *, struct sk_buff *)
>到底这个是指到了什么地方?
>是不是和具体的硬件有关,但是我怎么在内核中找不到对应的?

>Linux内核技术
>yawl (stranger ) 01/15/01 11:31 PM
>思路这样 [re: herze]
>内核中常有这样的类似处理,查找这种的一个好办法,就是找那种结构的实例,对于你的问题,就是找
>ppp_channel_ops,最终会找到async_ops(ppp_async.c)和sync_ops(ppp_synctty.c),没看过这块的
>具体代码,不敢多说,但思路如此。


九 后记
尽管此文中是在【12】的基础之上完成的,但是在内容上并未完全包括前者,感兴趣的朋友在那篇文章上可能能找到一些有趣的原始信息。由于时间关系,本文在此主题上的探讨仍显粗浅,对此只能说抱歉了。


十 参考文献
【1】了解Check Point FW-1状态表
http://magazine.nsfocus.com/detail.asp?id=538
【2】A Stateful Inspection of FireWall-1
http://www.dataprotect.com/bh2000/
【3】Linux IPCHAINS-HOWTO
http://www.linuxdoc.org
【4】防火墙新生代:Stateful-inspection
http://www.liuxuan.com/safe/anquan/html/firewall/04.htm
【5】netfilter站点上的文档
http://netfilter.kernelnotes.org
【6】Application Gateways and Stateful Inspection:A Brief Note Comparing and Contrasting
http://www.avolio.com/apgw+spf.html
【7】Internet Firewalls:Frequently Asked Questions
http://www.interhack.net/pubs/fwfaq
【8】Writing a Module for netfilter
http://www.linux-mag.com/2000-06/gear_01.html
【9】ipchains的源代码分析
http://www.lisoleg.net/lisoleg/network/ipchains.zip
【10】内核防火墙netfilter入门
http://magazine.nsfocus.com/detail.asp?id=637
【11】Check Point Firewall-1 on Linux, Part Two
http://www.securityfocus.com/frames/?focus=linux&content=/focus/linux/articles/checkpoint2.html
【12】防火墙技术分析讲义
http://bj.aka.org.cn/Lectures/003/Lecture-3.1.1/Lecture-3.1.1/firewall.txt
【13】IP分片重组的分析和常见碎片攻击 v0.2
http://magazine.nsfocus.com/detail.asp?id=584
【14】利用LLKM处理网络通信---对抗IDS、Firewall
http://security.nsfocus.com/showQueryL.asp?libID=431

 

 

上一篇:Linux 文件访问原语   下一篇:The Linux Kernel Sources(Linux核心源程序)
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论
  热点文章
·Linux常用基本命令及应用技巧
·写得蛮好的linux学习笔记
·学会在Linux下对硬盘分区
·找回Redhat的超级用户密码
·Linux下C语言编程
·GDB教程
·Cron服务配置详解
·Linux与Unix二大操作系统编程的
·Linux 2.6 内核的嵌入式系统应用
·Linux和Windows系统调用的比较
·vim命令(一)
·vim 命令(二)
  相关文章
·Linux 文件访问原语
·The Linux Kernel Sources(Linu
·Linux动态函式库解析
·Linux下的多进程编程初步
·Linux环境下的网络编程
·Linux 的编程常识
·LINUX VMWare-TOOLS的安装
·Linux教程(19)-TurboLinux Shell
·DLINK-DSL500-ADSL-初探(四)-
·Linux Data Structures(Linux数
·DLINK-DSL500-BusyBox on dsl-ga
·Linux教程(21)-标准输入/输出和
  相关信息
copy right @ 百家拳软件项目研究室 2007 辽ICP备07011763