状态机制是iptables中特殊的一部分,其实它不该该叫状态机制,由于它只是一种链接跟踪机制。但 是,不少人都承认状态机制这个名字。文中我也或多或或少地用这个名字来表示和链接跟踪相同的意思。这 不该该引发什么混乱的。链接跟踪可让Netfilter知道某个特定链接的状态。运行链接跟踪的防火墙称做 带有状态机制的防火墙,如下简称为状态防火墙。状态防火墙比非状态防火墙要安全,由于它容许咱们编写 更严密的规则。python
在iptables里,包是和被跟踪链接的四种不一样状态有关的。它们是NEW,ESTABLISHED,RELATED和INVALID。 后面咱们会深刻地讨论每个状态。使用--state匹配操做,咱们能很容易地控制 “谁或什么能发起新的会话”。linux
全部在内核中由Netfilter的特定框架作的链接跟踪称做conntrack(译者注:就是connection tracking 的首字母缩写)。conntrack能够做为模块安装,也能够做为内核的一部分。大部分状况下,咱们想要,也 须要更详细的链接跟踪,这是相比于缺省的conntrack而言。也由于此,conntrack中有许多用来处理TCP, UDP或ICMP协议的部件。这些模块从数据包中提取详细的、惟一的信息,所以能保持对每个数据流的跟 踪。这些信息也告知conntrack流当前的状态。例如,UDP流通常由他们的目的地址、源地址、目的端口和源 端口惟一肯定。shell
在之前的内核里,咱们能够打开或关闭重组功能。然而,自从iptables和Netfilter,尤为是链接跟踪被 引入内核,这个选项就被取消了。由于没有包的重组,链接跟踪就不能正常工做。如今重组已经整合入 conntrack,而且在conntrack启动时自动启动。不要关闭重组功能,除非你要关闭链接跟踪。安全
除了本地产生的包由OUTPUT链处理外,全部链接跟踪都是在PREROUTING链里进行处理的,意思就是, iptables会在PREROUTING链里重新计算全部的状态。若是咱们发送一个流的初始化包,状态就会在OUTPUT链 里被设置为NEW,当咱们收到回应的包时,状态就会在PREROUTING链里被设置为ESTABLISHED。若是第一个包不是本地产生的,那就会在PREROUTING链里被设置为NEW状 态。综上,全部状态的改变和计算都是在nat表中的PREROUTING链和OUTPUT链里完成的。服务器
咱们先来看看怎样阅读/proc/net/ip_conntrack里的conntrack记录。这些记 录表示的是当前被跟踪的链接。若是安装了ip_conntrack模块,cat /proc/net/ip_conntrack 的显示相似:网络
tcp 6 117 SYN_SENT src=192.168.1.6 dst=192.168.1.9 sport=32775 \ dport=22 [UNREPLIED] src=192.168.1.9 dst=192.168.1.6 sport=22 \ dport=32775 use=2
conntrack模块维护的全部信息都包含在这个例子中了,经过它们就能够知道某个特定的链接处于什么状 态。首先显示的是协议,这里是tcp,接着是十进制的6(译者注:tcp的协议类型代码是6)。以后的117是 这条conntrack记录的生存时间,它会有规律地被消耗,直到收到这个链接的更多的包。那时,这个值就会 被设为当时那个状态的缺省值。接下来的是这个链接在当前时间点的状态。上面的例子说明这个包处在状态 SYN_SENT,这个值是iptables显示的,以便咱们好理解,而内部用的值稍有不一样。SYN_SENT说明咱们正在观 察的这个链接只在一个方向发送了一TCP SYN包。再下面是源地址、目的地址、源端口和目的端口。其 中有个特殊的词UNREPLIED,说明这个链接尚未收到任何回应。最后,是但愿接收的应答包的信息,他们 的地址和端口和前面是相反的。框架
链接跟踪记录的信息依据IP所包含的协议不一样而不一样,全部相应的值都是在头文件linux/include/netfilter-ipv4/ip_conntrack*.h中定义的。IP、TCP、UDP、ICMP协 议的缺省值是在linux/include/netfilter-ipv4/ip_conntrack.h里定义的。具 体的值能够查看相应的协议,但咱们这里用不到它们,由于它们大都只在conntrack内部使用。随着状态的 改变,生存时间也会改变。tcp
当一个链接在两个方向上都有传输时,conntrack记录就删除[UNREPLIED]标志,而后重置。在末尾有 [ASSURED]的记录说明两个方向已没有流量。这样的记录是肯定的,在链接跟踪表满时,是不会被删除的, 没有[ASSURED]的记录就要被删除。链接跟踪表能容纳多少记录是被一个变量控制的,它可由内核中的ip- sysctl函数设置。默认值取决于你的内存大小,128MB能够包含8192条目录,256MB是16376条。你也能够在 /proc/sys/net/ipv4/ip_conntrack_max里查看、设置。函数
一个TCP链接是通过三次握手协商链接信息才创建起来的。整个会话由一个SYN包开始,而后是一个 SYN/ACK包,最后是一个ACK包,此时,会话才创建成功,可以发送数据。最大的问题在于链接跟踪怎样控制 这个过程。其实很是简单。spa
默认状况下,链接跟踪基本上对全部的链接类型作一样的操做。看看下面的图片,咱们就能明白在链接 的不一样阶段,流是处于什么状态的。就如你看到的,链接跟踪的代码不是从用户的观点来看待TCP链接创建 的流程的。链接跟踪一看到SYN包,就认为这个链接是NEW状态,一看到返回的SYN/ACK包,就认为链接是 ESTABLISHED状态。若是你仔细想一想第二步,应该能理解为何。有了这个特殊处理,NEW和ESTABLISHED包 就能够发送出本地网络,且只有ESTABLISHED的链接才能有回应信息。若是把整个创建链接的过程当中传输的 数据包都看做NEW,那么三次握手所用的包都是NEW状态的,这样咱们就不能阻塞从外部到本地网络的链接 了。由于即便链接是从外向内的,但它使用的包也是NEW状态的,并且为了其余链接能正常传输,咱们不得 不容许NEW状态的包返回并进入防火墙。更复杂的是,针对TCP链接内核使用了不少内部状态,它们的定义在 RFC 793 - Transmission Control Protocol的21-23页。但好在咱们在用 户空间用不到。后面咱们会详细地介绍这些内容。
正如你看到的,以用户的观点来看,这是很简单的。可是,从内核的角度看这一块还有点困难的。咱们 来看一个例子。认真考虑一下在/proc/net/ip_conntrack里,链接的状态是如何 改变的。
tcp 6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.35 sport=1031 \ dport=23 [UNREPLIED] src=192.168.1.35 dst=192.168.1.5 sport=23 \ dport=1031 use=1
从上面的记录能够看出,SYN_SENT状态被设置了,这说明链接已经发出一个SYN包,但应答还没发送过 来,这可从[UNREPLIED]标志看出。
tcp 6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.35 sport=1031 \ dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 \ use=1
如今咱们已经收到了相应的SYN/ACK包,状态也变为SYN_RECV,这说明最初发出的SYN包已正确传输,并 且SYN/ACK包也到达了防火墙。 这就意味着在链接的两方都有数据传输,所以能够认为两个方向都有相应的 回应。固然,这是假设的。
tcp 6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.35 \ sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 \ sport=23 dport=1031 use=1
如今咱们发出了三步握手的最后一个包,即ACK包,链接也就进入ESTABLISHED状态了。再传输几个数据 包,链接就是[ASSURED]的了。
下面介绍TCP链接在关闭过程当中的状态。
如上图,在发出最后一个ACK包以前,链接(指两个方向)是不会关闭的。注意,这只是针对通常的情 况。链接也能够经过发送关闭,这用在拒绝一个链接的时候。在RST包发送以后,要通过预先设定的一段时 间,链接才能断掉。
链接关闭后,进入TIME_WAIT状态,缺省时间是2分钟。之因此留这个时间,是为了让数据包能彻底经过 各类规则的检查,也是为了数据包能经过拥挤的路由器,从而到达目的地。
UDP链接是无状态的,由于它没有任何的链接创建和关闭过程,并且大部分是无序列号的。以某个顺序收 到的两个数据包是没法肯定它们的发出顺序的。但内核仍然能够对UDP链接设置状态。咱们来看看是如何跟 踪UDP链接的,以及conntrack的相关记录。
从上图能够看出,以用户的角度考虑,UDP链接的创建几乎与TCP的同样。虽然conntrack信息看起来有点 儿不一样,但本质上是同样的。下面咱们先来看看第一个UDP包发出后的conntrack记录。
udp 17 20 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 \
[UNREPLIED] src=192.168.1.5 dst=192.168.1.2 sport=1025 \
dport=137 use=1
从前两个值可知,这是一个UDP包。第一个是协议名称,第二个是协议号,第三个是此状态的生存时间, 默认是30秒。接下来是包的源、目地址和端口,还有期待之中回应包的源、目地址和端口。[UNREPLIED]标 记说明还未收到回应。
udp 17 170 src=192.168.1.2 dst=192.168.1.5 sport=137 \ dport=1025 src=192.168.1.5 dst=192.168.1.2 sport=1025 \ dport=137 use=1
一旦收到第一个包的回应,[UNREPLIED]标记就会被删除,链接就被认为是ESTABLISHED的,但在记录里 并不显示ESTABLISHED标记。相应地,状态的超时时间也变为180秒了。在本例中,只剩170秒了,10秒后, 就会减小为160秒。有个东西是不可少的,虽然它可能会有些变化,就是前面提过的[ASSURED]。要想变为 [ASSURED]状态,链接上必需要再有些流量。
udp 17 175 src=192.168.1.5 dst=195.22.79.2 sport=1025 \ dport=53 src=195.22.79.2 dst=192.168.1.5 sport=53 \ dport=1025 [ASSURED] use=1
能够看出来,[ASSURED]状态的记录和前面的没有多大差异,除了标记由[UNREPLIED]变成[ASSURED]。如 果这个链接持续不了180秒,那就要被中断。180秒是短了点儿,但对大部分应用足够了。只要遇到这个链接 的包穿过防火墙,超时值就会被重置为默认值,全部的状态都是这样的。
ICMP也是一种无状态协议,它只是用来控制而不是创建链接。ICMP包有不少类型,但只有四种类型有应 答包,它们是回显请求和应答(Echo request and reply),时间戳请求和应答(Timestamp request and reply),信息请求和应答(Information request and reply),还有地址掩码请求和应答(Address mask request and reply),这些包有两种状态,NEW和ESTABLISHED 。时间戳请求和信息请求已经废除不用了,回显请求仍是经常使用的,好比ping命令就用的到,地址掩码请 求不太经常使用,可是可能有时颇有用而且值得使用。看看下面的图,就能够大体了解ICMP链接的NEW和ESTABLISHED状态了。
如图所示,主机向目标发送一个回显请求,防火墙就认为这个包处于NEW状态。 目标回应一个回显应答,防火墙就认为包处于ESTABLISHED了。当回显请求被发送 时,ip_conntrack里就有这样的记录了:
icmp 1 25 src=192.168.1.6 dst=192.168.1.10 type=8 code=0 \ id=33029 [UNREPLIED] src=192.168.1.10 dst=192.168.1.6 \ type=0 code=0 id=33029 use=1
能够看到,ICMP的记录和TCP、UDP的有点区别,协议名称、超时时间和源、目地址都同样,不一样之处在 于没有了端口,而新增了三个新的字段:type,code和id。字段type说明ICMP的类型。code说明ICMP的代 码,这些代码在附录ICMP类型里有说明。id是ICMP包的ID。每一个ICMP包被发送时都被分配一个ID,接受方把一样的ID 分配给应答包,这样发送方能认出是哪一个请求的应答。
[UNREPLIED]的含义和前面同样,说明数的传输只发生在一个方向上,也就是说未收到应答。再日后,是 应答包的源、目地址,还有相应的三个新字段,要注意的是type和code是随着应答包的不一样而变化的,id和 请求包的同样。
和前面同样,应答包被认为是ESTABLISHED的。然而,在应答包以后,这个ICMP 链接就再也不有数据传输了。因此,一旦应答包穿过防火墙,ICMP的链接跟踪记录就被销毁了。
以上各类状况,请求被认为NEW,应答是ESTABLISHED。 换句话说,就是当防火墙看到一个请求包时,就认为链接处于NEW状态,当有应答 时,就是ESTABLISHED状态。
ICMP的缺省超时是30秒,能够在/proc/sys/net/ipv4/netfilter/ip_ct_icmp_timeout中修改。这个值是比较合适 的,适合于大多数状况。
ICMP的另外一个很是重要的做用是,告诉UDP、TCP链接或正在努力创建的链接发生了什么,这时ICMP应答 被认为是RELATED的。主机不可达和网络不可达就是这样的例子。当试图链接某台机 子不成功时(可能那台机子被关上了),数据包所到达的最后一台路由器就会返回以上的ICMP信息,它们就 是RELATED的,以下图:
咱们发送了一个SYN包到某一地址,防火墙认为它的状态是NEW。可是,目标网络 有问题不可达,路由器就会返回网络不可达的信息,这是RELATED的。链接跟踪会认 出这个错误信息是哪一个链接的,链接会中断,同时相应的记录删除会被删除。
当UDP链接遇到问题时,一样会有相应的ICMP信息返回,固然它们的状态也是RELATED ,以下图:
咱们发送一个UDP包,固然它是NEW的。可是,目标网络被一些防火墙或路由器所 禁止。咱们的防火墙就会收到网络被禁止的信息。防火墙知道它是和哪一个已打开的UDP链接相关的,而且把 这个信息(状态是RELATED)发给它,同时,把相应的记录删除。客户机收到网络被 禁止的信息,链接将被中断。
有时,conntrack机制并不知道如何处理某个特殊的协议,尤为是在它不了解这个协议或不知道协议如何 工做时,好比,NETBLT,MUX还有EGP。这种状况下,conntrack使用缺省的操做。这种操做很象对UDP链接的 操做,就是第一个包被认做NEW,其后的应答包等等数据都是 ESTABLISHED。
使用缺省操做的包的超时值都是同样的,600秒,也就是10分钟。固然,这个值能够经过/proc/sys/net/ipv4/netfilter/ip_ct_generic_timeout更改,以便适应你的通讯 量,尤为是在耗时较多、流量巨大的状况下,好比使用卫星等。
有些协议比其余协议更复杂,这里复杂的意思是指链接跟踪机制很难正确地跟踪它们,好比,ICQ、IRC 和FTP,它们都在数据包的数据域里携带某些信息,这些信息用于创建其余的链接。所以,须要一些特殊的 helper来完成工做。
下面以FTP做为例子。FTP协议先创建一个单独的链接——FTP控制会话。咱们经过这个链接发布命令,其 他的端口就会打开以便传输和这个命令相关的数据。这些链接的创建方法有两种:主动模式和被动模式。先 看看主动模式,FTP客户端发送端口和IP地址信 息给服务器端,而后,客户端打开这个端口,服务器端从它本身的20端口(FTP-Data端口号)创建与这个端 口的链接,接着就可使用这个链接发送数据了。
问题在于防火墙不知道这些额外的链接(相对于控制会话而言),由于这些链接在创建时的磋商信息都 在协议数据包的数据域内,而不是在可分析的协议头里。所以,防火墙就不知道是否是该放这些从服务器到 客户机的链接过关。
解决的办法是为链接跟踪模块增长一个特殊的helper,以便能检测到那些信息。这样,那些从FTP服务器 到客户机的链接就能够被跟踪了,状态是RELATED,过程以下图所示:
被动FTP工做方式下,data链接的创建过程和主动FTP的相反。客户机告诉服务器须要某些数据,服务器 就把地址和端口发回给客户机,客户机据此创建链接接受数据。若是FTP服务器在防火墙后面,或你对用户 限制的比较严格,只容许他们访问HTTP和FTP,而封闭了其余全部端口,为了让在Internet是的客户机能访 问到FTP,也须要增长上面提到的helper。下面是被动模式下data链接的创建过程: