抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

前言

通过前面IPv6知识的学习,我们已经知道IPv4只能为40多亿台网络设备分配IP地址了,随着接入互联网的设备越来越多,这很明显已经不够使用了。

而NAT就是一种解决IPv4地址不足的方案,它用来让多个【网络设备】共用同一个IPv4地址,那么它是怎么做到的,优缺点又是什么?玩各种联机游戏时经常会听到的NAT穿透/打洞技术又是啥?带着这些问题开始学习~

nat.drawio

什么是NAT?

NAT全称(Network Address Translation)网络地址转换,这是一种在路由器、防火墙上等网络设备上传输改变数据IP地址的网络技术。用于在内网IP和公网IP之间的转换,将从家庭/公司的内网IP发出的数据转换为公网IP,将从外部设备响应数据的公网IP转换为内网IP。

from GPT

假设我们有一个家庭网络,网络中有两台计算机A和B,它们的私网IP地址分别是192.168.1.2和192.168.1.3。这个家庭网络连接到了一个路由器,路由器的公网IP地址是203.0.113.1。

当计算机A想要访问互联网上的一个服务器(IP地址为203.0.113.2)时,计算机A会发送一个数据包,源IP地址是192.168.1.2,目标IP地址是203.0.113.2。

当这个数据包经过路由器时,路由器会将源IP地址更改为它自己的公网IP地址203.0.113.1,并且为这次通信分配一个端口,假设是50000。所以,这个数据包的源IP地址和端口被更改为203.0.113.1:50000,目标IP地址仍然是203.0.113.2。

当服务器回应时,它会发送一个数据包,源IP地址是203.0.113.2,目标IP地址是203.0.113.1:50000。

当这个数据包经过路由器时,路由器会查找它的NAT表,找到相应的映射,将目标IP地址和端口更改为192.168.1.2,然后将数据包发送给计算机A。

现在由于公网IP的稀缺,国内各大运营商都不会给普通家庭用户分配公网IP,运营商通过在路由器上使用NAT技术,来实现少量公网IP来满足大多数用户访问互联网的需求。

运营商路由器使用NAT技术被称为CGN(Carrier-grade NAT)电信级NAT,本质上和NAT技术没有什么区别,基本原理和工作方式是相同的,只是应用访范围和转换规模不一样而已。

因此我们通常是处于多层NAT网络下来访问互联网的(自身路由器NAT→运营商NAT→互联网)

解决了什么问题?如何实现?

正如前面所了解的,NAT缓解了IPv4地址不足的问题,通过内网IP和公网IP的转换实现多台设备共用一个出口公网IP。

那么它是如何实现的呢?NAT技术改变了IP报文中的IP地址,但我们平常使用时却毫无感觉?

当多台设备共用一个公网IP时,我们可以简单理解为多对一,但外部设备接受到请求后,要将响应返回给原先发出请求的内网设备时,NAT路由器是怎么知道要具体发给那个设备呢?

答案便是:路由表项映射,首先我们都知道TCP/UDP协议都有端口号,我们在网络通信中用五元组来确定一个TCP/UDP会话。

  1. 源IP地址:发起通信请求的设备的IP地址。
  2. 目的IP地址:接收通信请求的设备的IP地址。
  3. 源端口号:发起通信请求的设备使用的端口号。
  4. 目的端口号:接收通信请求的设备使用的端口号。
  5. 传输协议:通信使用的协议,通常是TCP或UDP。

NAT就是通过端口号来确认将外部设备响应数据转发给那个内网设备,例如现在有如下设备

内网A设备IP:192.168.1.2,端口号:5000 NAT路由器IP:203.0.113.1,端口号:30000 外网B设备IP:8.8.8.8,端口号:443

当一个内部设备向外部网络发送数据包时,路由器会将源IP地址(内网IP)和源端口号改为公网IP地址和一个新的端口号并将这个映射关系存储在NAT表中。

将源IP:192.168.1.2转换为NAT路由器公网IP:203.0.113.1 将源端口号:5000转换为NAT路由器端口号:30000

并记下这种映射关系变成NAT表项:

内部IP 内部端口号 NAT路由器IP NAT端口号
192.168.1.2 5000 203.0.113.1 30000

最后当外部网络的响应返回时,路由器会根据NAT表项将目的IP地址和目的端口号改回为原来的私网IP地址和端口号,然后将数据包转发给内部设备。

这表示NAT技术的基本原理了,通过维护路由器表项就可以实现共用公网IP了,看起来很不错。

那么,古尔丹,代价是什么呢?

如果我们想让外部设备直接对内部设备访问可以吗?很遗憾由于缺少路由表项,即使请求走到NAT路由器上,路由器也不知道要转发给那台内网设备上去。

同时NAT主要工作在传输层,它依赖于TCP/UDP的端口号来实现区分不同链接,因此当某个协议没有端口号时就会比较麻烦了。

例如之前了解的ICMP协议,当然现代的路由器为了处理这些问题也是做了特殊的适配,对于ICMP协议,NAT设备可能会使用ICMP报文的标识符字段来代替端口号,以此来区分和管理不同的ICMP会话。

因此最主要的问题便是NAT破坏了互联网最先的理想:端到端/点到点通信,NAT使得外部设备要直接访问处于NAT网络下的内部设备变得很困难。

NAT最大的问题:破坏端到端通信

但我们可以发现在现实使用中好像有使用到端到端通信,例如:在线联机游戏(steam)、语音通话(zoom)、文件共享传输(BitTorrent)。很明显这些设备都是处于NAT网络下的,不可能这些流量都通过中转服务器来进行通信,这样太耗资源了,那么他们是如何实现的呢?

端到端通信

我们先看下有那些方法可以实现端到端通信呢?

  1. 使用钞能力,找所在运营商申请一个公网IP,以深圳地区为例,申请一个固定的公网IP并开放80/443端口,费用在几千一年。
  2. 手动或者使用各种工具往自身路由器上写下路由映射表项,使得当外部设备请求走到该路由器时,能够知道转发到那个设备上。但据我了解到的信息,普通用户都是处于多重NAT网络下,即上层还有ISP运营商的NAT路由器等等,所以修改了自身路由器的表项也多半没有作用。
  3. 服务器中转或者叫TURN或者叫VPN,中转服务器会接收来自内网设备的数据包,并将它们转发给外部设备,同时也会将来自外部设备的数据包转发给内网设备。例如各大游戏加速器就有使用到这项技术,但很明显流量都需要通过第三方服务器,这会带来很多带宽,因此也是需要收费滴。
  4. 伟大的NAT打洞技术,不需要钞能力,不需要公网IP,不需要对路由器进行特别操作,不需要第三方服务器的中转,很多应用都会使用NAT打洞技术来实现内网设备直接的通信。

那么问题就变成了NAT打洞技术是怎么实现的?

NAT打洞技术主要在于要让连接双方的路由器上留下映射项,假设现在有A设备和B设备分别处于各自的NAT网络下,现在想要实现端到端通信大致步骤如下

  1. 获取自身公网端点信息,通过和公共的STUN(Session Traversal Utilities for NAT)服务器进行通信,该服务器的响应会返回设备自身的公网IP和公网端口号信息。
  2. 交换公网端点信息,A设备和B设备通过公共信令服务器,交换批次的公网IP和公网端口号信息。
    1. 公共信令服务器通常是免费的,注册个账户即可,不过目前还没试过
  3. 然后双方互相向对方的公网IP和端口号发送请求,在发送请求的过程中就会在各自NAT路由器中留下映射项。
  4. 一旦这些映射项被创建,设备A和设备B就能够直接相互发送请求了,因为它们的NAT路由器已经知道如何将外部的请求转发到正确的内部设备。

初次看到打洞的步骤可能会有个疑问:为什么需要同时互相对对方的IP:PORT发起请求?

回顾前面的知识、当外部设备对处于NAT网络的内部设备发起请求时,NAT路由器收到请求后并不知道需要转发给哪个具体的设备。当我们同时对要通信的设备发起请求时,会在各自的NAT路由器中留下表项。

例如现在位于NAT网络下的A设备和B设备需要实现通信。

内网A设备IP:192.168.1.2,端口号:5000 NAT路由器IP:203.0.113.1,端口号:30000

外网B设备IP:192.168.1.3,端口号:6000 NAT路由器IP:203.0.113.2,端口号:40000

A设备对B设备所属的公网IP和端口号(203.0.113.2:40000)发起请求,此时会在自己的路由器留下如下表项

内部IP 内部端口号 NAT路由器IP NAT端口号
192.168.1.2 5000 203.0.113.1 30000
来源IP 来源端口号 目标IP 目标端口号
203.0.113.1 30000 203.0.113.2 40000

B设备也同时对A设备所属的公网IP和端口号(203.0.113.1:30000)发起请求,此时会在自己的路由器留下如下表项

内部IP 内部端口号 NAT路由器IP NAT端口号
192.168.1.3 6000 203.0.113.2 40000
来源IP 来源端口号 目标IP 目标端口号
203.0.113.2 40000 203.0.113.1 30000

当设备路由器检测请求的目的IP和端口号能够和记录的表项匹配到一起时,就会转发到对应的内网设备

所以几次尝试后当A设备和B设备的路由器都通过表项记录正确将请求转发到A设备和B设备上时,打洞就完成了,能够实现端到端通信。

因此只需依赖各大公共的STUN服务器就可以实现端到端通信,对于多层的NAT网络环境下也能进行打洞操作,实现端到端同学,听起来还是很不错的,在大多数情况下打洞技术都能够帮助到我们。

NAT路由类型

为什么是大多数呢?例外出现在哪里?

答案还是在NAT路由器上,不同的路由器对于NAT表项的转发有不同的处理,通过转发规则被分为4种:完全圆锥形 NAT(Full cone NAT)、受限圆锥形 NAT(Restricted cone NAT)、端口受限圆锥形 NAT(Port-Restricted cone NAT)、对称 NAT(Symmetric NAT)

完全圆锥形 NAT(Full cone NAT):

当NAT路由器收到来自外部设备的请求时,只要请求的目的 IP 和目的端口号,能够匹配到记录的表项,那么就都会转换为对应的内网 IP 和内网端口号,转发到内网设备。

该规则下的路由器并不关心请求来自那台外部设备,只要收到匹配 NAT 表项的请求,就都转发到对应内网设备,即使该内网设备没有和该外部设备通信过,所以是最宽松的 NAT,打洞很简单。

受限圆锥形 NAT(Restricted cone NAT):

相对于Full cone NAT,路由器做了一些限制,它会检测内部设备是否有和该请求对应的IP通信过,只有内部设备先对该外部设备发送过请求,路由器才会根据表项进行转发,这样的规则安全性提高了一些,但并不影响我们打洞。

端口受限圆锥形 NAT(Port-Restricted cone NAT):

相对于Restricted cone NAT,路由器在做了一些限制,不仅检测IP了同时还检测端口号是否有过通信,只有内部设备和该外部设备的IP:PORT通信过,路由器才会根据表项进行转发操作,这样的规则安全性又提高了一些,但还是并不影响我们打洞,绝大部分家庭用户都处于这个类型的NAT网络下。

我们可以看先下这三种类型的NAT,无论发出去的目的IP、目的端口号是什么,NAT路由器都会分配相同的公网IP和端口号,这也是NAT打洞技术所需的基本条件。

对称 NAT(Symmetric NAT):

而对称NAT特殊在当目的IP或者目的端口号发生改变时,分配的端口号会发生改变,这就意味着通过STUN服务器获得到的IP:PORT和发送请求的IP:PORT并不一致,这就会导致打洞失败。因此在这种模式下的NAT很难打洞成功。

为什么是很难?还有希望能够打洞成功吗?

通常情况下路由器分配的IP都是相同的,因此只有端口号相同,理论上有65535种可能性,因此暴力遍历就可以了?

我们可能要花上较长时间才可以找到正确端口号,来建立端到端通信,但这看上去就上一个非法用户在进行端口扫描,可能会触发防火墙等安全检测软件。

那有啥方法可以优化这个问题吗?

有一个基于生日驳论原理的方法,即同时开启多个端口来提高匹配机会,这部分后续可以在了解一下

介绍生日驳论的可视化网站:datamuse.guokr.com/web?page=bpp

那么回到上面提出的疑问,在线联机游戏(steam)、语音通话(zoom)、文件共享传输(BitTorrent)是怎么实现端到端通信的呢?

在大多数情况下要实现端到端通信,都会先尝试先使用STUN来进行NAT打洞,如果打洞成功就可以直接连接了。如果不幸处于对称型NAT网络下,就只能使用备用方案TURN协议来进行流量中转来实现通信了,当然还有很多其他细节和方案,但大体是这样原理。

小结

那么大致了解NAT的理论知识后,就要实践一下了,结合上面的知识我们需要解决几个问题。

  1. 自己的设备处于什么NAT网络下?

    可以使用NATTester工具来检测NAT类型:https://github.com/HMBSbige/NatTypeTester

    如下图,我就处于端口受限圆锥形 NAT(Port-Restricted cone NAT)类型下

    my-nat

  2. 通信双方如何交换公网IP和端口号?

  3. 有什么P2P通信的相关协议吗?

  4. 使用什么工具来进行打洞实现端到端通信?

实践部分就留给下次学习吧~

参考资料