一、概述
1.简介
NAT英文全称是“Network Address Translation”,中文意思是“网络地址转换”,它是一个IETF(Internet Engineering Task Force, Internet工程任务组)标准,允许一个整体机构以一个公用IP(Internet Protocol)地址出现在Internet上。顾名思义,它是一种把内部私有网络地址(IP地址)翻译成合法网络IP地址的技术。因此我们可以认为,NAT在一定程度上,能够有效的解决公网地址不足的问题。
2.分类
NAT有三种类型:静态NAT(Static NAT)、动态地址NAT(Pooled NAT)、网络地址端口转换NAPT(Port-Level NAT)。
(1)静态NAT:静态地址转换将内部私网地址与合法公网地址进行一对一的转换,且每个内部地址的转换都是确定的。
(2)动态NAT:动态地址转换也是将内部本地地址与内部合法地址一对一的转换,但是动态地址转换是从合法地址池中动态选择一个未使用的地址来对内部私有地址进行转换。
(3)NAPT:它也是一种动态转换,而且多个内部地址被转换成同一个合法公网地址,使用不同的端口号来区分不同的主机,不同的进程。
从实现的技术角度,又可以将NAT分成如下几类:全锥NAT(Full Cone NAT)、限制性锥NAT(Restricted Cone NAT)、端口限制性锥NAT( PortRestricted Cone NAT)、对称NAT ( Symmetric NAT)。
(1)全锥NAT:全锥NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。任何一个外部主机均可通过该映射发送数据包到该内部主机。
(2)限制性锥NAT:限制性锥NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。但是,和全锥NAT不同的是:只有当内部主机先给外部主机发送数据包,该外部主机才能向该内部主机发送数据包。
(3)端口限制性锥NAT:端口限制性锥NAT与限制性锥NAT类似,只是多了端口号的限制,即只有内部主机先向外部地址:端口号对发送数据包,该外部主机才能使用特定的端口号向内部主机发送数据包。
(4)对称NAT:对称NAT与上述3种类型都不同,不管是全锥NAT,限制性锥NAT还是端口限制性锥NAT,它们都属于锥NAT(ConeNAT)。当同一内部主机使用相同的端口与不同地址的外部主机进行通信时, 对称NAT会重新建立一个Session,为这个Session分配不同的端口号,或许还会改变IP地址。
3 应用
NAT主要可以实现以下几个功能:数据包伪装、平衡负载、端口转发和透明代理。
数据伪装: 可以将内网数据包中的地址信息更改成统一的对外地址信息,不让内网主机直接暴露在因特网上,保证内网主机的安全。同时,该功能也常用来实现共享上网。
端口转发: 当内网主机对外提供服务时,由于使用的是内部私有IP地址,外网无法直接访问。因此,需要在网关上进行端口转发,将特定服务的数据包转发给内网主机。
负载平衡: 目的地址转换NAT可以重定向一些服务器的连接到其他随机选定的服务器。
失效终结: 目的地址转换NAT可以用来提供高可靠性的服务。如果一个系统有一台通过路由器访问的关键服务器,一旦路由器检测到该服务器当机,它可以使用目的地址转换NAT透明的把连接转移到一个备份服务器上。
透明代理: NAT可以把连接到因特网的HTTP连接重定向到一个指定的HTTP代理服务器以缓存数据和过滤请求。一些因特网服务提供商就使用这种技术来减少带宽的使用而不用让他们的客户配置他们的浏览器支持代理连接。
二、原理
2.1 地址转换
NAT的基本工作原理是,当私有网主机和公共网主机通信的IP包经过NAT网关时,将IP包中的源IP或目的IP在私有IP和NAT的公共IP之间进行转换。
如下图所示,NAT网关有2个网络端口,其中公共网络端口的IP地址是统一分配的公共 IP,为202.20.65.5;私有网络端口的IP地址是保留地址,为192.168.1.1。私有网中的主机192.168.1.2向公共网中的主机202.20.65.4发送了1个IP包(Dst=202.20.65.4,Src=192.168.1.2)。
当IP包经过NAT网关时,NAT Gateway会将IP包的源IP转换为NAT Gateway的公共IP并转发到公共网,此时IP包(Dst=202.20.65.4,Src=202.20.65.5)中已经不含任何私有网IP的信息。由于IP包的源IP已经被转换成NAT Gateway的公共IP,Web Server发出的响应IP包(Dst=202.20.65.5,Src=202.20.65.4)将被发送到NAT Gateway。
这时,NAT Gateway会将IP包的目的IP转换成私有网中主机的IP,然后将IP包(Des=192.168.1.2,Src=202.20.65.4)转发到私有网。对于通信双方而言,这种地址的转换过程是完全透明的。转换示意图如下。
如果内网主机发出的请求包未经过NAT,那么当Web Server收到请求包,回复的响应包中的目的地址就是私网IP地址,在Internet上无法正确送达,导致连接失败。
2.2 连接跟踪
在上述过程中,NAT Gateway在收到响应包后,就需要判断将数据包转发给谁。此时如果子网内仅有少量客户机,可以用静态NAT手工指定;但如果内网有多台客户机,并且各自访问不同网站,这时候就需要连接跟踪(connection track)。如下图所示:
在NAT Gateway收到客户机发来的请求包后,做源地址转换,并且将该连接记录保存下来,当NAT Gateway收到服务器来的响应包后,查找Track Table,确定转发目标,做目的地址转换,转发给客户机。
2.3 端口转换
以上述客户机访问服务器为例,当仅有一台客户机访问服务器时,NAT Gateway只须更改数据包的源IP或目的IP即可正常通讯。但是如果Client A和Client B同时访问Web Server,那么当NAT Gateway收到响应包的时候,就无法判断将数据包转发给哪台客户机,如下图所示。
此时,NAT Gateway会在Connection Track中加入端口信息加以区分。如果两客户机访问同一服务器的源端口不同,那么在Track Table里加入端口信息即可区分,如果源端口正好相同,那么在时行SNAT和DNAT的同时对源端口也要做相应的转换,如下图所示。
三、Linux下NAT实现
3.1 netfilter/iptables模块
netfilter/iptables(IP信息包过滤系统)是一种功能强大的工具,根据数据包过滤规则,对经过的网络数据包进行丢弃、改造、转发等处理。
netfilter组件也称为内核空间(kernelspace),是内核的一部分,由一些数据包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集。
iptables组件是一种工具,也称为用户空间(userspace),它主要用来向用户提供添加、修改、删除内核中数据过滤表的接口。
3.2 基于netfilter/iptables的NAT
netfilter/iptables中的数据包过滤表有三种:filter、nat和mangle。
filter 表用于一般的信息包过滤,它包含 INPUT 、 OUTPUT 和 FORWARD 链。
nat 表用于要转发的信息包,它包含 PREROUTING 、 OUTPUT 和 POSTROUTING 链。
如果信息包及其头内进行了任何更改,则使用 mangle 表。该表包含一些规则来标记用于高级路由的信息包,该表包含 PREROUTING 和 OUTPUT 链。
filter 表用来过滤数据包,我们可以在任何时候匹配包并过滤它们。Mangle不经常使用还在开发当中。我们下面主要介绍Nat表来实现NAT功能。
(1)用户使用iptables命令在用户空间设置NAT规则。通过使用用户空间iptables命令,可以构建用户自己的定制NAT规则。所有规则存储在内核空间的nat表中。根据规则所处理的信息包类型,将规则分组在链中。要做SNAT的信息包被添加到POSTROUTING链中。要做DNAT的信息包被添加到PREROUTING链中。直接从本地出站的信息包的规则被添加到OUTPUT 链中。
(2)内核空间接管NAT工作.做过NAT操作的数据包的地址就被改变了,当然这种改变是根据我们的规则进行的。属于一个流的包只会经过这个表一次。如果第一个包被允许做NAT或 Masqueraded,那么余下的包都会自动地被做相同的操作。也就是说,余下的包不会再通过这个表,一个一个的被NAT,而是自动地完成。这就是我们为什么不应该在这个表中做任何过滤的主要原因。PREROUTING 链的作用是在包刚刚到达防火墙时改变它的目的地址,如果需要的话。OUTPUT链改变本地产生的包的目的地址。下图是数据包穿越整个netfilter/iptables的流程图。
(3)NAT工作步骤:
DNAT:若包是被送往PREROUTING链的,并且匹配了规则,则执行DNAT或REDIRECT目标。为了使数据包得到正确路由,必须在路由之前进行DNAT。
路由:内核检查信息包的头信息,尤其是信息包的目的地。
处理本地进程产生的包:对nat表OUTPUT链中的规则实施规则检查,对匹配的包执行目标动作。
SNAT:若包是被送往POSTROUTING链的,并且匹配了规则,则执行SNAT或MASQUERADE目标。系统在决定了数据包的路由之后才执行该链中的规则。
四、P2P穿透NAT
要让处于NAT设备之后的拥有私有IP地址的主机之间建立P2P连接,就必须想办法穿透NAT,现在常用的传输层协议主要有TCP和UDP,下面就是用这两种协议来介绍穿透NAT的策略。
4.1. 网络拓扑结构
下面假设有如图1所示网络拓扑结构图。
Server(129.208.12.38)是公网上的服务器,NAT-A和NAT-B是两个NAT设备(可能是集成NAT功能的路由器,防火墙等),它们具有若干个合法公网IP,在NAT-A阻隔的私有网络中有若干台主机【ClientA-1,ClientA-N】,在NAT-B阻隔的私有网络中也有若干台主机【ClientB-1,ClientB-N】。为了以后说明问题方便,只讨论主机ClientA-1和ClientB-1。
假设主机ClientA-1和主机ClientB-1都和服务器Server建立了“连接”,由于NAT的透明性,所以ClientA-1和ClientB-1不用关心和Server通信的过程,它们只需要知道Server开放服务的地址和端口号即可。根据图,假设在ClientA-1中有进程使用socket(192.168.0.2:7000)和Server通信,在ClientB-1中有进程使用socket(192.168.1.12:8000)和Server通信。它们通过各自的NAT转换后分别变成了socket(202.103.142.29:5000)和socket(221.10.145.84:6000)。
4.2. 使用UDP穿透NAT
通常情况下,当进程使用UDP和外部主机通信时,NAT会建立一个Session,这个Session能够保留多久并没有标准,或许几秒,几分钟,几个小时。假设ClientA-1在应用程序中看到了ClientB-1在线,并且想和ClientB-1通信,一种办法是Server作为中间人,负责转发ClientA-1和ClientB-1之间的消息,但是这样服务器太累,会吃不消。另一种方法就是让ClientA-1何ClientB-1建立端到端的连接,然后他们自己通信。这也就是P2P连接。根据不同类型的NAT,下面分别讲解。
(1)全锥NAT,穿透全锥型NAT很容易,根本称不上穿透,因为全锥型NAT将内部主机的映射到确定的地址,不会阻止从外部发送的连接请求,所以可以不用任何辅助手段就可以建立连接。
(2)限制性锥NAT和端口限制性锥NAT(简称限制性NAT),穿透限制性锥NAT会丢弃它未知的源地址发向内部主机的数据包。所以如果现在ClientA-1直接发送UDP数据包到ClientB-1,那么数据包将会被NAT-B无情的丢弃。所以采用下面的方法来建立ClientA-1和ClientB-1之间的通信。
1.ClientA-1(202.103.142.29:5000)发送数据包给Server,请求和ClientB-1(221.10.145.84:6000)通信。
2. Server将ClientA-1的地址和端口(202.103.142.29:5000)发送给ClientB-1,告诉ClientB-1,ClientA-1想和它通信。
3. ClientB-1向ClientA-1(202.103.142.29:5000)发送UDP数据包,当然这个包在到达NAT-A的时候,还是会被丢弃,这并不是关键的,因为发送这个UDP包只是为了让NAT-B记住这次通信的目的地址:端口号,当下次以这个地址和端口为源的数据到达的时候就不会被NAT-B丢弃,这样就在NAT-B上打了一个从ClientB-1到ClientA-1的孔。
4. 为了让ClientA-1知道什么时候才可以向ClientB-1发送数据,所以ClientB-1在向ClientA-1(202.103.142.29:5000)打孔之后还要向Server发送一个消息,告诉Server它已经准备好了。
5. Server发送一个消息给ClientA-1,内容为:ClientB-1已经准备好了,你可以向ClientB-1发送消息了。
6. ClientA-1向ClientB-1发送UDP数据包。这个数据包不会被NAT-B丢弃,以后ClientB-1向ClientA-1发送的数据包也不会被ClientA-1丢弃,因为NAT-A已经知道是ClientA-1首先发起的通信。至此,ClientA-1和ClientB-1就可以进行通信了。
4.3. 使用TCP穿透NAT
使用TCP协议穿透NAT的方式和使用UDP协议穿透NAT的方式几乎一样,没有什么本质上的区别,只是将无连接的UDP变成了面向连接的TCP。值得注意是:
1.ClientB-1在向ClientA-1打孔时,发送的SYN数据包,而且同样会被NAT-A丢弃。同时,ClientB-1需要在原来的socket上监听,由于重用socket,所以需要将socket属性设置为SO_REUSEADDR。
2.ClientA-1向ClientB-1发送连接请求。同样,由于ClientB-1到ClientA-1方向的孔已经打好,所以连接会成功,经过3次握手后,ClientA-1到ClientB-1之间的连接就建立起来了。
4.4. 穿透对称NAT
上面讨论的都是怎样穿透锥(Cone)NAT,对称NAT和锥NAT很不一样。对于对称NAT,当一个私网内主机和外部多个不同主机通信时,对称NAT并不会像锥(Cone,全锥,限制性锥,端口限制性锥)NAT那样分配同一个端口。而是会新建立一个Session,重新分配一个端口。参考上面穿透限制性锥NAT的过程,在步骤3时:ClientB-1(221.10.145.84:?)向ClientA-1打孔的时候,对称NAT将给ClientB-1重新分配一个端口号,而这个端口号对于Server、ClientB-1、ClientA-1来说都是未知的。同样, ClientA-1根本不会收到这个消息,同时在步骤4,ClientB-1发送给Server的通知消息中,ClientB-1的socket依旧是(221.10.145.84:6000)。而且,在步骤6时:ClientA-1向它所知道但错误的ClientB-1发送数据包时,NAT-1也会重新给ClientA-1分配端口号。所以,穿透对称NAT的机会很小。下面是两种有可能穿透对称NAT的策略。
4.4.1.同时开放TCP(SimultaneousTCP open)策略
如果一个对称NAT接收到一个来自本地私有网络外面的TCP SYN 包,这个包想发起一个“引入”的 TCP 连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN包,或者回送一个TCP RST(connection reset,重建连接)包给请求方。但是,有一种情况却会接受这个“引入”连接。
RFC规定:对于对称NAT,当这个接收到的SYN包中的源IP地址:端口、目标IP地址:端口都与NAT登记的一个已经激活的TCP会话中的地址信息相符时,NAT将会放行这个SYN 包。需要特别指出的是:怎样才是一个已经激活的TCP连接?除了真正已经建立完成的TCP连接外,RFC规范指出:如果NAT恰好看到一个刚刚发送出去的一个SYN包和随之接收到的SYN包中的地址:端口信息相符合的话,那么NAT将会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部。同时开放TCP策略就是利用这个时机来建立连接的。
如果 Client A-1和 Client B-1能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个面向对方的“外出”的TCP连接请求,并在对方的 SYN 包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就能成功地建立了。
4.4.2. UDP端口猜测策略
同时开放TCP策略非常依赖于猜测对方的下一个端口,而且强烈依赖于发送连接请求的时机,而且还有网络的不确定性,所以能够建立的机会很小,即使Server充当同步时钟的角色。下面是一种通过UDP穿透的方法,由于UDP不需要建立连接,所以也就不需要考虑“同时开放”的问题。
为了介绍ClientB-1的诡计,先介绍一下STUN协议。STUN(SimpleTraversal of UDP Through NATs)协议是一个轻量级协议,用来探测被NAT映射后的地址:端口。STUN采用C/S结构,需要探测自己被NAT转换后的地址:端口的Client向Server发送请求,Server返回Client转换后的地址:端口。
参考4.2节中穿透NAT的步骤2,当ClientB-1收到Server发送给它的消息后,ClientB-1即打开3个socket。socket-0向STUN Server发送请求,收到回复后,假设得知它被转换后的地址:端口(221.10.145.84:6005),socket-1向ClientA-1发送一个UDP包,socket-2再次向另一个STUNServer发送请求,假设得到它被转换后的地址:端口(221.10.145.84:6020)。通常,对称NAT分配端口有两种策略,一种是按顺序增加,一种是随机分配。如果这里对称NAT使用顺序增加策略,那么,ClientB-1将两次收到的地址:端口发送给Server后,Server就可以通知ClientA-1在这个端口范围内猜测刚才ClientB-1发送给它的socket-1中被NAT映射后的地址:端口,ClientA-1很有可能在孔有效期内成功猜测到端口号,从而和ClientB-1成功通信。
4.4.3. 问题总结
从上面两种穿透对称NAT的方法来看,都建立在了严格的假设条件下。但是现实中多数的NAT都是锥NAT,因为资源毕竟很重要,反观对称NAT,由于太不节约端口号所以相对来说成本较高。所以,不管是穿透锥NAT,还是对称NAT,现实中都是可以办到的。除非对称NAT真的使用随机算法来分配可用的端口。
应师妹要求,字体变大些。。。