Netfilter中的连接跟踪系统

呵呵,第一次翻译技术文章,还挺大家见谅。原作连接:netfilter tracking system难过


基于单一的报文头信息的过滤策略已经过时了。如今有状态防火墙提供了一种高级机制,让系统管理员和安全专家可以定义更智能的策略。本文介绍了Netfilter项目包含的连接跟踪系统的实现细节,还介绍了一些需要的理解的背景知识。在最近的linux内核中,本文对于理解有状态防火墙子系统很有帮助。

Netfilter框架

Netfilter 项目是由Paul“Rusty”Russell在2.3.x系列开发过程中建立的。当时linux上的防火墙工具有一些缺点就是需要fullrewrite。Rusty决定从头开始,他创建一套Netfilter框架,这套框架由一些列构建在linux内核网络协议栈中的钩子Hook组成。用着套钩子,你可以注册不同的内核模块,来完成在各个阶段的各种网络包处理。

通常,人们把最流行的linux网络防火墙工具Iptables和Neffilter框架本身混淆。这个因为Iptable中的链概念和Netfilter中的钩子概念有相同的命名。但是Iptable只是Netfilter框架大厦之上的一块砖罢了。

幸运的是,Rusty花了相当多的时间学写了文档让人们容易去了解这个框架[1]。但是有时候你还得去看代码一边作进一步的了解。

 

钩子和回调函数

Netfilter在linux网络协议栈中插入了5个钩子函数来完成不同阶段的报文处理[图1]。这些钩子是:


PREROUTING:毫无疑问,所有的报文在路由决定之前和IP头健康检查之后,都会经过这个钩子。端口地址转换(NAPT)和重定向,也就是DNAT,就在这个钩子上实现。

LOCAL INPUT:所有到达本地的报文都会经过这个钩子。这也是进入本地路径上的报文流量经过的最后一个钩子。

FORWARD:不到本地机器上的报文(例如,正在经过防火墙的报文)会经过过这个钩子。

LOCAL OUTPUT:这个出报文路径上的第一个钩子。离开本地的报文也会经过这个钩子。

POSTROUTING:在路由决定之后实现了这个钩子。SNAT就是注册到这个钩子上的。所有离开本地的报文也会经过这个钩子。

因此,我们按照报文的目的地,可以得到三种报文流量:

1, 流经防火墙的报文,也就是不到本机的报文。这些报文按照如下路径走过协议栈:PREROUTING,FORWARD,POSTROUTING。

2, 进入防火墙的报文,例如到达本机的报文。这些报文按照如下路径走过协议栈:PREROUTING,INPUT。

3, 从防火墙出去的报文。这些报文按照如下路径走过协议栈:OUTPUT,POSTROUTING。

你可以在一个给定的钩子上注册一个回调函数在。这个回调函数的原型定义在netfilter.h头文件的nf_hook_ops结构中。这个结构包含一些注册回调函数的钩子的信息,优先级信息等。因此你可以注册一个到多个回调函数到一个钩子上。这个优先级指定了那个回调函数首先被调用。这个注册过程由nf_register_hook()函数完成。

回调函数可以返回如下的一些值:

1, ACCEPT:  让和这个报文继续流经协议栈。

2, DROP: 默默的丢弃这个报文。

3, QUEUE: 通过nf_queue把报文送报用户空间。这样用户空间的程序可以来处理这些报文。

4, STOLEN: 临时阻止报文在内核协议栈中旅行。这个通常来收集IP分片。

5, REPEAT:强制报文重新进入钩子。

总的来说,这个框架给我们提供了一种方法来注册回调函数,然后通过回调函数来处理在任何阶段的各种报文。然后这个返回值将会被这个框架按照某种策略来处理。

在这一点上,那如果你感觉信息不够,需要了解更多的关于linux网络协议栈的背景,你可以看一些关于报文流经协议栈的文档[2]。

 

连接跟踪系统和状态检测

以前报文过滤策略只能单一的依靠报文头信息,如IP源地址、目的地址、和端口。这种方法很难解决探测攻击和DOS攻击。

幸运的是,如今管理员不用找借口说他们的防火墙墙不能完成有状态过滤。有一些可行的开源代码可以用在他们的产品环境中。在linux上,这种特性已经存在于Neftilter项目之初。连接跟踪系统是构建在Netfilter框架大厦之上的另一块砖。

连接跟踪系统在一个内存结构里面存储了连接状态的基本信息。这些信息包括源、目的IP地址、端口号对、协议类型、状态和超时时间。用这些额外的信息,我们可以定义一个智能的过滤策略。

除此之外,一些应用协议,如FTP、TFTP、IRC和PPTP,如果按照传统的静态过滤方法,是很难用防火墙来跟踪的。连接跟踪系统定义了一种机制来跟踪这种协议,我们将在下面来描述。

连接跟踪系统本身不会过滤报文。虽然有一些异常会让报文丢掉(如,内存耗尽),但是他们的默认行为是让这些报文继续在内核网络协议栈中旅行。因此请记住,连接跟踪系统只是跟踪报文,并不过滤报文。

 

状态

一个连接的可能状态可以被定义如下:

1, NEW:一个新建的连接。这个状态表明这个报文是有效的,假如防火墙只看到一个方向上的流量(如,防火墙没有看到任何回复报文),那么这个连接就处于NEW状态,也就是这个报文属于一个有效的初始化序列(如,在一个TCP连接中,收到一个SYN报文)。

2, ESTABLISHED:连接已经被建立了。换句话说,如果连接处于这个状态,就表明防火墙看到了一个双向的通信。

3, RELATED:这是一个期望的连接。这个状态将在后面的“Helpers和Expectation”章节中详细介绍。

4, INVALID:这是一种特殊的状态,来描述那些没有按照预期行为进行的连接。系统管理员可定义一些规则来记录这些报文 或者丢掉这些报文。正如前文所讲,连接跟踪系统不会过滤报文,但是提供了一种方法去过滤报文。

正如你所注意到的,如果按照以上所描述的方法,即使一些无状态的协议,如UDP,也是有状态的。当然,这里的状态和TCP的状态是没有关系的。

 

大图

 

本文将把重点放在3层独立连接跟踪实现nf_conntrack,基于IPv4独立ip_conn_track,这个特性始自linux2.6.15内核。对具体的IPv4和IPv6协议的实现分别在nf_conntrack_ipv4和nf_conntrack_ipv6模块中。

四层协议支持也分别实现在不同的模块中。目前,有一些内嵌的分别支持TCP、UDP、ICMP和可选的SCTP。这些协议的处理按照具体的四层协议,以确保连接跟踪的正确定和无差错性。

nf_conntrack_ipv4模块注册了四个回调函数(图1)在不同的钩子上。这些回调函数在nf_conntrack_core.c文件中,然后接受三层协议的参数,因此基本上IPv6模块也是相同的。这些回调函数可以被分成3类:conntrack创建和查询、分片报文重和help。nf_conntrack_ipv6模块将不再本文档中描述,因为它属于IPv4模块的变体。

 

实现问题

基本的结构

虽然连接跟踪系统对NAT子系统来说是必须的,但是在模块加载子系统中属于一个可选的模块。它由一个哈希表(图2)来实现已完成高效的查询。每一个bucket有一个双向的hashtuple链表。每一个连接有两个hashtuple:一个是源方向(如,发起连接方向的报文);一个是回复方向(如,到发起连接方向的回复报文)。


一个tuple代表了一个连接的的相关信息,IP源和IP目的,还有4层的协议信息。这样的tuple被嵌在一个 hash tuple中。两个结构定义在nf_conntrack_tuple.h文件中。

这两个hashtuple嵌在nf_conn结构中,从这个角度出发,可以作为一个conntrack前向引用,这个结构存储了一个给定连接的状态。因此一个conntrack是两个hashtuple的容器,每一个hashtuple是一个tuple的容器。这导致了一个三层嵌套的结构。

一个哈希函数用来计算代表一个连接的hashtuple所在的位置。这个计算过程接受的参数涉及到三层和四层协议相关的信息。目前使用的hash函数是Jenkins’hash[3]。

哈希计算通过一个随机数来避免潜在的性能下降,一些恶意的用户用hash炸弹会导致一个很长的hashtuple链。然而conntrack表有一个对conntrack数目的最大限制。当这个conntrack表满了的时候,会按照LRU算法来删掉一个conntrack。Conntrack表的大小在模块加载或者启动时是可以调整的。

 

Conntrack创建 和查询

nf_conntrack_in回调函数注册在PREROUTING钩子上。一些健康检查在这个阶段来完成,以确保报文的正确性。随后将是conntrack查询。这个子系统试图查找一个conntrack以匹配接受的报文。假如没有conntrack找到,这个conntrack将被创建。这个机制在resolve_normal_ct函数中实现。

假如这个报文属于一个新的连接,这个刚创建的conntrack将标志confirmed置0。如果这个conntrack已经在这个哈希表中,confirmed标志会被置1。这就是说,在这一点上,没有插入新的conntrack。一旦一个报文成功的离开这个框架(例如,当报文到达最后一个钩子而没有被丢掉),这样的插入操作就会发生。一个报文和一个conntrack之间是借助一个指针来联系的。假如这个指针是空指针,这个报文就属于一个无效的连接。Iptable允许我们放弃对这种报文的跟踪。在这种目的之下,一个将会使用一个假的conntrack。

总结一下,回调函数nf_conntrack_confirm被注册在LOCALINPUT和POSTROUTING钩子上。正如你所看到的,这个是分别到达本地流量和转发流量的路径中的最后两个钩子。这个confirmation过程就发生在这个点上:在哈希表中杀入conntrack,就设置confirmed标志,然后激活关联的计时器。

分片重组报文处理

这个工作有回调函数ipv4_conntrack_defrag来完成,这个函数来收集需要分片重组的报文。一旦他们被成功接收,分片将通过协议栈继续旅行。

在2.4内核分支中,分片重组的报文是线性化的,也就是说,他们被拷贝到相邻的内存区域中。然而,在2.6内核分支中优化了这个而外的处理开销:这些分片不在拷贝到一个线性区域;取而代之的是,他们被组织到一个链表中。因此所有的处理必须要考虑到分片。例如,假如我们需要一些存储在TCP报文头中的信息,我们必须检查这个头是不是被分片了;假如是的,只要拷贝需要的信息到栈里面就可以了。如果报文需要分片重组,这不是一个很麻烦的问题,因为有一些很好用函数,如skb_header_pointer,他们会考虑分片并且只线性化需要的数据部分。除此之外,报文头检查不会导致任何处理损失。

 

Helpers和expectation

一些应用层协议是很难跟踪的。如FTP协议的被动模式使用21端口来作控制操作去请求服务器传输数据,但是它使用1024和65535之间的TCP端口号来接受数据,而不是使用传统的TCP20端口。这意味着这两个独立的连接内部是有关联的。因此,防火墙需要额外的信息来成功过滤这一类的协议。

连接跟踪子系统定义了一种机制叫做helpers,它让系统来标示一个连接是否和已存在的连接相关。为了完成这个目标,它定义了一个概念叫做expectation。一个expectation是一个在某个时间段期望发生的连接。他作为一个nf_conntrack_expect结构定义在nf_conntrack_core.h头文件中。

Helper会在包含这类协议的数据包中搜索一个pattern集合。在FTP中,当请求开启被动模式的连接时,helper会查找端口pattern(如,PASV 方法)。假如找到这个pattern,一个expectation就会被创建,然后插入到全局的expectation链表中(图3)。因此,helper定义了一种期望的可能连接的描述。  


一个expectation有一个有限的生存期。假如一个conntrack被创建,连接跟踪系统就会搜索匹配的expectation。假如没有找到匹配的expectation。他将会为这个连接查找一个helper。

当系统匹配到一个expectation时,和这个新的conntrack相关的主conntrack就会创建一个expectation。例如:在FTP被动模式下,这个表示到21端口的流量(控制流量)的conntrack就是主conntrack(masterconntrack),表示数据流量的conntrack(如,到高端口的流量)就是和这个主conntrack相关的conntrack。

可以通过nf_conntrack_helper_register来注册一个helper,他会添加一个nf_conntrack_helper结构到一个helper链表中去。

 

结论和展望

Netfilter的连接跟踪系统并不是a  piece of software stuck in time 。现存的实现方式有相当多的地方可以改进。如,一些地址选择的部分可以用哈希表树来替换现有的哈希表方法,一些性能方面的测试也可以得到改进。

幸运的是,本文表述的这个子系统并不是访问内核的唯一方法。有一个用户空间的库libnetfilter_conntrack可以系统一些API用来访问内核的连接跟踪状态表。

关于helper,支持英特网电话的协议如H.323和VoIP正在开发之中。此外,正如Rusty在Netfilter项目早期期待的那样,可以添加一些比较合适的机制允许人们在用户空间来实现他们自己的协议的helper。


参考

[1] Paul Russel and Harald Welte, “Netfilter Hacking How-to”:http://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO.txt.

[2] Miguel Rio et al., “A Map of the Networking Code inLinux Kernel2.4.20,” Technical Report DataTAG-2004-1, FP5/IST DataTAGProject,2004.

[3] Bob Jenkins, “A Hash Function for Hash TableLookup”:http://burtleburtle.net/bob/hash/doobs.html.

[4] 4th Netfilter Workshop, October 2005:

http://workshop.netfilter.org/2005/.

[5] Martin Josefsson, “Hashtrie: An Early Experiment,”October 2005:

http://workshop.netfilter.org/2005/presentations/martin.sxi.



转载于:https://my.oschina.net/stz/blog/726049

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值