在上图中,ICG网关后面有两台主机分别是有线主机192.168.1.2和无线主机192.168.1.3,现在这两台主机都要访问网站www.tektalk.cn(弯曲评论,域名解析后地址为74.220.215.202),根据专栏第三期介绍,ICG要将内部主机地址进行转换(NAT),换成网关的WAN口地址发起访问,否则网站数据无法正确返回。
我们先看看问题是怎么产生的,192.168.1.2和192.168.1.3同时访问弯曲评论,网关NAT后,数据包的源和目的都是一致的([源地址78.145.16.88;目的地址74.220.215.202],为了叙述方便,简写为[源地址“ICG网关”;目的地址“弯曲评论”]),因此弯曲评论在返回数据时,源和目的也是一致[源地址“弯曲评论”;目的地址“ICG网关”],为了聚焦NAT转换,图中将保持不变的弯曲评论地址略去。
那么问题就出现了,ICG同时收到两份数据(目的地址是ICG网关),ICG该如何处理呢?
1. 由于目的地址是ICG网关,所以全部由网关接收处理;后果是内部主机都无法打开网页
2. 由于192.168.1.2访问弯曲评论,所以全部转交给192.168.1.2;后果是无线主机无法打开网页
3. 由于192.168.1.3访问弯曲评论,所以全部转交给192.168.1.3;后果是有线主机无法打开网页
4. 智能地将一个包转给192.168.1.2,另外一个转交给192.168.1.3;没有后果,无线有线主机都能打开网页
经过以上比较,我们可以发现第四种处理方式最为合理,这种方式我们就称为“互联网应用穿越NAT”简称为“NAT穿越”。接下来就重点介绍ICG如何实现第四种方式的。
要求ICG能够对两个源目的地址完全一致的数据包进行智能的处理,将一个包交给有线主机,另外一个交给无线主机,并且两个包不能弄混,那么这两个包必然携带了内部主机信息,能够使ICG根据这个信息作正确的区分。
那么这个信息是什么呢?其实很简单,也许不少读者也已经想到了,是端口号,确切地说是TCP/UDP端口号。下面是TCP/UDP端口号在NAT中的转换原理。
从上图可以发现:
1. 有线主机打开浏览器访问弯曲评论,为何访问弯曲评论的时候目的端口是80,源端口却是1025呢?因为网页应用是著名TCP应用,使用保留端口范围[0-1023]中的80,而一般客户端发起访问时(除非应用特殊规定),源端口只能采用非保留端口范围[1024-65535],在该例中,有线主机使用1025端口发起访问。
2. 当ICG收到该数据包时,首先从网关端口池中取一个空闲端口,假设是13023,然后使用“ICG网关地址+13023端口”替换“有线主机+1025端口”,替换后还需要更新转换表项,这个转换表项就是解答前面问题的关键,转换表项是需要计时的,因为网关端口池内可用端口数量有6万多个(理论上从1024到65535,实际上有可能还会缩减),通常主机打开新浪的主页需要占用端口达到200多个,如果没有转换计时机制,端口池的空闲端口很容易消耗掉,没有空闲端口意味着无法转换,也就无法访问应用;通过计时机制,将不活跃的端口重新回收至端口池,以循环利用的方式能够应对大部分网络场景,通常TCP端口转换计时300s(300s内没有更新或被引用删除表项回收端口),UDP则是240s,对于网络应用来说,这个计时已经足够(TCP通常10s内没有数据传递就需要重传甚至重新连接)。
3. 弯曲评论收到数据,根据请求中的源地址、源端口进行回复,可以看到回复的目的地址是ICG网关,目的端口也是端口池中取出的13023。
4. ICG网关收到回复,此时要检查转换表中是否有对应的源转换表项:
a) 抽取数据包的5元组[协议、目的地址、目的端口、源地址、源端口]
b) 逐项匹配转发表5元组[协议、外部地址、外部端口、目的地址、目的端口]
c) 在上图中,ICG网关顺利找到一条表项,如果没有匹配的表项则会进入3元组匹配(3元组匹配详见最后一节),如果3元组也无法匹配则丢弃数据包
d) 将然后将数据包的目的地址和目的端口替换成表项中的内部地址、内部端口
e) 在举例中目的地址从“ICG网关”换成“有线主机”,目的端口从13023换成1025后发送给有线主机。
接下面来再看有线主机和无线主机同时访问弯曲评论的原理图。
可以发现弯曲评论返回目的地址完全一致的两个数据包,因为目的端口不同的原因,ICG网关能够正确地把数据包分别返回给有线主机和无线主机。
上述地址端口转换原理就是通常所说的NATPT(网络地址转换和端口转换),是目前应用最广泛的NAT技术,以至于通常所说的NAT其实指的就是NATPT,单纯意义上的地址转换由于地址利用率低而极少被使用。
那么还有哪些技术能够穿越NAT呢?我们把支持内部多台主机同时访问同一个外部应用称为支持,如果同一时刻只能由1台主机访问,那么就不支持NAT穿越。
| TCP应用 | UDP应用 | ICMP应用 | 其它协议 |
支持内部主机数量 | 多个 | 多个 | 多个 | 1个 |
应用举例 | 网页、电邮、聊天、下载、SSL VPN…… | 基于端口的Tracert、L2TP VPN、DNS、NTP…… | Ping、基于Ping的Tracert | GRE、ESP、AH |
幸运的是,互联网大部分应用都是TCP应用,TCP和UDP应用合起来占互联网应用类型的99%,更幸运的是我们常用的非TCP/UDP应用Ping也是可以穿越NAT的,而GRE隧道和IPSec使用的(ESP、AH)则无法穿越NAT,那么GRE、IPSec是不是在NAT环境中就无法使用了呢,当然不是。ESP结合UDP后能够穿越,GRE结合ESP因而也能够穿越,而AH则因为保护地址而无法穿越(在IPSec详解时会具体介绍)。
为什么TCP、UDP、ICMP能够穿越,而GRE、ESP、AH无法穿越呢,我们来看如下数据包对比(以下数据包截图采用WireShark解析)。
ICMP抓包可以发现源地址+Identifier(请求和应答使用相同的Identifier)可以作为区分内部主机的条件,也可以像TCP/UDP一样穿越NAT,ICMP的转换表项实例如下。
协议 | 外部地址 | 外部端口 | 内部地址 | 内部端口 | 目的地址 | 目的端口 |
ICMP | ICG网关 | 1320 | 有线主机 | 2 | 弯曲评论 | 2 |
…… |
|
|
|
|
|
|
为什么ICMP的表项也叫端口呢,ICG为了统一所有协议转换表项,将ICMP中的Identifier作为端口来处理。
上图是GRE的抓包,可以发现GRE里头没有类似于ICMP的Identifier字段,因此没有办法区分多个内部主机,只能支持1台内部主机对外建立GRE隧道。
从ESP封装我们似乎发现可以通过安全参数索引来区分内部不同的主机,实际上SPI的确可以区分出不同内部主机;但安全参数索引是源和目的协商出来的,ESP加密、解密、计算校验使用的密钥存在一一对应关系,如果ICG网关使用自己的安全参数索引进行替代,那么接收方将找不到正确的密钥解密,因此NATPT无法支持多个内部主机同时进行ESP通信,只能支持1台内部主机。ICG网关包括GRE和ESP协议的完整NATPT表项如下表所示。
协议 | 外部地址 | 外部端口 | 内部地址 | 内部端口 | 目的地址 | 目的端口 |
GRE | ICG网关 | - | 有线主机 | - | 弯曲评论 | - |
ESP | ICG网关 | - | 无线主机 | - | 弯曲评论 | - |
ICMP | ICG网关 | 1320 | 有线主机 | 2 | 弯曲评论 | 2 |
ICMP | ICG网关 | 1321 | 无线主机 | 2 | 弯曲评论 | 2 |
UDP | ICG网关 | 14040 | 无线主机 | 1035 | 弯曲评论 | 500 |
TCP | ICG网关 | 13023 | 有线主机 | 1025 | 弯曲评论 | 80 |
TCP | ICG网关 | 13024 | 无线主机 | 1026 | 弯曲评论 | 80 |
…… |
|
|
|
|
|
|
如上所述,能够进行NAT穿越的应用的必要条件是
1. 除了源IP地址外,还有其余类似于TCP/UDP端口、ICMP的Identifier的标志位和内部主机的应用进行绑定
2. 该标志位和密钥、认证无关,因为如果和密钥或认证相关,网关修改该标志位后会导致解密和认证失败
如上2个条件同时满足就是NAT穿越的充分条件。
上图所示场景可以说是NAT穿越的终极场景,目前广泛使用的P2P就使用大量这种连接提高传输效率,如何解决问题大家想到的方法可能如下:
1. 由有线主机或无线主机直接向对端发起访问,假设是有线主机直接访问无线主机,那么访问哪个地址呢,大家可能会回答访问161.71.89.3,那么有线主机怎么知道访问161.71.89.3,大家可能也会回答图上画的,但实际互联网应用的时候我们手上并没有这么一张图告诉我们这个主机在这个网关之后,那个主机在那个网关之后,再者互联网上主机几十亿台,完成这张图简直是Mission Impossible,即使我们通过打电话、写信的方式获得了对方的地址、协议(TCP)、端口(2000),那么当访问发送给161.71.89.3时,会出现什么状况呢?
为什么访问会被丢弃呢,首先2000端口是无线主机的端口,所以ICG网关会查找NAT转换表项,是否存在[协议TCP;外部地址161.71.89.3;外部端口2000]的3元组表项,但是内部主机是无法指定外部端口的,外部端口是由ICG网关动态从端口池中取得,所以即使找到这个2000端口,也未必对应无线主机192.168.1.2,所以该方法被毙。
2. 既然外部端口是动态的,那么我们使用静态端口吧,如果使用静态端口,那么就是内部服务器应用了,而不是NAT了,对于P2P这种自动建立大量连接的应用,要手工指定端口映射显然是不现实的,因为P2P应用使用端口量大,而且端口经常变化。
3. 借助外部服务器如弯曲评论的帮忙。
首先有线和无线主机都要向服务器发起注册,连接中携带用户名,这样服务器收到连接后就能够将注册信息中携带的IP地址、端口和用户名关联起来,做成一个表,在BT技术中这种表叫做Trace表。
假设有线主机向无线主机发起连接,那么首先去服务器检索无线主机的IP地址和端口信息,检索到后向对应的IP地址、端口发起连接:
1. 当数据包到达无线网关ICG时,数据包5元组是[TCP、无线ICG网关、13000、有线ICG网关、12001]
2. 在无线网关的转发表项中找不到相匹配的5元组表项
3. 此时会按照3元组[TCP、无线ICG网关、13000]匹配,可以找到如下表项
协议 | 外部地址 | 外部端口 | 内部地址 | 内部端口 | 目的地址 | 目的端口 |
TCP | 无线ICG网关 | 13000 | 无线主机 | 1025 | 弯曲评论 | 80 |
…… |
|
|
|
|
|
|
4. 将数据包目的地址“无线ICG网关”换成内部地址“无线主机”,再将目的端口13000换成1025
5. 替换好后发送给无线主机
上述介绍的是P2P穿越NAT的原理,实际上的P2P协议要比这个复杂,可能涉及到分布式服务器、许多端口,但是任何P2P通信具体到每个数据包时都是之和一个服务器的一个端口进行通信,因此依然是符合上述原理的。
P2P技术除了要解决穿越NAT,另外一个要穿越的是防火墙,因为大部分防火墙不进行3元组匹配,因为3元组匹配使任何外网主机都能够访问内网某个端口,在防火墙安全区域理论中,这属于需要禁止的非信任域主机主动访问信任域主机,在以后的专栏中会对P2P穿越防火墙进行介绍。