在使用网络的过程中,或许你会遇到这样的问题:
或许你还会对这些问题感到好奇:
那么,NAT到底是什么?NAT会为我们的「网上冲浪」带来哪些不便之处?如何解决NAT为我们带来的不便?……本文将尝试详细地解答这些问题。
在Internet上,每台设备都有一个IP地址。IP地址和我们日常生活中的家庭住址类似,一台设备想和另一台设备通信,必须知道另一台设备的IP地址,才能将数据发送到对方。
对于个人、家庭的网络设备,IP地址一般由运营商分配。在过去,一户家庭一般只有一台电脑,这台电脑通过调制解调器(moden)直接接入Internet,获取运营商分配的IP地址。
而现在,很多家庭会同时拥有手机、电脑、智能家居等多种联网设备,一个IP地址完全不够这么多设备使用。
对于常用的IPv4地址,其格式是这样的:
综上,如下两个原因,让我们面临了IP地址不够用的问题:
所以,我们需要一种技术,让多个设备「共用」同一个IP地址,来缓解IPv4地址不够用的问题。
NAT的全称是「网络地址转换」(NetworkAddressTranslation),指的是路由器等网络设备,在传输数据的过程中,改变数据中的IP地址的一种技术。
NAT可用于内网IP地址(以下简称为「内网IP」)和公网IP地址(以下简称为「公网IP」)之间的转换。例如家庭中的多个联网设备,都拥有各自的内网IP,无线路由器运行NAT功能;家中的设备向外发送数据时,数据中的内网IP,在无线路由器上会被转换为公网IP;外部数据发送到家庭设备时,数据中的公网IP,会被转换为内网IP。
通过这种方式,家庭设备能够「共享」同一个IP地址。即使运营商只为用户分配了一个IP地址,用户家中的多台设备,也能同时访问Internet。
做为对比,如果没有NAT,家中每一个设备,都需要获取一个独立的公网IP地址,对于本来就少的IPv4地址资源,就显得有点「奢侈」了。
NAT改变了报文中的IP地址。但是,为什么我们平时上网时,并没感觉到NAT的存在?
在上文中的例子里,多台内网设备共用同公网IP。外部数据到达路由器后,路由器应该将数据发送给哪个内网设备?
在介绍NAT的工作原理之前,让我们先了解一下另外一个概念:端口号。
简单说,在网络中传输的数据,会被拆分一个个较小的片段。每个片段被称为一个报文。在报文中,除了IP地址,一般还包含了端口号。
IP地址每台电脑唯一,用来找到网络中的电脑。端口号每个应用程序唯一,报文到达电脑后,可根据端口号匹配到应用程序。
我们使用的大部分软件,例如网页浏览器,都是客户端软件。客户端需要主动向服务器发起连接。
从下图中也可以看出,回应报文的目的IP,就是请求报文的源IP,也就是用户电脑的IP地址;回应报文的目的端口号,就是请求报文中的源端口号,也就是用户浏览器的端口号。这样,少数派服务器的回应报文,就能根据IP地址发送回用户电脑,并根据端口号最终到达浏览器:
那么,如果用户的电脑是内网设备,经过了NAT,使用浏览器访问少数派网站时,又会是什么样的过程呢?
首先,浏览器同样会发送请求报文,报文的源IP为电脑的内网IP(此处以192.168.1.126为例)。
当报文到达路由器后,路由器将报文源IP修改为公网IP(1.1.1.10),并分配一个新的源端口号。在这个过程中,路由器会记录下源IP和源端口号,在转换前后的对应关系,形成NAT表项。
路由器将源IP和源端口号转换后的报文发送到服务器,服务器回应的报文,目的IP和目的端口端口号,就是请求报文中的源IP和源端口号。这样,报文就能根据目的IP,到达用户的路由器上。
路由器收到来自服务器的报文,根据NAT表项,将目的IP和目的端口号,从外部IP、外部端口,转换为内部IP、内部端口。这样,报文就能顺利到达用户电脑的浏览器上。
由于我们日常上网,使用的基本上都是TCP和UDP协议。而且自己的设备一般是做为客户端,主动连接第三方服务器的。所以,在日常上网的情况下,我们一般不会感受到NAT的存在。
NAT缓解了IP地址资源不足的问题,同时能使家庭中的多个设备共享同一条宽带,同时上网。另外,启用NAT后,外部设备无法主动发起对内网设备的连接,相当于起到了防火墙的作用,保护了内网设备,一定程度上提高了安全性。
NAT通过「巧妙」的方式,在内部地址和外部地址之间进行转换。大部分情况下,我们感受不到NAT的存在。但仍有部分应用,需要内网设备做为服务器,被外部连接,例如:
对于这些应用,如果设备位于NAT之内,没有公网IP,就难以实现了。
那么,在NAT环境下,应该如何让内网设备做为服务器,使内网设备被外部连接?下文将介绍几种常见的方式。
如果想让游戏主机等设备获取独立的公网IP,供外部连接,可以在光猫之后连接交换机。游戏主机连接交换机,直接进行PPPoE拨号。无线路由器也连接交换机,家中的其他网络设备经过无线路由器访问Internet。
但是多拨的局限也很大:
所以,这种方式不太常用。
上文中介绍的NAT,路由器会根据内网设备发出的报文,自动形成NAT表项。实际上,用户还可以在路由器上手动配置端口映射关系,让内网设备可被外部访问。
其中,DMZ功能,可以指定一台内网设备为DMZ主机。到达路由器上的报文,如果没有匹配NAT表项,就会转发到DMZ主机。从而使DMZ主机可被外部访问。
DMZ功能能让一台内网设备上的所有端口,都能被公网访问。但这样做也影响了内网设备的安全性,如果没有特殊需要,不建议打开这一功能。
而端口转发功能,可以手动设置端口映射关系,让指定内网设备的指定端口,能够被公网访问:
这种方式能够精确控制哪些设备的哪些端口可被公网访问。但需要用户具有一定的网络知识,知道需要被公网访问的应用的端口号,才能正确设置。
另外批评一下任天堂,在官网的帮助文档中,直接让用户打开了所有UDP端口的端口转发。这样做降低了安全性,而且可能与用户的其他端口转发规则冲突。不过从另一个方面,也说明了根据实际应用,手动设置端口转发规则,对部分用户来说,确实是一件门槛较高的事情:
上文中的端口转发功能,需要手动配置端口转发规则,操作起来比较麻烦。而UPnPIGD和NAT-PMP协议,则能实现自动配置端口转发规则。
UPnPIGD(互联网网关设备协议)和NAT-PMP(NAT端口映射协议)分别由微软和Apple提出,功能类似,都可以让应用程序告诉路由器需要打开的端口,让路由器自动设置端口转发规则。
UPnPIGD和NAT-PMP的工作,需要应用程序和路由器的配合。首先需要在路由器上打开UPnP或NAT-PMP功能:
安装宽带时,运营商附送的光猫,一般会默认打开路由功能。这时光猫同时能作为路由器使用。
但光猫的功能和性能有限,一部分型号的光猫不支持UPnPIGD等协议,或者不能手动配置端口转发规则。
所以,可以考虑将光猫修改为桥接模式,通过自己的无线路由器拨号,充分利用路由器上端口转发、UPnPIGD、DMZ等功能。
由于IPv4地址资源不足,不少运营商已经不再分配公网IP。
另外一部分地区的运营商,在进行PPPoE拨号时,用户名中加入pub,即可获取公网IP:
部分地区需要付费购买,例如北京移动宽带,之前提供有公网IP叠加包。
但是,由于IPv4地址资源本身已经不足,不一定能够成功申请到公网IP。另外,拥有公网IP,家中的路由器能直接被公网访问,如果没有配置好,可能会带来更多安全问题。所以,需要根据自己的实际需要,来决定是否申请公网IP。
对于运行NAT的家庭路由器,通过UPnPIGD或NAT-PMP协议,可以方便地将端口映射到公网。
但是,由于IPv4地址的不足,电信运营商也开始使用NAT,不再为用户分配公网IP。那么是否有一种类似UPnPIGD或者NAT-PMP的协议,运行在运营商的路由器上,能直接在运营商路由器上创建端口转发规则?
PCP就是这样一种协议:
该协议由NAT-PMP发展而来,运行在运营商的路由器上。用户的应用程序可通过PCP协议,申请在运营商路由器上打开端口。
PCP需要运营商的配合,选用支持的网络设备,并打开PCP功能,才能正常工作。根据V2EX网友的测试,国内已有运营商支持该协议,能通过PCP使eMule获得HighID:
上文中介绍了一系列使内网设备可被外部访问的方式。但这些方式或者需要用户手动配置,或者路由器的支持,或者需要运营商的支持……如果上述方式都不可用,就要通过第三方服务器中转的方式,让内网设备供外部访问。
这种方式虽然需要第三方服务器的参与,浪费资源,但成功率最高,所以应用范围也很普遍。例如常见的游戏加速器,就可以通过第三方服务器中转的方式,为游戏主机提供更高的NAT类型:
也有不少开源的反向代理工具,可以搭建在自己的服务器上,使内网服务可在公网访问:
服务器中转需要额外的服务器,且需要消耗服务器上的流量。所以这种方式往往需要用户额外付费,例如购买游戏加速器会员,或者自行购买虚拟服务器,并在服务器上搭建反向代理应用。
如果两台设备都位于NAT路由器之后,没有公网IP。在没有第三方服务器的中转下,是不是就没有办法直接进行通信了?
这项技术听起来很神奇,但是原理并不复杂:
我们以PC1、PC2两台主机的通信为例。两台主机均位于NAT路由器之后,各自的IP地址都是内网地址,无法互相通信:
在两台主机能够直接通信之前,需要一台第三方服务器:
PC1、PC2首先需要给服务器发送一个报文。经过NAT路由器后,报文的源IP和源端口号被转换,同时在路由器上形成NAT表项:
报文到达服务器后,服务器记录下PC1、PC2两侧报文的源IP和源端口号,也就是PC1、PC2两侧的公网IP和外部端口号。然后,服务器将两台设备的公网IP、外部端口号发送给对方。这样,PC1、PC2都能相互知道对方的公网IP和外部端口号:
其中一部分路由器的NAT检查比较宽松。一旦NAT表项建立,只要路由器上收到的报文,目的IP和目的端口号能够匹配到NAT表项,都会转发到表项对应的内网设备。对于这样的路由器,PC1、PC2互相用对方的公网IP与外部端口号,就能直接通信了,不再需要第三方服务器:
另一部分路由器的NAT检查比较严格,只有内网设备向指定的目的IP、目的端口号发送过数据,来自这个IP和端口号的报文,才能转发到内网设备:
对于这样的路由器,PC1、PC2两台主机需要同时向对方的公网IP和外部端口号发送一个报文。这样,PC1侧的路由器认为PC1向PC2发送过数据;PC2侧的路由器认为PC2向PC1发送过数据,PC1和PC2就能相互通信了。
经历了上述步骤,NAT打洞成功,两台设备就可以不依赖第三方路由器,直接进行通信。当然,上述过程只是一个简化的描述,不完全描述。如果想要进一步详细了解NAT打洞的过程,建议参考文末的RFC文档链接。
可以看出,NAT打洞可以在无需路由器特殊配置、无需运营商配合的情况下,实现两个内网设备的相互通信。另外,对于多层NAT的网络环境(例如运营商和家庭路由器各进行一级NAT),NAT打洞也能正常处理。
在一些路由器的设置页面或文档中,我们会看到,NAT能设置成不同的类型,例如Fullcone、Restrictedcone、Port-Restrictedcone、Symmetric:
在Xbox、PlayStation、NintendoSwitch等游戏主机上,我们也能看到不同的NAT类型,例如open、moderate、strict等:
那么,这些NAT类型到底意味着什么?如何提升NAT类型,来获取更好的游戏体验?让我们先来了解一下基础的NAT类型:
完全圆锥形NAT(FullconeNAT):
对于完全圆锥形NAT,内网IP和内网端口号,被映射为外部IP和外部端口号。当路由器收到来自外部的报文时,只要报文的目的IP和目的端口号,匹配到NAT表项的外部IP和外部端口号,都会转换为对应的内网IP和内网端口号,转发到内网设备。
对于外部报文,路由器并不关心报文的源IP和源端口号(即报文来自谁),只要收到匹配NAT表项的报文,都能发送到内网设备。所以,完全圆锥形NAT是最宽松的NAT,打洞最方便。
受限圆锥形NAT(RestrictedconeNAT):
与完全圆锥形NAT相比,受限圆锥形NAT,在内网设备向外发送报文时,路由器除了生成NAT表项,还会根据报文的目的IP,记录下内网设备正在与哪些外部设备通信。
这样,只有内网设备先发送报文给外部设备,外部设备回应的报文,才会被转发到内网设备。而其他外部设备发送过来的报文,即使匹配NAT表项,也无法发送到内网设备。
这样的NAT安全性有一定的提高,但是也提高了打洞难度。两台内网设备需要互相给对方发送一个报文,才能打洞成功。
端口受限圆锥形NAT(Port-RestrictedconeNAT):
端口受限圆锥形NAT和受限圆锥形NAT类似,但增加了检查的严格程度:受限圆锥形NAT,只会外部设备的IP地址,来检查内网设备与哪些外部设备通信过。而端口受限圆锥形NAT,会同时根据IP地址和端口号来进行检查。
对称NAT(SymmetricNAT):
前面的三种圆锥形NAT,会根据内网设备发出去的报文的源IP、源端口号两个信息建立NAT表项,将内网IP和内网端口号映射到外部IP和外部端口号。内网设备发出去的报文,无论目的IP和目的端口号如何变化,不管发给哪台外部设备,都会被映射为相同的外部IP和外部端口号。
而对称NAT,会同时根据内网设备出方向报文的源IP、源端口号、目的IP、目的端口号四个信息来建立NAT表项。如果报文的目的IP、目的端口号发生了变化,映射到的外部端口号也会发生改变。
对于对称NAT,我们再来回顾一下前文中NAT打洞的过程。内网设备首先和第三方服务器通信,内网IP和内网端口号会被映射为一个外部IP和外部端口号。接下来,内网设备和另一台设备通信,相同的内网IP和内网端口号,又会被映射为另外一个外部端口号。这样,NAT打洞就无法成功。
所以,在对称NAT下,很难进行NAT打洞。
而Xbox、PlayStation、NintendoSwitch上的NAT类型,基本上也跟上述四种NAT类型对应。例如Xbox上的三种NAT类型:
PlayStation的NAT类型也与之类似,可参考如下链接:
所以,对于能设置NAT类型的路由器(例如华硕的部分型号),将NAT类型设置为FullconeNAT,能够更容易地实现NAT打洞,使游戏主机更容易被外部连接。
按照上文中的描述,「对称NAT」是无法打洞成功的。但是,事实真的是这样吗?
实际上,运用一点点概率知识,对称NAT也是可以打动成功的。让我们先来了解一下「生日问题」:
假设内网设备A位于「圆锥形NAT」之后,内网设备B位于「对称NAT」之后。这时候A向B发送报文,B并不知道A的报文经过NAT之后,公网端口号会被转换为什么,所以无法向A发送数据。
当然,在B不知道A的外部端口的情况下,也可以用不同的端口号,依次尝试向A发送报文,直到发送成功为止。但端口号一共有65535个,扫描所有端口并不现实…..
那么,如果我们运用上「生日问题」,A随机向B以不同的源端口号,发送256个报文,形成256个NAT表项;B同时也随机以不同的目的端口号,向A发送256个报文进行探测,打洞成功率就能达到64%。如果随机探测1024个端口,打洞成功率则能达到98%。
果壳DATAMUSE团队制作了一个有趣的交互页面,来解释生日问题。如果感兴趣,不妨打开如下链接尝试一下:
前文中已经提到,语音通话、视频会议应用,以及在线游戏,都用到了NAT打洞。那么,利用NAT打洞,还能实现哪些有趣的应用?
其实,最常见的应用,就是通过NAT打洞,将多个设备组建一个虚拟局域网。例如在家中有NAS,且没有公网IP的情况下,通过这些利用NAT打洞的工具,仍然可以在离开家的时候,用手机直接访问NAS上的文件。而且由于是直接通信,不会因为第三方服务器中转而降低传输速度。
前面提到的NAT打洞,一般都是基于UDP协议的。但是,在Internet上,TCP协议也十分常见。例如我们浏览网页、下载文件常用的HTTP协议,就是基于TCP的。
那么,NAT打洞时,为什么不常用TCP协议?
这主要是因为,TCP相对UDP复杂得多。UDP协议是基于报文,一个一个报文收发的。而TCP需要先建立连接,然后才能传输数据。
所以,应用程序可以方便地发送UDP报文,进行打洞。而对于TCP,操作系统底层处理了连接建立、断开等过程。应用程序无法方便地控制单个报文的发送。
另外,部分路由器的NAT实现,也会对TCP报文的状态进行额外检查。如果发送报文不属于某个连接,就会丢弃报文。
所以,TCP打洞理论上能实现,但实现复杂,且成功率不高。如果需要进行TCP打洞,建议改用QUIC协议。QUIC协议虽然基于UDP,但引入了与TCP类似的可靠传输、拥塞控制等机制。最新的HTTP3协议,就是基于QUIC的。
在网上,我们会常常看到关于「某个工具的NAT打洞成功率更高,而另一些工具经常打洞失败」的讨论。那么,如何理解这里说的「成功率」
NAT打洞为内网设备之间的直接通信,提供了可能性。但是,不同厂商、不同设备的NAT实现,不是完全相同的。另外,两台内网设备之间,可能会经过多个路由器和防火墙,更增加了复杂性。
所以,NAT打洞无法保证完全成功。例如前文中介绍的对称NAT,就无法保证100%成功。
所以,一个比较好的NAT穿透实现,会进行多种尝试:例如先尝试UPnPIGD和NAT-PMP,然后尝试不同的NAT打洞方案。在最终无法打洞成功时,选择服务器中转等备用方案。
NAT打洞虽然能让内网设备直接通信,那么,是不是大部分设备,都不需要公网IP,直接位于NAT之后就行了。
其实不是这样,比起使用公网IP直接通信,NAT打洞仍存在不少缺点,例如:
根据前面的介绍,我们看到,NAT在缓解IPv4地址资源不足的问题上,做出了巨大的贡献;同时,NAT的出现,也避免了个人设备暴露在公网,「意外地」提升了安全性。
但是,为了保证安全性,家用路由器、PC、NAS等设备上,一般都有防火墙功能。防火墙默认会阻止传入连接,除非用户手动配置防火墙,打开特定端口。所以,即使NAT被淘汰,类似NAT打洞的技术,在IPv6时代仍会得到应用。
那么,当IPv6普及,NAT消失,Internet是否会诞生新的有趣的应用?家中的每个物联网设备都有了公网IPv6地址,是否会有一些新的玩法?是否会带来新的安全性问题?随着国内大幅度推进IPv6的建设,这些问题应该很快会有答案。
关于NAT的具体实现,可参考如下RFC:
在NAT环境中进行P2P通信,会遇到哪些问题?应该如何解决?可参考如下RFC:
已经有一系列协议,实现了完整的NAT打洞过程,可参考如下RFC: