NAT原理分析
关键词: NAT,NATP
摘要:
缩略语:
NAT: Network Address Translator
NAPT:Network Address Port Translator
ALG:Application Specification Gateway
NAT(Network Address Translation),中文称为:网络地址转换器,其基本的含义指互联网络中的中继设备在转发分组时改变分组的网络层地址、甚至上层地址的一种方法。NAT的实现并没有统一的规范,各个产品(包括Linux)根据应用的需要独立完成。IETF的NAT工作组在2001年推出Traditional IP NAT的RFC,其中阐述了Basic Network Address Translation和Network Address Port Translation的原理。
Basic NAT是一种把一组IP地址映射成另一组IP地址的方法,映射的过程在IP中继设备上完成,对用户完全透明。NAPT则要复杂一些,它把许多(不能太多)IP地址连同TCP/UDP端口号映射到单独一个IP地址和端口号上。无论是Basic NAT还是NAPT都提供一种把内部的私有地址转换成在公网上可用的全球唯一IP地址的方法。
NAT可以帮助那些没有足够IP地址资源的用户,比如:公司和企业,实现IP地址资源共享。但上述两种NAT的这方面的能力有所不同。Basic NAT需要一个比较大的外部地址池,直接的地址映射使得外部地址数目要与最大同时上网数基本相同。也可以采用一些动态绑定的方法来尽可能的增加外部地址的使用效率,但从根本上而言,Basic NAT并不能解决外部地址稀缺的矛盾;它可能更多的应用在大部分流量位于私网内部,少部分流量到公网的企业网中。
NAPT则有所不同,因为分组中参与映射的部分包括了私网的IP地址和传输层的端口号,因此被映射的公网IP地址最少可以只有一个。NAPT主要应用在SOHO,个人节点和小公司的情况。这类网络环境的特点是:有多个IP终端设备需要访问公网,但仅有一个公网IP。
在完美的世界中(IPv6?),并不需要NAT。正是因为IPv4资源太宝贵了,才会有NAT这种折衷的措施。尽管如此,NAT也带来一些额外的收获,比如:为服务器集群提供负载均衡的手段,隐藏内部网络地址以免受黑客的直接攻击,等等。
NAT也有其局限性,具体而言就是:同一个Session中的交互报文必须经由同一个NAT路由器。因此,NAT路由器通常是一个单出口网络的边缘路由器,所有要出去的报文必须经由这个路由器。在有的情况下,比如:企业网有多个ISP接口,也即一个私网有多个出口,这时NAT只能在其中的一个出口生效;其它的出口只能作为备用,当主接口失效后才能切换到备用接口。
此外,使用NAT就意味着损失IP地址的端到端的性质所带来的某些特性。比如,IPSec和其它一些网络层上的安全措施。不是所有应用层协议都能顺利的运行在NAT的网络上,一旦协议中包含了与IP地址有关的内容,经过NAT转换后都会失效。这方面最著名的例子就是FTP协议。如果需要支持这类型的协议,必须有特定的ALG处理这类型的报文。
Basic NAT是一种1:1映射的NAT,其基本原理如下:私网是一个单出口网络(stub network),与公网的接口处做地址转换。NAT的公网一侧分配有一个地址段的“合法”IP,私网一侧则分配保留IP。地址映射关系可以静态配置,也可以动态绑定。私网访问公网的数据报文经过NAT时,源IP地址被改写成所映射的公网IP地址;公网到私网的数据报文经NAT时,目的IP地址被改写成映射的私网IP地址。
采用静态配置的方式,必须保证公网IP数目与私网IP数目一样多,否则没有被配置的私网IP地址将不能访问公网。动态绑定方式则要灵活得多,公网IP的数量可以少于私网IP的数量,当有较多用户同时访问公网时,公网IP可能不足,会导致部分访问的请求失败。管理员可以根据私网中对公网访问的最大并发数来配置公网IP地址数。此外,其它的一些管理措施也能增加对公网IP地址的使用率,比如:限制用户对公网IP的绑定时间,以及解除长期为使用的IP绑定。
Figure2.1 Basic NAT 示意图
在多数的企业网和小办公室的网络中,通常都只有一个WAN接口和一个“合法”IP地址,私网内的众多节点对公网的访问都由出口处的路由器完成地址转换。NAPT是一种“一对多”的地址映射方法,公网IP只有一个,与之映射的私网IP若干,因此只得把传输层的TCP/UDP端口或者ICMP的Identifier也加入进来,以(私网地址, 本地传输层端口号)——(公网地址, 分配的传输层端口号)。
NAPT的限制相对较多,通常只有TCP/UDP会话才允许穿过路由器,而且会话的发起者必须是私网里的主机。ICMP报文,除了REDIRECT报文外,穿过NAPT路由器都需要用ICMP报文中的Identifier域做映射。ICMP Identifier由Query方生成,Response方则使用这个Identifier,以此作为区别不同ICMP报文。
Figure 3.1 NAPT示意图
在一个Basic NAT的会话中,私网中的主机发出的第一个到公网的IP报文将会触发NAT路由器为这个私网IP地址绑定一个公网IP。从此之后,所有源地址为该私网IP的报文都使用这个绑定。如果NAT路由器采用动态绑定的策略,可以对这个会话计时,当会话有很长时间的空闲后,可以把绑定解除,释放被分配的公网地址。如果用户采取静态绑定的策略,则需要由管理员来配置公网地址与私网地址的映射关系。
NAPT只有一个公网地址,绑定的关系是(私网地址,源端口)和(公网地址,分配的端口)。与Basic NAT不同的是,NAPT对一个NAT会话的标识是(私网地址,源端口),因此每个主机如果有若干TCP/UDP会话,就会有过对应的多个NAPT会话。
NAT会话建立后,路由器维护的一个地址转换表中将为每一个会话建立一条表项,可以用一个状态属性来标识表项,路由器据此可以采用不同的绑定策略。NAT路由器收到报文后,首先做地址转换表的查找,如果找到会话已存在则把报文的地址(IP或者IP和端口)做相应的转换,其中可能会涉及到报文中其它域的改变,比如重新计算checksum值。
当NAT的会话不再使用时,路由器触发解绑定的过程,地址转换表中的表项被删除。地址解绑定的原因有几种:管理员强制解除地址绑定,或者NAT会话状态为空闲的时间过长。
NAT的报文转发过程在两个方向上都涉及到报文内容的改变,以下内容将分别予以阐述:
Basic NAT的报文头转换包括IP地址转换和TCP/UDP头的checksum修改。路由器需要把出私网的报文的源IP需要转换成NAT的外部IP,进私网报文的目的IP需转换成内部IP,然后重新计算新的IP Checksum值。
如果是TCP/UDP报文,路由器需要在转换了IP地址后再计算TCP/UDP的checksum值,因为TCP/UDP Checksum把IP报头也计算在内。唯一例外的是当UDP的checksum值为0时,不需要再计算。ICMP报头的checksum不需要修改,因为ICMP报头没有把IP地址包含在内。
NAPT的报文头改变包括IP地址转换,TCP/UDP端口号或ICMP Indentifier,和各个报头的checksum重计算。路由器把出私网的报文的(源IP地址,源TCP/UDP端口)改为(公网IP地址,NAT分配的端口),进私网的报文则把(目的IP地址,目的TCP/UDP端口)改为(私网IP地址,私网TCP/UDP端口)。IP头Checksum和TCP/UDP checksum或ICMP Checksum都要重新计算。
NAT的每一个报文都要重新计算上述几个校验和,因此运算量比较大。同时,由于计算的值相互间有关联,对算法的顺序有一定的要求。IP头校验和和TCP/UDP(ICMP)头校验和都是“取补”校验,所以只需要计算转换前的地址和转换后地址的差,然后加入原来的校验和即可。具体的算法见附录A.
ICMP的错误报文中包含了原报文的IP头信息,在经过NAT路由器时必须对这些内容也做相应的地址转换,不仅要改IP地址,还要把其中的checksum值重新计算。如果NAPT方式,而且恰好ICMP错误报文中包含有TCP,UDP头的信息,则这些ICMP的payload也要做修改,并重新计算所有的checksum值。
FTP协议在穿过NAT路由器时必须要有一些额外的处理,因为FTP协议的Payload中会附带一些IP地址以及端口号的信息,此外TCP Sequence和Acknowledge number也要在FTP的PORT命令和PASV命令的时候做修改。为了处理上述的问题,NAT需要针对FTP协议加一个专门的处理模块(ALG)负责处理FTP payload中的地址转换问题。
对Basic NAT而言,需要把FTP payload中的有关私网地址的部分转换成公网地址,这个地址是用八位的字符表示的,在转换的过程中如果两个地址的长度(字符串)不同,会导致payload的长度也发生变化,此时就要修改TCP Sequence和Acknowledge number。这个变化的量需要记录下来,在此后所有的报文中都要增加/减少这个量。
NAPT的情况稍复杂一些,除了地址之外还要考虑TCP端口的转换,以及由此可能引起的TCP Sequence和Acknowledge number的变化。
私网内的主机要解析私网内的其它主机的域名必须在私网内有一个DNS Server,这个Server可以保存私网内的(域名, IP)映射关系以及公网的(域名,IP)映射,但公网的主机不能访问这个DNS Server。如果DNS Server位于公网,则NAT路由器必须把私网内对公网主机域名的解析报文定向到DNS server。
网络中有报文的IP Option中含有IP地址信息,如:严格源路由和松散源路由。NAT可以完全不处理这些信息,不会影响报文的转发。
NAT中没有对私网地址空间的限制,私网地址空间可以是保留地址也可以是非保留地址。推荐使用保留地址空间。因为如果使用非保留地址则有可能恰好与公网中的某个主机重合,如果访问公网的报文中包含有该地址,在转换的时候就会发生混乱。
IANA在RFC 1918中规定了保留地址的空间:10.0.0.0/8,172.16.0.0/12,192.168.0.0/16。共有一个A类地址段,16个B类地址段,256个C类地址段。
NAT路由器不应该向公网发送私网的路由信息,也不应该把私网中的路由信息发送到公网中,但应该允许公网的路由信息进入私网。
通常NAT路由器都配置一条静态路由把所有的到公网的报文转发到ISP的路由器上,ISP的路由器也有一条静态路由指向NAT路由器。
参考文献:
RFC3022 Traditional IP Network Address Translator (Traditional NAT)
附录A.
NAT校验和调整算法示例:
注意:算法中的偏移量必须是相对于报文头的偶数个字节的偏移量。
void checksumadjust(unsigned char *chksum, unsigned char *optr,
int olen, unsigned char *nptr, int nlen)
/* assuming: unsigned char is 8 bits, long is 32 bits.
- chksum points to the chksum in the packet
- optr points to the old data in the packet
- nptr points to the new data in the packet
*/
{
long x, old, new;
x=chksum[0]*256+chksum[1];
x=~x & 0xFFFF;
while (olen)
{
old=optr[0]*256+optr[1]; optr+=2;
x-=old & 0xffff;
if (x<=0) { x--; x&=0xffff; }
olen-=2;
}
while (nlen)
{
new=nptr[0]*256+nptr[1]; nptr+=2;
x+=new & 0xffff;
if (x & 0x10000) { x++; x&=0xffff; }
nlen-=2;
}
x=~x & 0xFFFF;
chksum[0]=x/256; chksum[1]=x & 0xff;
}