[转]PPTP穿透NAT之深入分析

大家好,现在是人静时分,我公司人员都以溜光,只有我还在面对computer,在经过不解、迷惑、结论之后,现与大家分享结果,感谢朋友Zyliday,见贤思齐的实验帮助。

 

在研究技术原理之前,让我们先了解几个基本的概念。我们要了解NAT技术,这个就不用多说了吧!我们的主机大部分存在于一个内部网络中,有一个私有ip地址,所以所有去往Internet的数据包,都要将其源ip与端口转换,来实现数据通信。让我们感到幸运的是,我们对于互联网的服务应用,大部分用tcp与udp协议来传输,由于tcp与udp协议中含有端口,所以往返的数据包很容易找到归宿。但难免会有特殊出现,pptp协议,l2tp协议,ftp 协议,ipsec 协议等,根据其各自协议特殊原因,均无法顺利通过nat,俗话说兵来将挡水来土掩,nat 该怎么办going?今天重点我们放到pptp协议身上。pptp是什么?pptp是其vpn隧道协议中的一种,加以其它协议的配合,成功实现了身份验证、加密数据和协议配置等问题,为远程用户创建了一条穿越公网的安全连接,让我们去了解PPTP协议。

 

PPTP流量由两条连接产生:

• PPTP控制连接

这是一个逻辑连接,表示必须通过一系列PPTP消息创建、维持和终止的PPTP隧道。 PPTP控制连接流量使用PPTP客户端上动态分配的TCP端口,以及PPTP服务器上由IANA保留的TCP端口1723。

• PPTP数据连接

当数据通过PPTP连接发送时,PPP帧将使用通用路由封装(Generic Routing Encapsulation,GRE)报头进行封装,报头包含用于识别该数据包的特定PPTP隧道的信息。

现在来分析下,由于控制连接用了端口1723,所以这条连接我们就大可放心了,根据nat发送原则,会成功将数据包转发到pptp 服务器,如果你的vpn 服务器建立于内网中,在路由上映射其1723端口到服务器就OK.

 

让我们看下数据连接,我们发现以经没有了tcp协议参于,反而成了GRE协议包与PPP协议包,为什么它无法通过nat呢?来看下GRE协议格式:

每个字段的详解:

我们发现根本没有发现有端口这个字段,怎么映射?没有就不做端口映射呗~~~就转换ip不就得了。哈哈其实远远不是这样的,这种非常理的数据包格式,对于nat是个问题,对于防火墙也是个问题。今天我们先论nat:先来分析如果一个位于内部网络中( 公网IP2.2.2.2)的主机A192.168.1.2与pptp服务器5.5.5.5建立连接,控制连接成功建立,数据连接于由没有端口,nat只做其ip的转换,于是在2.2.2.2这个网络的nat设备上有这样的一个nat表:

ip

port

协议

公网ip

公网port

目标ip

目标port

192.168.1.2

 

GRE

2.2.2.2

 

5.5.5.5

 

192.168.1.3

 

GRE

2.2.2.2

 

5.5.5.5

 

当2.2.2.2这个网络收到来自于pptp服务器的数据流包时,2.2.2.2网络的nat设备发现来自于网络5.5.5.5的gre 协议数据包,是给内部主机192.168.1.2的,所以成功通信。分析如果位于2.2.2.2这个网络中的主机B192.168.1.3 也与5.5.5.5网络中的pptp服务器通信,结果就不太理想了~~如果在nat表中还有一条到达主机192.168.1.3的gre通信,到收到一个gre协议数据包时,2.2.2.2网络中的nat设备犹豫了,这个包给谁呢?192.168.1.2?还是192.168.1.3呢?在此大家也不要忘了,我们的实验环境是只有一个公网ip,内部主机A与B出去的时候,源ip都转换成了公网的ip2.2.2.2,所以收到的数据包都是目标ip为2.2.2.2,然后nat设备根据端口的不同转发到不同的主机。有两个客户端就无法通信,这就是网上说的位于内部的主机只能与同一服务器之间建立一个会话,不能有第二个客户端。

vpn 技术的流行,但技术上有约束,人们想办法开发能穿透nat的设备,实现多路复用。在细分析gre协议后,人们发现gre协议中有一个字段,可以拿来利用,那就是这个call id值,这个值并不固定,可以改变。这个值是怎么回事呢?用来标识唯一会话的。这个值最早出现在控制连接中,客户端与服务器互相通告彼此的call id值,然后在其数据连接中,服务器call  id值写上客户端的,客户端写上服务器的call id值。nat引入了一种技术nat编辑器,大家可以去看下我们的路由器,大部分自动开启了对ipsec隧道与pptp隧道协议的支持。路由器对pptp隧道的建立施全程监控,并在每一台PPTP客户机的私用IP地址、CALL ID和公共IP地址以及PPTP服务器接收的CALL ID之间建立单独的映射关系。我可以把call id值当做端口来看,然后添加到nat表中。

让大家看一个今天做的一个实例图(pptp客户端路由器NAT表):pptp数制连接我们可以认为它有两条nat映射记录,一个用于数据包的发送,一个用于数据包的接收。

ip

port

协议

公网ip

公网port

目标ip

目标port

192.168.1.10

5000

TCP

59.173.163.119

50001

221.217.151.68

1723

192.168.1.10

30061

GRE

59.173.163.119

32769

221.217.151.68

 进

192.168.1.10

256

GRE

59.173.163.119

256

221.217.151.68

 出

192.168.1.11

10000

TCP

59.173.163.119

50002

221.217.151.68

1723

192.168.1.11

5868

GRE

59.173.163.119

32770

221.217.151.68

 进

192.168.1.11

512

GRE

59.173.163.119

512

221.217.151.68

 出

nat把客户端的call id值当做源端口,然后告诉服务器一个假的call id来充当转换后的端口。这个修改是在控制连接中修改的,然后做nat记录后发送给服务器。说到此我说下,前两天在弯曲评论上看到了一文章,作者写到nat对其服务器的call id也转换,我认为不转换服务器的call id 值。在我们实验的时候,一开始我让多个不同外网的朋友建立vpn连接,连接我这边内部vpn服务器主机,抓包发现服务器通告的call id都一样,不解?如果在同一个内部网络中,有两个客户端连接进来,服务器通告的call id不改变,通信将出故障?今天又与见贤思齐做了一个同一内部网络中,多个客户端连接情况,抓包发现服务器对于同一个ip多个客户机的连接分配了不同的call id值,所以直接修改其客户端call id便可实现流量多路复用,vpn服务器自动解决了这个同一ip不同call  id值问题。

客户端192.168.1.10的连接:

服务器方抓包分析如下:

上图:客户端在控制连接中,告诉服务器它的call id值为32769。

上图:服务器控制连接中告诉它的call id值为256。

客户端抓包分析如下:

上面客户端告诉服务器它的call id为30061。

服务器告诉客户端它的call id值为256。

看了以后大家知道nat做数据包做了什么处理了吧,客户端12的连接我就不贴图了,道理一样。这样的端口改变还可以避免不同的客户端告诉服务器相同的call id,因为它无法意识到另一个主机也用了此call id ,如果nat不加转换,将出现问题。

[转]博客自动显示摘要(read more)的方法-blogger.com优化


blogger.com已经推出了官方的read more功能,当你在控制台写文章的时候,点击插入换行符,就可以达到显示摘要的功能。但是本人比较懒,都是通过Google docs写文章,再加上以前写了好多文章了,不想再为每篇文章加上read more断点了,找到了一个一劳永逸的方法解决博客自动显示摘要(read more)的问题。

这个博客自动显示摘要也有手动模式和自动模式,自动模式修改完代码就不要在进行任何操作了,手动模式是对特殊的页面进行操作,代码对博客最近推出的静态页面支持不够好,需要手动模式。


方法如下,由于每个人的模板代码都可能不同,红色代码为搜寻的切入口:
1.“控制台”-“布局”-“修改 HTML”,勾选“扩展 小窗口部件模板”,同时请保存原有模板以防不测!
2.查找到下面的代码添加.link_fullpost的css样式表:
</head>
在后面添加如下代码:
<script type="text/javascript" src="http://forbloggeruse.googlecode.com/svn/trunk/digest_blogbody.js"></script>
<style tyle="text/css">
.post-body .link_fullpost {
  font-size: 100%;
  margin: 1em;
  display:block;
}
</style>
3.加上返回摘要的锚点,找到如下代码
<b:includable id='post' var='post'>
  <div class='post'>
<a expr:name='data:post.id'/>
修改为如下代码:
<b:includable id='post' var='post'>
  <div class='post'>
    <a expr:id='data:post.id + "-post-body-post-title"' expr:name='data:post.id + "-post-body-post-title"'></a>
4.安装自动文章摘要,找到如下代码:
<div class='post-body'>
   <data:post.body/>
<div style='clear: both;'/> <!-- clear for photos floats -->
    </div>
修改成如下代码:
<div class='post-body' expr:id='data:post.id + "-post-body"'>
   <data:post.body/>
    <div style='clear: both;'/> <!-- clear for photos floats -->
    </div>
<script type="text/javascript">
digest_blogbody('<data:post.id/>'+'-post-body', '<data:post.url/>', location.href);
</script>


通过以上修改后,就会自动产生摘要,对于一些特殊的页面,如今年刚刚出现的静态页面支持不是太好,默认为显示摘要,如果你想直接显示出来,可以在文章的任何地方添加如下代码即可
<!--Show All-->
其他手动摘要的说明:
<!--Digest-->,在你设定的文章锚点出插入此代码,在这之前的文章会显示出来,后面的会隐藏起来。
<!--Hidden All-->与<!--Show All-->,一個是隐藏全文、一個是显示全文,插入在文章任何地方皆可

说明:这个方法和官方显示摘要的方法不同,官方提供的read more方法只是加载read more前面的代码,可以加快网页加载速度,而这个方法原理是全部显示文章后再通过css样式隐藏read more后面的文章,不会加快网页速度,可能还会慢些。

[转]当NAT遇见PPTP

转自弯曲评论:http://www.tektalk.org/2010/03/13/%E5%BD%93nat%E9%81%87%E8%A7%81pptp/


我建了一个PPTP的帐号,想访问一下公司的内网资源,结果发现,没法传输数据,这是怎么回事?于是在Google里面输入rfc pptp,直接就点进去了第一条带有RFC 2637的链接,开始了今天的穿越。。。。。。

问题出在哪里

首先稍微介绍一下,PPTP有两个流,一个是控制流(RFC2637定义),另外一个数据流(GRE,RFC2784)。和一般的ALG不同的是(比如FTP),NAT遇到PPTP的时候,不是端口或者IP惹的祸,而是PPTP里面邪恶的callID(抛一个球给各位,为何PPTP把call ID整到PPTP控制流里面?)。在PPTP协议里面,有一个call ID的概念,客户端向服务器发起连接,会告诉服务端我的call ID是多少,服务端也会回应客户端它的call ID是多少,并且更重要的一点是,这个call ID在以后的某些(注意不是全部哦)控制传输中需要用到,在所有的数据传输中(GRE)需要用到。而接下来我们从两个方面一起来看一下问题是如何产生,以及如何去解决:
  • 从内网向外网发起PPTP连接请求
  • 从外网向内网发起PPTP连接请求(俗称静态映射)

走,咱们去看看外面的世界

(注:下面的结构是1)提一下PPTP协议;2)提一下GRE协议;3)如何搞定GRE数据流;4)如何搞定PPTP控制流。)
PPTP服务器是监听在TCP/1723端口。客户端发送的第一个携带call ID的数据包是被称为Outgoing-Call-Request的数据包,一个示例如下图所示(忽略了前面的以太网头,IP头,以及TCP头):
image
可以看到我们这边的示例客户端的call ID是16384(这个call ID是给自己分配的),那我就以Client-Call-ID表示吧。
服务端收到这个数据包之后,然后会回应一个称为Outgoing-Call-Reply的数据包,示例如下:
image
这个说明服务端自己的call ID是107(这个服务端是给自己分配的,我就以Server-Call-ID表示), 同时也把Client-Call-ID返回给客户端。咦,这样看来好像没有什么问题嘛,反正这个call ID改不改,对于这一条连接都没有影响,NAT也可以正常运作。但是,各位看官,PPTP还需要用到一个协议,那就是GRE(RFC 2784)。而GRE是一个和TCP以及UDP处在同一个水平线的协议。但是和TCP/UDP之流不同的是,GRE里面不携带端口信息,而它利用的就是call ID来作multiplex以及demultiplex的。我们看一下客户端发给服务端的一个GRE数据包(提示一点,GRE头后面的是PPP协议封装,不过这里不需要关心):
image
里面有call ID,这个call ID不是说自己的,而是说对方的(对,就是PPTP里面的那个Server-Call-ID),也就是服务端的,也就是我这个GRE数据要发给服务端的107"端口"。

而服务端也采用同样的策略,只不过call ID表明的是客户端的,一个示例如下:
image
(注:上面的都是一些协议的原理,如果你读了RFC,未必不懂这些。插一个情景,来自一个电影:C开一辆车在公路上,前面有一个人D骑一辆自行车。C在追D的,C紧张的说:"你。。。你骑自行车未必比我开车快!")
问题是这些个数据包也要经过我们可怜的NAT啊,GRE会问NAT:"我这个call ID,管还是不管?",NAT答曰:"不管。"话虽如此,但是谈何容易,因为GRE里面没有端口,如果NAT不伪造一个端口的话,下面的情形就不堪设想了:
  • 一个主机A的IP地址是192.168.1.2,向1.1.1.1(声明:这里仅仅是假设,没有对其进行真正的试验,1.1.1.1请不要见怪)发起PPTP连接请求,Client-Call-ID是16384,Server-Call-ID是107;
  • 还有一个主机B在内网,IP地址是192.168.1.3,也向1.1.1.1发起PPTP连接请求,Client-Call-ID2是16385, Server-Call-ID2是108。
NAT如果仅仅是转换IP地址(假设NAT的公网IP是116.238.184.159),那么数据到能够到达1.1.1.1,可是GRE数据包回到116.238.184.159的时候,NAT这个时候是该把它发给192.168.1.2呢,还是192.168.1.3呢?在这种情况下,NAT必须要找一个"端口",来决定这个数据包是发给谁,而"端口"需要满足以下特性:
  • 在一台主机上,这个是唯一的;
  • 必须每个数据包中都存在。
而扫描整个GRE数据包,只有call ID具有此特性。所以这个关系是这样的,由于GRE数据包里面没有像TCP/UDP的端口信息,所以需要这样一个信息,而call ID的存在恰恰满足了这样一个条件。但是call ID和TCP/UDP端口有不一样的地方:
  • TCP/UDP的端口在传输层中的位置是一样的,相对偏移量是0,而call ID在传输层里面的偏移量是6;
  • TCP/UDP的端口在每个数据包里面都会携带源端口和目的端口,但是call ID仅仅携带一个,并且是指对方的。
意识到这些不同点非常重要,这会影响到NAT对待TCP/UDP和GRE要完全不同,而也正是PPTP ALG的原因之一!
嘿嘿,是否对为何需要针对GRE作特殊处理的原因了吧?好,那我们看如果保证GRE数据包能够顺利通过NAT。
我们知道,有一个NAT会话的概念(不要嫌我罗嗦。。。),里面有以下几个关键的元素:
  • 内网地址(innerip)
  • 内网端口(innerport)
  • NAT公网地址(outerip)
  • NAT公网端口(outerport)
  • 目的地址(destip)
  • 目的端口(destport)
  • 协议(Protocol)
而从内网的数据包,至少需要innerip以及innerport才能NAT出去(请读者思考protocol在什么情况下也需要作为一个依据;destip在symmetric NAT的情况下也需要考虑,不过这里我们就不关心了),而数据包返回的时候需要outerip, outerport。他们两者虽然查找的依据不一样,但是查找到的会话对于一条连接必须是同一个,这是保证通信畅通的前提。为了要使GRE顺利通过NAT,我们以主机A为例就把其分解为从里面到外面,从外面到里面两个方向。
  • 从里面到外面。这个时候,call ID是Server-Call-ID(107)。innerip是192.168.1.2,可以把Server-Call-ID作为innerport,outerip是116.238.184.159,outerport假设是1234(NAT分配的呢)。OK这个数据包可以发送给服务端;
  • 从外面到里面。这个时候,call ID是Client-Call-ID(16384),数据包的目的是116.238.184.159,那么NAT如何将其发送给192.168.1.2?大家可以看到这个时候16384帮不上任何的忙,因为outerport是1234啊。哦,等等,如果再创建一条NAT会话,把16384作为outerport,innerip作为192.168.1.2,innerport随便了(反正不用),这样不就可以发送给192.168.1.2呢?
所以,为了能够使GRE数据顺利来回,需要创建两条GRE的NAT会话,如下:
维护从里面到外面的数据传输(GRE-SESSION-1):
image
维护从外面到里面的数据传输(GRE-SESSION-2):
image
各位看到问题没有,outerport是16384,如果内网还有一台PC也是使用16384的call ID,那么这样就不出现两个outerport为16384的NAT会话呢?呵呵,这个后果很严重。所以outerport需要有NAT自己来分配一个唯一的(比如1244),这样就算内网还有一台PC使用16384,NAT也可以识别。不过既然outerport不是16384了,也就意味着一个非常重要的话题,就是NAT必须要让服务端知道Client-Call-ID是1244,而不是16384,这是PPTP ALG的另外的原因。这个带来的影响:
  • 在PPTP所有的控制包里面,如果牵扯了Client-Call-ID,都需要修改为1244,如果不修改,服务端就会认为Client-Call-ID是16384了;
  • 从服务端过来的GRE数据包中的call ID从1244还原为16384;
  • 从外面到里面的NAT会话(GRE-SESSION-2)需要存储16384,咦,上面不是说Innerport没有用到吗?我们就可以把1244存储在innerport这个位置,不过仅仅是用来作为call ID从1244还原为16384的依据。
更新后的GRE-SESSION-2应该是这个样子的:
image
这样子的话,GRE就是通行无阻了,从里面到外面匹配GRE-SESSION-1,从外面到里面匹配GRE-SESSION-2。接下来我们看看这两条会话是在什么时候生成的以及控制流中需要做的工作。
前面说到,Client-Call-ID在第一个Outgoing-Call-Request里面首次亮相,那么就可以先生成GRE-SESSION-2(获取outerport,即1244),然后根据GRE-SESSION-2中的outerport再把这个数据包中的call ID改成1244,之后就放了它。
收到Outgoing-Call-Request之后,服务端会返回Outgoing-Call-Reply,此时就可以:
  • 生成GRE-SESSION-1;
  • 把里面的Client-Call-ID从1244还原成16384。
在其后的控制连接中,再次强调,NAT你得记住有一点,只要是牵扯到Client-Call-ID,你就要动手脚。如果是从客户端到服务端,那么得将其修改成1244;如果是从服务端到客户端,那么你就得把它还原成16384。而GRE数据流,就可以让它自己匹配GRE-SESSION-1和GRE-SESSION-2了。

哦,回城了

如果是从外面主动发起请求,希望读者能够自己分析一下。给一点点提示:
  • Outgoing-Call-Request里面的Call ID(Client-Call-ID)就不用修改了,因为这个是外面的;
  • 而Outgoing-Call-Reply里面的Server-Call-ID需要修改,因为这个是属于里面的。

结语

造成PPTP需要做ALG的原因,是GRE数据里面没有携带端口,而又只能通过call ID来模拟"端口"作NAT,而由于需要模拟'端口",进而需要修改PPTP控制流中的相关call ID。
还有一个特点是,call ID的修改是单方面,而决定其是否要修改的原因就是这个call ID是由内网的机器产生还是由外网的机器产生。
由于call ID是固定2个字节,这个修改在PPTP中只需要更新TCP和IP校验和,不需要处理seq/ack问题;在GRE中只需要关心IP校验和(由于修改了IP地址),因为GRE自己压根就没有校验和。

有关于FTP的port和pasv模式以及FTP ALG

百度空间文章:ftp的port和pasv模式

一、ftp的port和pasv模式的工作方式       FTP使用2个TCP端口,首先是建立一个命令端口(控制端口),然后再产生一个数据端口。国内很多教科书都讲ftp使用21命令端口和20数据端口,这个应该是教书更新太慢的原因吧。实际上FTP分为主动模式和被动模式两种,ftp工作在主动模式使用tcp 21和20两个端口,而工作在被动模式会工作在大于1024随机端口。FTP最权威的参考见RFC 959,有兴趣的朋友可以仔细阅读ftp://nic.merit.edu/documents/rfc/rfc0959.txt的文档了解FTP详细工作模式和命令。目前主流的FTP Server服务器模式都是同时支持port和pasv两种方式,但是为了方便管理安全管理防火墙和设置ACL了解FTP Server的port和pasv模式是很有必要的。
1.1 ftp port模式(主动模式)       主动方式的FTP是这样的:客户端从一个任意的非特权端口N(N>1024)连接到FTP服务器的命令端口(即tcp 21端口)。紧接着客户端开始监听端口N+1,并发送FTP命令“port N+1”到FTP服务器。最后服务器会从它自己的数据端口(20)连接到客户端指定的数据端口(N+1),这样客户端就可以和ftp服务器建立数据传输通道了。ftp port模式工作流程如下图所示:

针对FTP服务器前面的防火墙来说,必须允许以下通讯才能支持主动方式FTP:
1、客户端口>1024端口到FTP服务器的21端口 (入:客户端初始化的连接 S<-C)
2、FTP服务器的21端口到客户端>1024的端口(出:服务器响应客户端的控制端口 S->C)
3、FTP服务器的20端口到客户端>1024的端口(出:服务器端初始化数据连接到客户端的数据端口 S->C)
4、客户端>1024端口到FTP服务器的20端口(入:客户端发送ACK响应到服务器的数据端口 S<-C)
如果服务器的ip为192.168.10.1在H3C 8500的GigabitEthernet 2/1/10 上创建in acl策略允许ftp 主动模式其他禁止:
rule permit tcp source 192.168.10.1 0 source-port eq 21 destination-port gt 1024
rule permit tcp source 192.168.10.1 0 source-port eq 20 destination-port gt 1024
rele deny ip
1.2 ftp pasv模式(被动模式)
       在被动方式FTP中,命令连接和数据连接都由客户端。当开启一个FTP连接时,客户端打开两个任意的非特权本地端口(N > 1024和N+1)。第一个端口连接服务器的21端口,但与主动方式的FTP不同,客户端不会提交PORT命令并允许服务器来回连它的数据端口,而是提交PASV命令。这样做的结果是服务器会开启一个任意的非特权端口(P > 1024),并发送PORT P命令给客户端。然后客户端发起从本地端口N+1到服务器的端口P的连接用来传送数据。ftp pasv模式工作流程如下图所示:

对于服务器端的防火墙来说,必须允许下面的通讯才能支持被动方式的FTP:
1、客户端>1024端口到服务器的21端口 (入:客户端初始化的连接 S<-C)
2、服务器的21端口到客户端>1024的端口 (出:服务器响应到客户端的控制端口的连接 S->C)
3、客户端>1024端口到服务器的大于1024端口 (入:客户端初始化数据连接到服务器指定的任意端口 S<-C)
4、服务器的大于1024端口到远程的大于1024的端口(出:服务器发送ACK响应和数据到客户端的数据端口 S->C)
如果服务器的ip为192.168.10.1在H3C 8500的GigabitEthernet 2/1/10 上创建in acl策略允许ftp 主动模式其他禁止:
rule permit tcp source 192.168.10.1 0 source-port eq 21 destination-port gt 1024
rule permit tcp source 192.168.10.1 0 source-port gt 1024 destination-port gt 1024
rele deny ip
二、ftp的port和pasv模式的工作方式       ftp的port和pasv模式最主要区别就是数据端口连接方式不同,ftp port模式只要开启服务器的21和20端口,而ftp pasv需要开启服务器大于1024所有tcp端口和21端口。重网络安全的角度来看的话似乎ftp port模式更安全,而ftp pasv更不安全,那么为什么RFC要在ftp port基础再制定一个ftp pasv模式呢?其实RFC制定ftp pasv模式的主要目的是为了数据传输安全角度出发的,因为ftp port使用固定20端口进行传输数据,那么作为黑客很容使用sniffer等探嗅器抓取ftp数据,这样一来通过ftp port模式传输数据很容易被黑客窃取,因此使用pasv方式来架设ftp server是最安全绝佳方案。
       如果作为一个有经验的网络管理员就会发现使用ftp pasv方式会给网络安全很大隐患,那就是ftp pasv需要开启服务器tcp大于1024所有端口,这样对服务器的安全保护是非常不利的。在此我建议两种方法来完善FTP Pasv模式的端口开放问题,第一种就是使用弱洞扫描工具比如Xscan找出服务器开放的端口然后使用acl把端口deny掉,另外一种方法就是使用具有状态检测防火墙开启ftp pasv的端口。
       在ftp pasv模式下是使用状态检测防火墙比acl最大的好处就是使用状态检测防火墙只要开启ftp 21端口就可以了,状态检测防火墙会检测客户端口连接ftp server的21命令端口,一但检测客户端使用ftp 21命令端口然后就会允许这个Session使用ftp服务器大于1024端口,而其他方式是无法直接访问ftp服务器大于1024端口。通过状态检测防火墙就可以保证ftp 服务器大于1024端口只对FTP Session开放了。目前像IPTable、ISA Server 2000/2004/2006、以及主流硬件防火墙都可以支持状态检测。




NAT面对FTP该如何下手

最简单的NAT只需要修改IP头里面的IP地址,不过大多数的NAT还需要传输层里面的端口。而这里我们仅仅考虑后者。在这个时候,存在着一个问题,那就是如果应用层需要使用IP地址或者端口,该怎么办?
当然,这仅仅是一个假设,许多应用不会用到网络层以及传输层里面的信息;还有一些应用会在应用层携带者IP或者端口信息,比如迅雷,但是没有它,天也不会塌下来;不过还有一些应用,非常需要它,如果没有它的话,当然天倒不会塌下来,但是一碰到NAT它却有可能没法活了,这些应用比如有FTP,PPTP,VoIP等。。。。。。此时就需要NAT从中忽悠了,这个被称为ALG(Application Layer Gateway)。
有人会有疑问,那既然这样,别在应用层数据里面使用IP或者端口信息,世界不就安宁了吗?当然,这是一个很好的建议,不过这在FTP是不可行的,因为FTP是在1985年的时候就诞生了(RFC 959),可是NAT是在1994年成为标准(RFC 1631)。但是还有很多应用比如PPTP是在1994年之后成为标准的,但是为何却不肯向NAT妥协呢?请读者讨论(当然,最好能够先问问PPTP等其他协议的作者,当时具体是如何思考这件事情的)。

FTP为何需要做ALG

FTP在进行NAT穿透的时候,根据不同的场合而有不同的结果(甚至有时候NAT不需要做任何事情):
  • 客户端是在NAT内网,还是在外网(俗称NAT静态映射);
  • 是PORT模式还是PASV模式。
根据FTP协议,FTP会使用一条数据连接来传输数据,并且这条连接的IP和端口是在控制连接里面协商的结果。而决定是否需要做ALG的,就是要看在协商的时候是携带了内网IP和内网端口信息,如果是的话,就需要做ALG。以下分四种情况讨论。

客户端在内网,使用PASV模式

比如我现在以IP为192.168.16.5,源端口为1315,然后通过firefox直接FTP到www.kernel.org。首先会建立一条控制连接:
image
那么在这条连接里面,双方会协商数据传输的端口,在这里采用的是PASV模式:
image
注意后面的(149,20,20,133,127,85),这个就是本文的灵魂所在(NAT就是需要对这一串数字知根知底,顺便提一下《LOST》里面的4, 8, 15, 16, 23, 42)。在PASV模式下其实就是服务端告诉客户端其IP地址以及其开放的端口,通过以下方式运算得到:
  • 前面4个字节代表的是IP,逗号是分隔符,这里是:
image
转换成IP其实就是149.20.20.133,就是服务器的IP
  • 后面两个代表的是端口,这里是:
image
即为32597
PASV模式意味着,服务端打开了数据端口,这里是开启了32597端口,所以接下来了客户端会向服务端的32597端口发起连接:
image
在这种情况下,由于IP和端口是公网的,所以不需要做ALG了。一般的FTP客户端都是使用了这样PASV的方式,所以应用上一般都没有异常。

客户端在内网,使用PORT模式

我使用Firefox的一个插件FireFTP作为FTP客户端,并且设置为PORT模式,仍然FTP到149.20.20.133,并且传输了一个文件:
image
这个时候,我们可以看到,情况有点不一样了,此时是客户端直接把自己的IP和开放的端口告诉给了服务端了,在这里IP是192.168.16.5,端口是2230。服务端能够向192.168.16.5的2230端口发起数据请求吗?答案是否定的。这个时候NAT需要勇敢的站出来了!
  • 首先不管怎么说,NAT得把192.168.16.5这个内网的IP修改成公网的IP,这里是116.238.184.142,以便149.20.20.133能够访问得到;
  • 端口呢?端口是否需要修改?这得分情况,如果说NAT出去的端口中没有2230这个端口,那么可以不修改;但是如果这个端口已经被占用了,那么就必须得修改,否则就会出现一台主机(NAT设备)出现两个相同的端口,比如现在我们将其修改成12345,修改之后还必须要添加一条NAT会话(相当于作了一条静态映射),以便服务端发起数据连接的时候能够匹配到;
  • 还有一个事情必须得记住,那就是这些IP信息和端口信息是以ASCII码传输的,所以可能会造成数据包修改前后,长度发生了变化!比如从192,168,16,5,8,182修改成116,238,184,142,48,57就是从18个字节增加到了21个字节了。所以这个数据包的修改需要同时更新IPLEN字段,并且你得记住你的长度变化(这里是3个字节),因为服务端响应的是后面修改的数据,所以ACK会比客户端预期的多3,当ACK从服务端过来的时候,NAT要把ACK减掉3,以便让192.168.16.5认识。事情还没有完,由于ACK减少了,这样客户端发送出来的SEQ就是这个被减少了的ACK,这样服务端也不干啊,所以针对客户端发送出去的SEQ,NAT需要加上3。以后在这条连接上面,NAT需要做这样的事情over and over again去忽悠客户端和服务端;
  • 最后别忘了更新IP校验和,以及TCP校验和。

客户端在外网,使用PASV模式

如果在NAT设备内网搭建了一台FTP服务器,客户端在外网来对其进行访问,结果将和前面的不一样。首先需要映射FTP服务器的端口,标准是FTP/21。而此时由于是服务端报告自己的IP和端口信息,并且传输给客户端,由于服务端的IP信息是内网的,所以NAT需要对其进行修改,如何修改呢?留给读者去分析。

客户端在外网,使用PORT模式

呵呵,想想看看,这个是客户端(外网)报告自己的IP和端口信息,哦,不用动。。。。。。

后记

在使用FTP的时候,并且处于NAT的环境之下,如果出现异常的话,就可以怀疑怀疑是否是ALG功能失效导致。