iptables--状态机制

1.概述

状态机制是 iptables 中特殊的一部分,其实它不应该叫状态机制,因为它只是一种连接跟踪机制。连接跟踪可以让 Netfilter 知道某个特定连接的状态。运行连接跟踪的防火墙称作带有状态机制的防火墙,以下简称为状态防火墙。状态防火墙比非状态防火墙要安全,因为它允许我们编写更严密的规则。在 iptables 里,包是和被跟踪连接的四种不同状态有关的。它们是NEW,ESTABLISHED,
RELATED 和INVALID。所有在内核中由 Netfilter 的特定框架做的连接跟踪称作 conntrack。conntrack 中有许多用来处理 TCP,
UDP 或 ICMP 协议的部件。这些模块从数据包中提取详细的、唯一的信息,因此能保持对每一个数据流的跟踪。这些信息也告知 conntrack 流当前的状态。例如,UDP 流一般由他们的目的地址、源地址、目的端口和源端口唯一确定。
除了本地产生的包由 OUTPUT 链处理外,所有连接跟踪都是在 PREROUTING 链里进行处理的,意思就是, iptables 会在 PREROUTING 链里从新计算所有的状态。如果我们发送一个流的初始化包,状态就会在 OUTPUT 链里被设置为 NEW,当我们收到回应的包时,状态就会在 PREROUTING 链里被设置为 ESTABLISHED。如果第一个包不是本地产生的,那就会在PREROUTING 链里被设置为 NEW 状态。综上,所有状态的改变和计算都是在 nat 表中的 PREROUTING 链和 OUTPUT 链里完成的。

2.conntrack记录

查看文件/proc/net/ip_conntrack 里的 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,说明这个连接还没有收到任何回应。最后,是希望接收的应答包的信息,他们的地址和端口和前面是相反的。
当一个连接在两个方向上都有传输时,conntrack 记录就删除[UNREPLIED]标志,然后重置。在末尾有 [ASSURED]的记录说明两个方向已没有流量。这样的记录是确定的,在连接跟踪表满时,是不会被删除的,没有[ASSURED]的记录就要被删
除。连接跟踪表能容纳多少记录是被一个变量控制的,它可由内核中的 ip-sysctl 函数设置。默认值取决于你的内存大小,128MB 可以包含 8192 条目录,256MB 是 16376 条。你也可以在 /proc/sys/net/ipv4/ip_conntrack_max 里查看/设置。

3.用户空间的状态

包的状态依据 IP 所包含的协议不同而不同,但在内核外部,也就是用户空间里,只有 4 种状态:NEW,ESTABLISHED,RELATED 和 INVALID。它们主要是和状态匹配一起使用。下面就简要地介绍以下这几种状态:

State(状态) Explanation(注释)
NEW 说明这个包是我们看到的第一个包。意思就是,这是 conntrack模块看到的某个连接第一个包,它即将被匹配了。比如,我们看到一个 SYN 包,是我们所留意的连接的第一个包,就要匹配它。第一个包也可能不是 SYN 包,但它仍会被认为是 NEW 状态。这样做有时会导致一些问题,但对某些情况是有非常大的帮助的。例如,在我们想恢复某条从其他的防火墙丢失的连接时,或者某个连接已经超时,但实际上并未关闭时。
ESTABLISHED 已经注意到两个方向上的数据传输,而且会继续匹配这个连接的包。处于 ESTABLISHED 状态的连接是非常容易理解的。只要发送并接到应答,连接就是 ESTABLISHED 的了。一个连接要从NEW 变为 ESTABLISHED,只需要接到应答包即可,不管这个包是发往防火墙的,还是要由防火墙转发的。ICMP 的错误和重定向等信息包也被看作是 ESTABLISHED,只要它们是我们所发出的信息的应答。
RELATED 是个比较麻烦的状态。当一个连接和某个已处于ESTABLISHED 状态的连接有关系时,就被认为是 RELATED 的了。换句话说,一个连接要想是 RELATED 的,首先要有一个 ESTABLISHED的连接。这个 ESTABLISHED 连接再产生一个主连接之外的连接,这个新的连接就是 RELATED 的了,当然前提是 conntrack 模块要能理解 RELATED。ftp 是个很好的例子,FTP-data 连接就是和FTP-control 有 RELATED 的。还有其他的例子,比如,通过 IRC 的DCC 连接。有了这个状态,ICMP 应答、FTP 传输、DCC 等才能穿过防火墙正常工作。注意,大部分还有一些 UDP 协议都依赖这个机制。这些协议是很复杂的,它们把连接信息放在数据包里,并且要求这些信息能被正确理解。
INVALID 说明数据包不能被识别属于哪个连接或没有任何状态。有几个原因可以产生这种情况,比如,内存溢出,收到不知属于哪个连接的 ICMP 错误信息。一般地,我们 DROP 这个状态的任何东西。

这些状态可以一起使用,以便匹配数据包。这可以使我们的防火墙非常强壮和有效。以前,我们经常打开 1024 以上的所有端口来放行应答的数据。现在,有了状态机制,就不需再这样了。因为我们可以只开放那些有应答数据的端口,其他的都可以关闭。这样就安全多了。

4.TCP连接

一个 TCP 连接是经过三次握手协商连接信息才建立起来的。整个会话由一个 SYN包开始,然后是一个 SYN/ACK 包,最后是一个 ACK 包,此时,会话才建立成功,能够发送数据。最大的问题在于连接跟踪怎样控制这个过程。其实非常简单。
默认情况下,连接跟踪基本上对所有的连接类型做同样的操作。看看下面的图片,我们就能明白在连接的不同阶段,流是处于什么状态的。就如你看到的,连接跟踪的代码不是从用户的观点来看待 TCP 连接建立的流程的。连接跟踪一看到 SYN包,就认为这个连接是 NEW 状态,一看到返回的 SYN/ACK 包,就认为连接是ESTABLISHED 状态。如果你仔细想想第二步,应该能理解为什么。有了这个特殊处理,NEW 和 ESTABLISHED 包就可以发送出本地网络,且只有 ESTABLISHED 的连接才能有回应信息。如果把整个建立连接的过程中传输的数据包都看作 NEW,那么三次握手所用的包都是 NEW 状态的,这样我们就不能阻塞从外部到本地网络的连接了。因为即使连接是从外向内的,但它使用的包也是 NEW 状态的,而且为了其他连接能正常传输,我们不得不允许 NEW 状态的包返回并进入防火墙。

下面介绍 TCP 连接在建立连接过程中的状态:
建立连接过程

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 分钟。之所以留这个时间,是为了让数据包能完全通过各种规则的检查,也是为了数据包能通过拥挤的路由器,从而到达目的地。
如果连接是被 RST 包重置的,就直接变为 CLOSE 了。这意味着在关闭之前只有10 秒的默认时间。RST 包是不需要确认的,它会直接关闭连接。针对 TCP 连接,还有其他一些状态我们没有谈到。下面给出一个完整的状态列表和超时值。内部状态如下:

State Timeout value
NONE 30 minutes
ESTABLISHED 5 days
SYN_SENT 2 minutes
SYN_RECV 60 seconds
FIN_WAIT 2 minutes
TIME_WAIT 2 minutes
CLOSE 10 seconds
CLOSE_WAIT 12 hours
LAST_ACK 30 seconds
LISTEN> 2 minutes

这些值不是绝对的,可以随着内核的修订而变化,也可以通过/proc/sys/net/ipv4/netfilter/ip_ct_tcp_*的变量更改。这些默认值都是经过实践检验的。它们的单位是 jiffies(百分之一秒),所以 3000 就代表 30 秒。

5.UDP连接

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秒是短了点儿,但对大部分应用足够了。只要遇到这个连接的包穿过防火墙,超时值就会被重置为默认值,所有的状态都是这样的。

6.ICMP连接

ICMP 也是一种无状态协议,它只是用来控制而不是建立连接。常用的回显请求和应答(Echo request and reply),有两种状态 NEW 和 ESTABLISHED 。
比如 ping 命令。
ICMP连接
主机向目标发送一个回显请求,防火墙就认为这个包处于 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 的,如下图:
ICMP不可达
我们发送了一个 SYN 包到某一地址,防火墙认为它的状态是 NEW。但是,目标网络有问题不可达,路由器就会返回网络不可达的信息,这是 RELATED 的。连接跟踪会认出这个错误信息是哪个连接的,连接会中断,同时相应的记录删除会被删除。

7.缺省的连接操作

有时,conntrack 机制并不知道如何处理某个特殊的协议,尤其是在它不了解这
个协议或不知道协议如何工作时,比如,NETBLT,MUX 还有 EGP。这种情况下,conntrack 使用缺省的操作。这种操作很象对 UDP 连接的操作,就是第一个包被认作 NEW,其后的应答包等等数据都是 ESTABLISHED。 使用缺省操作的包的超时值都是一样的,600 秒,也就是 10 分钟。当然,这个值可以通过/proc/sys/net/ipv4/netfilter/ip_ct_generic_timeout 更改,以便适应你的通信量,尤其是在耗时较多、流量巨大的情况下,比如使用卫星等

8.复杂协议和连接跟踪

有些协议比其他协议更复杂,这里复杂的意思是指连接跟踪机制很难正确地跟踪它们,比如,ICQ、IRC 和 FTP,它们都在数据包的数据域里携带某些信息,这些信息用于建立其他的连接。

温馨提示:
以上文章描述如有不清晰之处,欢迎在评论区评论,如有时间,会第一时间回复,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章