NAT traversal
更新日期:
NAT
通常家用的路由器实际上就是NAT路由器,并非计算机网络IP层的路由器。
NAT全称为 Network Address Translation (网络地址转换),其通过维护NAT转换表(NAT translation table),实现局域网内的主机共用一个公网IP地址,且局域网内(家里)的主机感觉不到NAT的存在。
NAT的存在严重违反了进程设立端口号的初衷,切断了主机彼此直接对话的通道(端到端原则)。使得对于编程人员来说及其不友好!尤其是使用(NAT)家庭网络来运行服务器程序的时候,非局域网的主机难以访问服务器。
还有一点,NAT映射有两种,分别是Cone NAT 和 Symmetric NAT,前者映射期间端口号不变,后者端口号动态改变,下面提的都是围绕着端口号不变展开的。
NAT工作实例
假设家里主机A在受NAT控制的网络中,家里的公网IP地址为123.123.123.123,A的地址为172.0.0.1,此时A(假如用端口213,通常浏览器随机选用一个端口)访问acg.tv(假设其公网地址为233.233.233.233,端口为80),
则其报文会先通过NAT路由器,此时NAT路由器会拆开你的报文,找到TCP首部的源和目的端口号,然后在NAT转换表添加一条记录,大概为
172.0.0.1:213 –> 233.233.233.233:80,同时建立一个持续一定时间(如几分钟)的会话状态(超过这个时间会删除这条记录)。
添加完记录后,NAT将随机生成一个端口号(比如666),修改原来报文TCP首部的源端口号及IP首部的源IP地址,然后发到公网。
上述过程称为NAT穿透(NAT Traversal)。
此时acg.tv收到的报文,源IP:端口为123.123.123.123:666而不是172.0.0.1:213,可见acg.tv并不知道后者的存在,而A也不知道报文被NAT路由器偷偷修改了。
假如有一台外地主机B(213.213.213.213:2333)访问172.0.0.1:213,显然主机B的路由器不会发送给A,因为这个地址是IP协议规定的内网地址,路由器不会将其发到公网。
当主机B访问123.123.123.123:666(即此时A的公网地址),路由器会将报文交给A所在网络的NAT路由器,然而这个NAT路由器不会将报文发给A,
因为前面提到,与172.0.0.1:21关联的目的地址为233.233.233.233:80(acg.tv),而不是B。
上面例子也说明了前面提到的,A无法直接运行服务器程序的原因。因为服务器是被动接受来自客户端的请求,而NAT路由器的存在使得A只能主动与外界联系,这与服务器的定位不符合。
继续这个例子,假如A得知B的地址,并访问了一次B(不论B是否响应),此时NAT转换表就增加了A到B的映射(地址对应关系),因而在之后B能访问A。
假如是acg.tv把B的地址告诉A的,则这种情况称为连接反转(connection reversal)。
解决方案
要想让外来主机与A联系,其中一个方法是利用NAT穿透机制,让一台具有公网IP的主机(假如acg.tv)作为中介一直持续地为主机A提供服务,主要是两个服务:
- 告知外来主机A此时的公网IP:端口(如前面提到的123.123.123.123:666)
- 告知A外来主机的公网IP:端口(理由见上面B访问A的案例)
此时A要主动访问一次外来主机(无论对方是否响应),才可建立双方连接。
这个方案适用于P2P(点对点)应用程序,因为P2P应用有公网的追踪服务器(tracker)来提供中介服务。
然而对于服务器应用程序没有帮助,还是因为服务器是被动接收请求的。而且如有专用的公网IP的主机,那用该公网主机作为服务器就没这些麻烦了。
另一个方法是采用UPnP协议,使用该协议,能够直接配置NAT(如该NAT路由器接收修改请求)的转换表,使得A可以有固定的公网IP:端口。