摘要
随着互联网的飞速发展,网络安全问题也变得越来越突出。包过滤防火墙技
术作为保护网路安全的的一种技术,其应用十分广泛。所谓包过滤就是对流经网
络防火墙的所有数据包进行逐个的检查,并依据设置好的防火墙过滤策略来决定
每个数据包是通过还是不通过。
在此提出了一种基于 linux 系统 netfiter 内核模块的包过滤防火墙的策略
设计与实现方法,其功能设计实现是仿照 linux 系统中的 iptables 和 netfiter
实现的。借助于 netfilter 的数据包过滤功能框架,本文对 linux 包过滤防火墙
的访问控制策略进行了研究、设计以及代码实现。其中最常用的过滤策略包括:
IP 过滤,端口过滤以及协议过滤,在 netfiter 内核模块中,系统定义了五个钩
子函数,在开发防火墙时,可以在相应的钩子点注册自己的处理函数,然后设置
一些规则,当网络数据包到达内核协议栈时,会进入到程序里设定的处理函数,
将每一个数据包的 ip 地址,端口,协议等信息与规则进行匹配,若匹配成功,则
按照规则设置的策略对数据包进行处理。
关键字:网络安全 防火墙 包过滤技术 Linux
netfilter
设计目的
随着互联网的迅速发展,各种网络应用软件层出不穷,一些网络新业务也开
始兴起,比如电子商务、数字货币、互联网络银行等,人们的生活和学习对网络
的依赖越来越多,但问题也接踵而来,比如,网站遭到攻击,网上的个人重要信
息被窃取,病毒泛滥等等,所以网络安全问题越来越受到人们的重视。目前,网
络互连一般都基于 TCP/IP 协议,TCP/IP 协议在制订之初,并没有把安全考虑在内,
所以协议中存在很多的安全问题。为解决这一问题,人们想出了两种办法,一种
是重新制定出一个更安全的网际互连协议,比如现在正在研究的 IPv6,另一种方
法就是采用网络安全技术,比如密码技术,防火墙技术等等。其中,防火墙作为
最早出现的网络安全产品和使用量最大的安全产品,受到了广大用户和研发机构
的青睐,在内网和外网之间设置防火墙已被我们广泛的应用于实践,它也被证明
是一种行之有效的方法。因此,研究防火墙的包过滤技术,无论从理论上还是实
际应用上都具有十分重要的意义。
防火墙简介
所谓防火墙指的是一个由软件和硬件设备组合而成、在内部网和外部网之间、
专用网与公共网之间的界面上构造的保护屏障.是一种获取安全性方法的形象说
法,它是一种计算机硬件和软件的结合,使 Internet 与 Intranet 之间建立起一
个安全网关(Security Gateway),从而保护内部网免受非法用户的侵入,防火
墙主要由服务访问规则、验证工具、包过滤和应用网关 4 个部分组成,防火墙就
是一个位于计算机和它所连接的网络之间的软件或硬件。该计算机流入流出的所
有网络通信和数据包均要经过此防火墙。
在网络中,所谓“防火墙”,是指一种将内部网和公众访问网(如 Internet)
分开的方法,它实际上是一种隔离技术。防火墙是在两个网络通讯时执行的一种
访问控制尺度,它能允许你“同意”的人和数据进入你的网络,同时将你“不同
意”的人和数据拒之门外,最大限度地阻止网络中的黑客来访问你的网络。换句
话说,如果不通过防火墙,公司内部的人就无法访问 Internet,Internet 上的
人也无法和公司内部的人进行通信。
用一句简短的话来描述便是:防火墙是一种位于内部网络与外部网络之间的
网络安全系统。
防火墙的种类
防火墙从诞生到现在,已经历了四个发展阶段:基于路由器的防火墙、用户
化的防火墙工具套、建立在通用操作系统上的防火墙、具有安全操作系统的防火
墙。目前常见的防火墙属于具有安全操作系统的防火墙,例如 NETEYE、
NETSCREEN、TALENTIT 等。
从结构上来分,防火墙有两种:即代理主机结构和路由器+过滤器结构,后
一种结构如下所示:内部网络过滤器(Filter)路由器(Router)Internet
从原理上来分,防火墙则可以分成 4 种类型:特殊设计的硬件防火墙、数据
包过滤型、电路层网关和应用级网关。安全性能高的防火墙系统都是组合运用多
种类型防火墙,构筑多道防火墙“防御工事”。
Linux 防火墙分析
Netfilter 框架
在 Linux 操作系统上,有一个成功的网络数据过滤框架——netfiter,于是基
于这一框架,仿照 Linux 系统的 iptables 所设计的一个简单的包过滤防火墙。
基于 Linux 系统 Netfilter 框架构造防火墙, Netfilter 是 Linux 核心中一个抽
象、通用架构,用于扩展各种服务的结构化底层服务。Netfilter 组件也称为内核
空间(kernel space),是内核的一部分,由一些数据包过滤表组成,这些表包含内
核用来控制数据包过滤处理的规则集。Netfilter 是 Linux 实现的防火墙框架,
Netfliter 框架提供了一系列的“表”(tables),每个表由若干“链”(chains)组成,其实
质是处理数据包时,提供的一系列的检查点,又称为钩子函数。而每条链可以由
一条或数条“规则”(rules)组成,对每一个流经的数据包,Netfilter 根据其流动方向,
会利用相应的钩子函数按照规则进行检查。Netfilter 在 IPV4 中定义了 5 个检查点,
其位置如图 1 所示。方框为检查点,箭头表示数据包的流向。如下图所示:
NF_ INET _PRE_ROUTING
ROUTE
NF_INET_FORWARD
NF_IP_POST_ROUTING
网络数据包
网络数据包
NF_INET_LOCAL_IN
ROUTE
NF_INET_LOCAL_OUT
本地网络数据
数据包进入系统,在第一个检查点 NF_INET_PRE_ROUTING 进行处理,然后进入
路由选择,判断数据包是需要转发还是发给本机的。若该数据包是发往本机的,则
该数据包经过检查点 NF_INET_LOCAL_IN 处理后传递给上层协议;若该数据包应该被
转发则它被 NF_INET_FORWARD 处理, 经过转发的数据包经过最后一个检查点
NF_INET_POST_ROUTING 处理后,再传输到网络上。本地产生的数据包经过检查点
NF_INET_LOCAL_OUT 处理后再进行路由选择处理,然后经过 NF_INET_POST_ROUTING
处理后发送到网络上。上述的五个检查点具体含义如下:
NF_INET_PRE_ROUTING,在报文作路由以前执行
NF_INET_FORWARD,在报文转向另一个 NIC 以前执行
NF_INET_POST_ROUTING,在报文流出以前执行
NF_INET_LOCAL_IN,在流入本地的报文作路由以后执行
NF_INET_LOCAL_OUT,在本地报文做流出路由前执行
数据包处理过程中,会检查相应的钩子函数在链表中是否有钩子函数注册,如
果注册了该钩子函数则监听数据包,调用该钩子函数,并根据钩子函数返回的结果
决定下一步的动作。钩子函数的返回结果值有 5 个值,分别如下:
NF_DROP:通知内核丢弃该数据包
NF_ACCEPT:通知内核接受该数据包,继续其旅途
NF_STOLEN:通知内核忘掉该数据包,有本模块接管
NF_QUEUE:通知内核将该数据包传递到用户空间
NF_REPEAT:通知内核再次调用该hook函数(需要防止死循环)
防火墙采用 Netfilter 的 5 个钩子实现,选取 Netfilter 5 个钩子中的 3 个作为实
现防火墙网络数据截取的基础: NF_INET_LOCAL_IN , NF_INET_LOCAL_OUT ,
NF_INET_FORWARD,分别对应于防火墙的 INPUT、OUTPUT 和 FORWARD 链。当在
上述的 3 个钩子上截取了网络数据的时候,可以查找网络数据上的内容在不同的链
上进行规则的查找。如果找到匹配的规则,则进行规则制定动作的处理。
用户空间与内核空间的通信
防火墙要能与用户交互,必须有一种适合用户空间和内核空间交互的方式。在
Linux 中内核空间和用户空间进行交互的方法有 sysctl()方法、ioctl()方法,PROC 方
法、文件读写的方法以及网络的方法等等。早期的 iptables 框架采用了网络框架中
的 setsocket()/getsockopt()的方法来实现用户空间和内核空间的通信,现在 iptables
采用了 netlink()方法。
该防火墙采用 netlink 框架的方法实现用户空间和内核空间的通信。netlink 框
架用于实现用户命令行的交互,即用户的命令行交互使用 netlink 来实现,将用户的
命令设置发送到内核。示意图如下
用户空间
内核空间
创建 netlink_socket
绑定到本进程
填充数据
调用 send_to 发送信息
调用 recv_from 接收信息
关闭 socket
定义接收回调函数
创建 socket 套接字,并注
册回调函数
回调函数接收
广播或单播
释放 socket
netlink 是一种特殊的 socket,它是 Linux 所特有的,是一种在内核与用
户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket
API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来
使用 netlink, netlink 提供了一种异步通讯方式,与其他 socket API 一样,
它提供了一个 socket 队列来缓冲或者平滑瞬时的消息高峰。发送 netlink 消息
的系统调用在把消息加入到接收者的消息对列后,会触发接收者的接收处理函数。
接收者在接收处理函数上下文中,可以决定立即处理消息还是把消息放在队列中,
在以后其它上下文去处理它(因为我们希望接收处理函数执行的尽可能快)。系统
调用与 netlink 不同,它需要一个同步的处理,因此,当我们使用一个系统调用
来从用户态传递消息到内核时,如果处理这个消息的时间很长的话,内核调度的
粒度就会受到影响。
总体框架及工作过程
防火墙的总体架构分为两部分:内核空间部分的主要处理模块和用户空间部
分的交互控制用户接口。内核模块主要有网络数据的过滤,防火墙过滤规则的增
删。 用户空间部分主要处理用户输入命令格式的解析,用户空间与内核空间的
通信。
内核模块中的钩子函数是防火墙网络数据的主要处理部分,按照用户定义的
过滤规则,对通过 Netfilter 架构上的 INPUT、OUTPUT、FORWARD 这 3 个监视点
的网络数据进行过滤,目前实现 DROP 和 ACCEPT 两种处理方式。网络数据进入
网络协议栈之后, Netfilter 架构有 5 个钩子函数,用户调用插入的回调函数,
当用户设置了回调函数之后,就会进入用户的处理过程。该防火墙选择了上述 3
个,对进入本地、从本机发出、从本机转发的数据进行处理。
内核中与用户的通信采用了 Netfilter 的框架进行处理。内核建立了一个
私有的 netlink 通信类型,与用户的通信通过此类型的套接字进行处理。此模
块中用于处理用户对过滤规则的增加等操作。当模块接收到用户空间发来的数据
后,根据其操作方式对过滤规则链表中的规则进行增加等操作。
用户空间主要用于与防火墙进行交互,通过 netlink 网络协议与防火墙进行
规则的设置等操作,用户空间主要分为命令行解析和与内核通信。命令行主要将
用户输入的过滤规则转换成易于传送的数据格式。通信部分主要负责给内核发送
规则数据。
内核模块在启动时进行初始化工作,建立好 netlink 通信后监听用户交互数
据以及网络数据的到来。用户的 netlink 部分和内核部分的 netlink 部分进行交
互,对防火墙的过滤规则进行处理。钩子函数则会对网络数据进行处理。
用户命令格式
用户输入命令格式如下:
操作 链名称 源 ip 源端口 目的 ip 目的端口 协议类型 动作名称
用户输入的命令行参数和含义:
命令字段
Command
chain
Sip
Sport
Dip
Dport
Proto
Act
参数含义
规则操作类型
链名称
源 IP
源端口
目的 IP
目的端口
协议
对包的处理动作
防火墙所支持的链:
防火墙支持进入、发出、转发 3 个链:
含义
链名称
IUPUT
OUTPUT
FORWARD
发往本机的网络数据
从本机发出的网络数据
通过本机转发的网络数据
类型
Int
Int
Unsigned long
Unsigned short
Unsigned long
Unsigned short
Int
Int
类型
Int
Int
Int
防火墙所支持的命令:
命令名称
含义
值
命令选项
APPEND
FLUSH
向某个链尾插入规则
删除所有规则
0
4
-A
-F
防火墙所支持的协议:
防火墙支持根据网络数据的协议进行过滤,支持 TCP、UDP 两种协议类型:
协议名称
TCP
UDP
含义
TCP 协议
UDP 协议
值
IPPROTO_TCP
IPPROTO_UDP
防火墙所支持的动作:
防火墙的支持通过和丢弃两种动作,其中 ACCEPT 表示让网络数据正常通过,
不对数据进行处理,DROP 表示丢弃网络数据:
动作名称
DROP
ACCEPT
含义
丢弃网络数据
让网络数据通过,不进行处理
值
0
1
部分模块代码
SK_BUFF 结构体
网络数据存放在 skb 中,它是一个 struct sk_buff 结构,sk_buff 是一个
复杂的双向链表,在这个结构中有 next 和 prev 指针,分别指向链表的下一个节
点和前一个节点。。并且为了某些需求需要很快定位到链表头部,所以还有一个
指向链表头部的指针 list。
sk_buff_head 结构是:
struct sk_buff_head
{
};
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev; __u32 qlen; //代表元素节点数目
spinlock_t lock; //加锁,防止对表的并发访问
IP 头
通过 struct sk_buff 结构可以的 nh 枚举类型的 iph 成员变量,可以获得
IP 的头部结构,通过 IP 的头部结构可以获得网络数据的源 IP 地址,目的 IP 地
址和协议类型。
struct iphdr 结构的原型如下:
struct iphdr
{
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix
"
#endif
__u8 tos;
__be16 -tot_len;
__be16 -id;
__be16 -frag_off;
__u8 ttl;
__u8 protocol;
__be16 -check;
__be32 -saddr;
__be32 -daddr;
};
各字段含义:
iphdr->version 版本(4 位),目前的协议版本号是 4,因此 IP 有时也称作
IPv4。
iphdr->ihl 首部长度(4 位):首部长度指的是 IP 层头部占 32 bit 字的数目
(也就是 IP 层头部包含多少个 4 字节 -- 32 位),包括任何选项。
iphdr->tos 服务类型字段(8 位)
iphdr->tot_len 总长度字段(16 位)是指整个 IP 数据报的长度,以字节为单
位。利用首部长度字段和总长度字段,就可以知道 IP 数据报中数据内容的起始
位置和长度。
iphdr->id 标识字段(16 位)唯一地标识主机发送的每一份数据报。通常每
发送一份报文它的值就会加 1。
iphdr->frag_off (16 位) frag_off 域的低 13 位 -- 分段偏移(Fragment
offset)域指明了该分段在当前数据报中的什么位置上。
iphdr->ttl TTL(time-to-live) -- 8 位,生存时间字段设置了数据报可以
经过的最多路由器数。它指定了数据报的生存时间。
iphdr->protocol 协议字段(8 位): 根据它可以识别是哪个协议向 IP 传送
数据。
iphdr->check 首部检验和字段(16 位)是根据 IP 首部计算的检验和码。