Chinaunix首页 | 论坛 | 博客
  • 博客访问: 487775
  • 博文数量: 82
  • 博客积分: 3003
  • 博客等级: 中校
  • 技术积分: 1285
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-11 15:27
文章分类

全部博文(82)

文章存档

2011年(1)

2010年(5)

2009年(63)

2008年(13)

我的朋友

分类: LINUX

2010-01-03 21:00:20

Netfilter的核心——跟踪连接(Conntrack

Author: Jaylin Zhou


问题:FTP有两种工作模式:主动模式和被动模式。当被动模式的时候,客户端和服务器的连接都是通过随机的端口。这样我们设置防火墙就很困难了。这里就引入了conntrack机制。


Conntrack会在PREROUTINGOUTPUT两个位置注册钩子。当一个包进来,首先进入PREROUTING的位置,conntrack的优先级要高于其他的链,这时候在所有规则之前,conntrack会先查看ip_conntrack_tuple_hash表。这个表的每条记录都记录了一个连接,而且是唯一的连接。怎样确定一个连接是唯一的呢?这里面用了5个元素——源IP、源端口、目的IP、目的端口、协议号。这五个元素足以确定一个连接的唯一性。但是记录中还有一个元素——方向,方向标志了这个连接是源方向还是返回的方向。举个例子:

Client192.168.1.1, 2000)访问Server192.168.1.100, 80)的80端口,那么在Client端当包产生之后,首先就会到OUTPUT的位置。Conntrack会读取包的源地址、目的地址、源端口号、目的端口号等等,然后再检查hash表,看看是否有这条记录(即,Client是否用2000端口访问过Server80端口),发现没有,那么就要创建记录。如何创建呢?Conntrack会一下子创建两条记录:

正向记录:

SRC 192.168.1.1 SPORT 2000 DST 192.168.1.100 DPORT 80 PROTO TCP DIR ORIGINAL

反向记录:

SRC 192.168.1.100 SPORT 80 DST 192.168.1.1 DPORT 2000 PROTO TCP DIR REPLY

同时给记录的状态设为NEW

这之后,包会被丢到POSTROUTING的位置,发现没有SNAT的规则,确认(confirm)了hash表的记录不用更改,再将包丢出去。

服务器接到包,首先进入PREROUTING位置。Conntract查看自己的hash表,没有对应的记录,只好创建两条记录(我们假设PREROUTING里没写DROP状态为NEW的包):

正向记录:

SRC 192.168.1.1 SPORT 2000 DST 192.168.1.100 DPORT 80 PROTO TCP DIR ORIGINAL

反向记录:

SRC 192.168.1.100 SPORT 80 DST 192.168.1.1 DPORT 2000 PROTO TCP DIR REPLY

同样,状态是NEW

之后这个包交给了应用程序处理,最终会给Client一个回包,首先丢给了OUPUT位置。包的内容里也会有源地址、目的地址、源端口号、目的端口号等信息。

包的记录:

SRC 192.168.1.100 SPORT 80 DST 192.168.1.1 DPORT 2000 PROTO TCP

Conntrack会读取包中的这些信息,然后查看hash表的记录,发现有一条状态是NEW的记录(之前的反向记录)和包的信息是完全匹配的,就会将这条记录和对应的正向记录的状态都设置为ESTABLISHED,并将包发送给POSTROUTING,再丢出去。这个过程里,我们可以在OUTPUT链上加规则,限制是否让状态为NEW的记录出去,或者是否让状态为ESTABLISHED的记录出去。

当包进入Client端的PREOUTING位置,会读取包的信息,再检查hash表,发现有一条状态为NEW的记录(之前的反向记录)完全匹配(我们假设PREROUTING链没有DROP状态为NEW的记录的规则),这样这个包就被接受了,并且记录的状态被设置为ESTABLISHEDConntrack的过程就完成了,整个连接的过程也ESTABLISHED了。




看下面的图,发现有一个新的地方,就是RELATEDConntrack会先在hash表里查找ESTABLISHED状态的记录,然后在查看这个包是不是之前某个记录所期望的包(RELATED),如果上面两条都不是,才新建记录(NEW)。什么时候会使用到RELATED呢,我们分别看一下FTP的主动模式和被动模式。




假设FTP Server192.168.1.200),Client192.168.1.1)。

先看一下FTP的被动模式(PASSIVE MODE):


  1. 客户端向服务器发送一个建立控制连接的请求。数据包的内容如下:

SRC 192.168.1.1 SPORT 3000 DST 192.168.1.200 DPORT 21 PROTO TCP

这个包首先会到OUTPUT位置,conntrack读取包中的上述信息,然后到hash表里找对应的记录,发现没有,NEW两条记录。

  1. 服务器端收到包,在PREROUTING的位置,conntract读取包的信息,查看hash表,发现没有对应的记录,NEW两条记录。

  2. 包传给了应用程序,应用程序回包给客户端。包首先到OUTPUT位置,conntrack会读取包的信息,查看hash表,发现有对应的记录(之前的反向记录),状态为NEW,将记录状态改为ESTABLISHED,再将包发出去。

  3. 客户端接到包,首先进入PREROUTING的位置,conntrack读取包的信息,查看hash表,发现有对应的记录(之前的反向记录),状态为NEW,于是将记录状态改为ESTABLISHED,再传给应用程序。至此控制连接创建完毕。

  4. 客户端向服务器发送一个PASV命令,请求被动模式的数据连接。此时走的依旧是控制连接。

  5. 服务器接受到请求,回一个以“227”字样开头的包(这个数字标识了此包是回复客户端PASV请求的),包里包含了服务器的IP和允许客户端连接的端口号。当这个包到达OUTPUT的时候,conntract读取包的信息,找到“227”字样,调用Helper函数,生成一个期望(Expectation)。再将这个包丢出去。这走的也是控制连接。

  6. 客户端接到这个包,到PREROUTING位置,conntrack读取包的信息,检查hash表,因为依然是控制连接,所以又对应的记录,正常接收。

  7. 当客户端向服务器发送数据连接请求的时候,首先到OUPUT位置,conntrack读取包的信息,查看hash表,没有匹配的记录,于是NEW两条记录。再将包丢出去。

  8. 服务器接到包,到达PREROUTING位置,conntrack读取包的信息,查看hash表,发现ESTABLISHED状态的记录没有匹配的,但是根据Helper函数的结果,此包是之前某条记录所期望的,那么就会新建两条记录,状态为RELATED。再传给应用程序。

  9. 应用程序的回包,首先到OUTPUT位置,conntrack读取包的信息,查看hash表,发现有状态为RELATED的匹配记录,于是将记录状态改为ESTABLISHED,再将包丢出去。

  10. 客户端接收到了包,到达PREROUTING位置,conntrack读取包的信息,查看hash表,发现有状态为NEW的记录匹配,将记录状态设为ESTABLISHED,将包传给应用程序。这样数据连接就建完毕了。




再看看主动模式:

数据连接的部分和被动模式一样(1—4),这里就不再说了。

  1. 客户端向服务器发送一个PORT命令,要求建立主动模式的数据连接。包的头部会有“PORT”字样,后面是自己的IP和允许服务器20端口连接的端口号。包到达OUTPUT位置,conntract会读取包的信息,发现有“PORT”字样,就调用Helper函数算一个期望值。这时走的是控制连接。

  2. 服务器收到后,给客户端一个回包。这是走的也是控制连接。

  3. 当服务器发起数据连接时,第一个包到达OUTPUT位置,conntrack会读取包的信息,查看hash表,没有匹配的记录,NEW两条记录。再将包丢出去。

  4. 客户端接到包,到达PREROUTING位置,conntract会读取包的信息,查看hash表,没有ESTABLISHED状态的记录匹配,但是这个包的信息符合之前某个控制连接记录的期望,于是就创建两条记录,状态为RELATED。再将包传给应用程序。

  5. 应用程序的回包,首先到达OUTPUT位置,conntrack读取包的信息,查看hash表,发现有状态为RELATED的记录匹配,将记录状态改为ESTABLISHED。再将包丢出去。

  6. 服务器接收到包,到达PREROUTING位置,conntrack读取包的信息,查看hash表,发现有状态为NEW的记录匹配,将记录的状态改为ESTABLISHED。再将包传给应用程序。至此数据连接建立完毕。



综上所述,我们可以通过包的状态(NEWRELATEDESTABLISHED)来设置iptables的规则。


上面都是没有NAT的情况下conntrack的工作过程,下面的图是讲解一个网管做SNATconntrack工作过程。




环境:内网的一台主机(192.168.122.117)访问外网(66.17.201.11),内网主机指向这台默认网关(10.66.0.174)。我们只监视网关的conntrack

  1. 客户端访问外网的一个Apache服务器,将包丢给默认网关。包的内容:

SRC 192.168.122.117 SPORT 37564 DST 66.17.201.11 DPORT 80 PROTO TCP

  1. 这个包进入默认网关,首先到PREROUTING位置。Conntrack读取包的信息,查看hash表,没有匹配的记录,于是就要NEW两条记录。记录的内容:

正向记录:

SRC 192.168.122.117 SPORT 37564 DST 66.17.201.11 DPORT 80 PROTO TCP DIR ORIGINAL

反向记录:

SRC 66.17.201.11 SPORT 80 DST 192.168.122.117 DPORT 37564 PROTO TCP DIR REPLY

之后这个包经过route table,进入FORWARD链,再传到POSTROUTING位置。POSTROUTING链里有SNAT规则,将发出去的包的源地址都转为网关的地址。这样包的记录就会被改为:

SRC 10.66.0.174 SPORT 37564 DST 66.17.201.11 DPORT 80 PROTO TCP

同时conntrack注册在PREROUTING的钩子也会将刚才生成的反向记录的目的地址改为网关的地址,即:

SRC 66.17.201.11 SPORT 80 DST 10.66.0.174 DPORT 37564 PROTO TCP DIR REPLY

同时要再加一个状态“SRC NAT”

这个包被再丢出去。

  1. 外网的的Apache服务器接到这个包,经过应用程序的处理,会回一个包(我们不讨论Apache服务器的conntrack过程),包的内容:

SRC 66.17.201.11 SPORT 80 DST 10.66.0.174 DPORT 37564 PROTO TCP

  1. 这个包会被网关接受到,首先进入到PREROUTING的位置。Conntrack读取包的信息,查看hash表的记录,发现有一个状态为SRC NAT的记录匹配。之后的一步很关键。找到状态为SRC NAT的记录匹配之后,会根据此记录和对应的正向记录的内容自动做一个DNAT。将包的目的地址改为匹配记录对应正向记录的的SRC地址,包的内容改为:

SRC 66.17.201.11 SPORT 80 DST 192.168.122.117 DPORT 37564 PROTO TCP

这样包就会FORWARD给内网的客户端。这个DNAT的过程不是iptables中设定的规则,而是conntrack自动做的。


假设这样一种情况:如果内网有两台机器192.168.122.117192.168.122.118,不巧的是他们都访问同一个外网的Apache服务器,更不巧的是他们都是用自己的30000端口去连接服务器的80端口。这种情况下,网关的conntrack生成的记录会有冲突。

对于192.168.122.117主机:

正向记录:

SRC 192.168.122.117 SPORT 30000 DST 66.17.201.11 DPORT 80 PROTO TCP DIR ORIGINAL

反向记录(做过SNAT之后):

SRC 66.17.201.11 SPORT 80 DST 10.66.0.174 DPORT 30000 PROTO TCP DIR RELPAY

对于192.168.122.117主机:

正向记录:

SRC 192.168.122.118 SPORT 30000 DST 66.17.201.11 DPORT 80 PROTO TCP DIR ORIGINAL

反向记录(做过SNAT之后):

SRC 66.17.201.11 SPORT 80 DST 10.66.0.174 DPORT 30000 PROTO TCP DIR RELPAY

可以发现两条方向记录一模一样,这样接到服务器的回包时就不知道应该找那条正向记录做DNATConntrack是这样处理这种情况的,当第二条方向记录的反向记录和之前的某天记录有冲突,就将这条反向记录的DPORT改为一个随机的值,同时数据包的源端口也要改。例如将30000改为30001。这样就可以区分回包了。


还有一种情况比较特殊,内网通过网关(SNAT)连接外网的FTP Server,主动模式时会有问题。为什么呢?根据上面关于正常FTP主动模式的说明,Client会向Server发一个“PORT”开头的包,内容是ClientIP和允许Server连接的端口号。当这个包到达网关,到POSTROUTING链,做SNAT转换的时候,只是将这个包的源地址改为了网关的地址,真正的“PORT”命令传的IP是没有改的。当服务器创建主动连接时就会发送一个连接到Client地址的包,而服务器是无法直接连接客户端的,连接就无法建立。如何解决这个问题呢?添加一个针对这个问题处理的模块,可以直接修改“PORT”命令里的地址。


虽然iptables主要是针对IP层进行的设定,但是它已经可以修改应用层的包信息。



阅读(2469) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~