1. 引言 状态检测是目前防火墙系统必备的功能之一。它工作在IP层,检查由防火墙转发的包,并创建相应的结构记录连接的状态。它的检查项包括链路层、网络层、传输层、应用层的各种信息,并根据规则表或状态表来决定是否允许转发包通过。 开源项目SIFI(网址http://www.ifi.unizh.ch/ikm/SINUS/)实现了一个包含状态检测功能的防火墙。这个防火墙内核部分的代码是建立在Linux2.2.x网络安全框架的之上,是一个很好的分析、学习状态检测技术的实例。下面将从数据结构、状态检查、序列号检查、超时、应用协议数据检查等几个方面分析它的状态检测是如何实现的。 2. 概况 首先看看SIFI中的几个重要的数据结构。 2.1. sf_fw_ops SIFI的内核代码是在LINUX2.2.x网络安全框架之上实现的,所以它要定义在检查点上引用的结构,如下: strUCt firewall_ops sf_fw_ops = { NULL, sf_forward_chk, sf_input_chk, sf_output_chk, PF_INET, 2 } 结构中sf_forward_chk,sf_input_chk,sf_out_chk三个函数分别在检查点call_fw_firewall,call_in_firewall,call_out_firewall上被调用。这三个函数都调用函数sf_check_packet去完成真正的动作。这个结构的优先级是2,高于LINUX2.2.x内核中定义的ipfw_ops的优先级,所以在检查点上,先调用这个结构中定义的函数。只有在 它的函数的返回值是FW_SKIP的情况下才会调用ipfw_ops中定义的函数。 2.2. 规则表 规则表是一个单向链表,用于匹配需要处理的包,并决定匹配后的动作。如图2.1所示。 2.3. 连接表 连接表是一个哈希表,相同哈希值的结构被链接成一个双向链表。所有的连接结构又被链接成一个双向链表,指针conns指向链表的头,lastc指向链表的尾。如图2.1所示。 2.4. 状态定义 连接表中的每一个结构在某一刻都有一个确定的状态。在SIFI中,只为TCP协议和UDP协议创建连接结构。其他协议的检查都通过匹配规则表实现的,没有创建连接结构。状态的定义如下: [表2.1] 表中用灰色标识的状态在状态检查时没有使用。 SF_TCP_ESTABLISHEDFTP状态是为FTP协议的控制连接定义的状态,定义这个状态的目的是为检查FTP协议,找出其中的地址和端口,并创建新的连接结构,使FTP的数据连接在没有相应规则的情况下也可建立起来。其他状态定义了TCP连接变迁的过程中的合法状态,其状态转换过程将在下面讲到。 3. 基本流程 接下来看看SIFI对不同协议包的处理。 对ICMP协议或IGMP协议的包,它检查包的长度,并在规则表中查找,如果找到匹配的规则,执行规则所规定的动作;如果没有找到匹配的规则,默认禁止这个包。它没有记录ICMP或IGMP协议的状态。 对UDP协议的包按如下的流程检查: [图3.1] 首先在连接表中查找,如果找到,则说明此方向的UDP通信是规则允许的;如果没有找到,则在规则表中查找,如果有匹配的规则,执行规则所规定的动作,如果没有找到匹配的规则,默认禁止这个包。规则检查时,如果匹配到的规则允许这个包通过,将在连接表中加入一个新的连接结构,其状态值是SF_UDP_STATE,后续的包将不再进行规则检查,直到这个结构被删除。值得注意的是,对UDP的包在检查连接表时用的是单向的匹配,这于对TCP的处理不同。 对TCP协议的包按如下的流程检查: [图3.2] 首先在连接表中查看是否有相应的连接结构。这里的查找使用的是双向匹配(将目的地址、目的端口和源地址、源端口互换),所以一个TCP连接对应一个连接结构。如果找到相应的连接结构,则进行状态变迁(状态如何变迁将会在后面介绍);如果没有找到,则查看这个包是否是syn置位,但ack没有置位的包(TCP发起连接的包),如果是,用源端口为零计算哈希值,在连接表中查找相应的结构(这个结构是在处理应用协议数据中的动态地址和动态端口时创建的,与地址转换中的处理类似),如果找到,删除原来源端口为零的结构,并用这个包的地址和端口重新计算哈希值,创建新的结构;如果不是syn置位而ack没有置位的包,则禁止包通过。 4. 状态检查 4.1. 状态检查 状态检查定义了TCP连接变迁过程中的合法状态,以及在某个特定状态下允许通过的包的类型。状态检查同时对允许通过的包做合法性检查,比如确定其序列号是否符合TCP协议的规定等。 各状态之间的变迁如图所示: [图4.1] 连接结构的初始状态是SF_TCP_ACCEPT_SYN,这是在syn置位而ack没有置位的TCP包在规则检查的返回值是FW_ACCEPT时创建。结构创建之后,进入创建连接的状态检查。步骤如下: (1) 如果是CLIENT(连接发起方)的syn置位而ack没有置位的包,连接状态变迁到SF_TCP_CLIENT_SYN。 (2) 在SF_TCP_CLIENT_SYN状态下,如果SERVER(连接接受方)回应syn置位或syn/ack置位的包,连接状态变迁到SF_TCP_SYN_ACK。在这个状态下同样允许CLIENT的syn置位或syn/ack置位的包,连接状态保持不变。 (3) 在SF_TCP_SYN_ACK状态下,如果CLIENT回应有ack置位或syn/ack置位的包,连接状态变迁到SF_TCP_ESTABLISHED3。在这个状态下,同样允许SERVER的syn/ack置位的包,连接状态保持不变,但是不允许SERVER的syn置位和CLIENT的syn置位的包通过。 以上是SF_TCP_ACCEPT_SYN、SF_TCP_CLIENT_SYN、SF_TCP_SYN_ACK三个状态下对syn或ack置位的包的检查。这个检查支持TCP连接的同时打开,但前提是规则要允许。 关闭连接的状态检查按如下的步骤进行: (4) 如果是CLIENT的fin置位的包,连接状态变迁到SF_TCP_CLIENT_FIN。 (5) 如果是SERVER的fin置位的包,连接变迁到SF_TCP_SERVER_FIN。 (6) 以上两步的变迁如果都已完成,连接的状态是SF_TCP_TERMINATED或是大于SF_TCP_TERMINATED( 如果是为FTP控制连接创建的结构)。 (7) 在状态大于等于SF_TCP_TERMINATED时,如果收到syn置位的包,将删除连接,并检查规则。 在任何状态下如果收到rst置位的包,会删除连接。并且,如果收到syn、fin置位或syn、rst置位或fin、rst置位的包(圣诞树数据包),将禁止这个包通过。 4.2. 超时 在每个非ESTABLISHED的状态上都设置了相应的超时值,如下表: [表4.1] 超时通过内核定时器实现,超时连接将被删除。SF_TCP_ESTABLISHED3,SF_TCP_ESTABLISHEDFTP两个状态上缺省没有定义超时值,但是可以通过定义SF_TCP_IDLE编译选项,使这两个状态的超时值为8 * 3600* HZ,是8小时。 4.3. 序列号检查 序列号是TCP协议用于保证数据可靠性的重要手段。TCP连接中传递的每一个字节的数据都用一个序列号来标识。连接的初始序列号在建立连接时协商。收到包的一方会发出ack置位的应答包,告诉对方自己接受的下一个序列号是多少,这样就确认了下一个序列号之前所收到的数据。TCP协议同时使用序列号窗口来限制对方可以发送的数据包的长度,以控制流量。窗口大小是一个16位的正整数,可以通过TCP的scale选项扩展窗口的大小。使用scale选项,在计算窗口大小时会将窗口值左移以增大窗口的值。 TCP协议中协商初始序列号的syn置位的包占用一个序列号,断开连接的fin置位的包占用一个序列号。SIFI通过检查转发包的序列号来确定这个包是否合法。在SIFI的实现中,可以通过配置参数检查每一个TCP连接的序列号,其步骤如下: (1) 对没有syn置位的包(fin或ack置位的包),先确定发送方的最大序列号在对方的窗口之内,然后如果是ack置位的包,其确认序列号应在对方的序列号之后。 (2) 在SF_TCP_CLIENT_SYN状态时记录CLIENT的序列号、窗口大小和缩放因子。在SF_TCP_SYN_ACK状态时记录SERVER的序列号、窗口大小和缩放因子,并将CLIENT的序列号增加1。 (3) 在SF_TCP_TERMINATED状态时,如果收到syn置位的包,它的序列号应在本方序列号之后。 (4) 在SF_TCP_SYN_ACK状态时,SERVER的重复的syn/ack置位的包,其确认序列号应和连接记录CLIENT的序列号相等。 4.4. 应用协议数据检查 应用协议数据检查所做的是检查控制连接中的应用协议所传递的地址和端口,并创建对应的连接结构,使后续的数据连接能够建立。一般情况下,除非规则允许所有的端口,否则这些协议中传递的动态端口在规则中默认是禁止的。如果能够动态创建连接结构,则避免检查规则,对用户透明。当然,这也可以通过创建动态规则去实现,但是规则是一
[1] [2] 下一页
(出处:http://www.sheup.com)
上一页 [1] [2]