frankfan的胡思乱想

学海无涯,回头是岸

NAT(NAPT)机制及应用

UDP/TCP的差异 TCP的模拟 模拟TCP的现实意义

本章内容分为三部分:

  • NAT(NAPT)机制及应用
  • TCP协议的特性
  • 使用UDP模拟TCP实现TCP协议中的主要特性

NAT(NATP)

我们已经学习过UDPTCP协议(伯克利socket接口函数)的使用,无论是sendto还是send,我们的目标IP地址都是公网IP,而不是局域网IP(本机通信或者局域网通信除外)。

当我们想要将流量发送到公网(广域网)上时,目标IP地址必须是公网IP。

以上是网络编程的基础常识。

那么,为什么我主机上的钉钉能够能跟你主机上的钉钉通信呢?我们两台主机都在不同的局域网中,都位于网关(路由器)后面。

似乎道理也很简单,因为钉钉服务器的存在,我们不过是将消息发送给了钉钉的公网服务器,然后服务器进行了转发。那么,一台网关设备后面有若干台主机,每台主机上都有钉钉,那网关是怎么准确无误的指导需要将钉钉消息发送到哪台主机呢?

最关键的问题是,我们的局域网IP地址192.168.0.1怎么就能与一个局域网后面的192.168.0.2主机通信呢?服务器的存在?为啥公网服务器就能与局域网IP通信呢?

要回答这个问题,就要知道,NAT机制。

为了缓解IP地址的紧张,现在网关都具备NAT功能,能够只通过一个公网IP地址就能让N台主机与外界通信。

NAT的全称是Network Address Translation网络地址转换。

image.png

局域网地址之所以能访问远程公网服务器的原因在于在网关(路由器)设备中,有一个nat模块专门用来处理局域网地址与公网地址的映射关系:

image.png

真的的秘密在于每台主机的流量到达网关后,其内网IP地址以及端口都会被改写。

改写为这个网关的外网IP地址和一个新的端口号因此内网IP:端口1外网IP:端口2形成了一个1对1的新映射,这个映射的组成元素一共有4个,任何一个元素不同都是一条新的映射。

其实注意到,并不是简单的内网IP与外网IP组成一条映射,还包括端口这一元素,因此NAT又被称为NAPT(P = Port)

因此,真实的通信就变成了这样子:

image.png

NAT还未形成一个标准方案,不同的设备厂商都有自己的实现,不过大致说了现行的NAT有4种不同的类型:

  • 完全圆锥形NAT类型
  • 受限圆锥形NAT类型
  • 端口受限圆锥形NAT类型
  • 对称NAT类型

从名字上看不出任何端倪,甚至有些莫名其妙。

image.png

当主机通过网关(路由器)访问远程服务器server-1时,流量经过网关前往server-1服务器时,便会建立一个NAT映射,将内部地址LocalIP:LocalPort映射到了外部地址WideIP:WidePort,此时通过WideIP:WidePort这个点就能访问server-1服务器,而server-1也能讲流量发送到WideIP:WidePort这个点,然后通过查NAT映射关系,网关将流量转发到局域网主机上。不仅如此,即使之前没有与主机通信过的server-2服务器此时也能直接通过WideIP:WidePort将流量发送到局域网内部主机,即server-2也能直接与局域网主机通信,而这一切得益于NAT表映射关系的建立

映射建立完成后,对目标主机的IP和端口都无限制,的这种类型,就是所谓的完全圆锥形NAT类型


image-20201126152448961

主机LocalIP:LocalPort访问server-1时,NAT设备会创建一个NAT表映射,得到一个WideIP:WidePort,通过这个地址访问主机server-1,此后server-1就能通过NAT设备将流量传输给主机了,与上面(完全圆锥形NAT类型)类型不同的是,此时server-2不能通过这个WideIP:WidePort访问主机,而必须要是内部主机先与外部主机通信,然后外部主机才能将流量传输给内部主机。比如内部主机通过LocalIP:LocalPort先访问了server-3后,此时server-3才能访问内部主机(通过NAT设备)。

而对外部主机的地址要求是theWideHostIP:AnyPort。必须要是已经通过信的那个外部主机IP,而对外部主机的端口,不做要求。

映射建立完成后,对目标主机的IP有限定,对目标主机端口无限制,的这种类型,就是所谓的受限圆锥形NAT类型


当在『受限圆锥形NAT类型』的基础上加一个端口限制后,就成了第三种NAT类型,即所谓的『端口受限圆锥形NAT类型

这意味着,只有主机先与外部server通信后,外部server才能与主机通信,并且前提还有,并不是只要外部主机IP地址符合要求就好,还必须要求外部主机的端口也不能变,如图:

image.png

而最后一种类型就是『对称NAT』,这种类型的限制最多。

image.png

只有先当内部主机访问外部主机(服务器)后,外部主机(服务器)才能访问内部主机,并且,只能沿着原路返回数据,也就是说,通过网关设备出去的外部地址都是一个唯一的点,通过这个点,只能访问一对IP:端口号(注意,不是说只能访问一台主机)

到目前为止,我们已经讨论完了NAT的4种类型,路由器就是一个NAT设备。那么有个这个知识储备后,有什么用呢?

主机之间的通信

C-S模型的架构是非常经典的并且占据互联网大部分流量的一种模型,几乎我们常说的网络编程就是在使用C-S架构(及其变种)

image.png

这是经典的C-S通信架构,通过公网服务器,每一个客户端都能访问到这台服务器,而无论客户端是否位于NAT后面(是否处于局域网中),这种设计的好处是明显的:

  • 保证两台主机之间肯定可以进行通信
  • 让两台主机实现通信的逻辑变得简单
  • 很多时候业务逻辑并不要求两台主机进行通信,而是服务器只作为资源的提供者,这种模式下C-S架构天生适合

以上都是这种架构的优势。但这种架构并不适合所有场景(其实,没有任何一种架构能包治百病),比如即时聊天软件直接的通信就是主机与主机的通信了,那么在需要端与端的通信时,C-S模式的架构就不是最优的了(只是从技术上讲,不从政策上讲。因为政策上而言,服务器必须介入主机与主机的通信,用来保存通信内容供government查阅,这种情况下必须C-S架构,而某些主打隐私安全的设计就不是采用这种架构)

image.png

我们对C-S-C模式能够理解,但是我们需要讨论下p2p(peer to peer)可行的理由。(这部分只是在我们实际工作中可能不会使用上,但是对理解掌握网络的完整性是个必不可少的一部分,甚至日后理解所谓的反射、隧道等都大有裨益)

image.png

根据图示,两台NAT之间的流量是可以直达的,但这是否意味这局域网主机1和局域网主机2就能因此而流量直达了呢?

答案是肯定的,但是还有几个问题要解决:

  • 主机需要知道自己的公网IP地址(端口)
  • 主机需要让另一台主机知道自己的公网IP地址(端口),且,自己也需要知道对方的

那么,怎么解决呢?

image.png

引入一个公网服务器,完美解决以上2个问题。

这个服务器通常被称为『打洞服务器』,这个『洞』就是给NAT挖了个洞,让流量可以穿透过去。那么这个打洞服务器做了几个什么事情呢?

  • 识别局域网主机1和局域网主机2的外网地址以及端口
  • 相互交换对方的外网地址(端口)信息

这样,局域网主机1里的相关进程就知道了局域网主机2的外网地址(端口),反之同理。这样局域网主机1 、2里的某个相关进程都掌握了对方的外网地址(端口)。

那这样是不是就意味着局域网主机1、2能直接通信了呢?

答案是不一定。

回顾我们上面讨论的NAT的4种类型,会发现一种NAT类型是绝对无法打洞成功的。在此之前,我们来理一下正常情况下,是如何打洞的,步骤是什么。

1、主机A访问打洞服务器

2、主机B访问打洞服务器

3、打洞服务器交换两个主机的外网地址信息(端口)

4、主机A访问B的外网地址,此时NAT会记录B的外网地址,当流量到达主机B的NAT设备后,因为NAT设备『不认识』(没记录过A主机,也不知道这个流量要转发给那个主机),所以直接丢弃这个包,数据并不会到主机B,但是,此时B主机的NAT设备会记录A的外网地址。

5、此时,打洞服务器会通知主机B,让B主机马上把流量发送到主机A的外网地址,此时因为在第4步时主机A的NAT设备记录了主机B的外网信息,因此主机A的NAT设备会让主机B的流量通过,并且会把B的流量准确的转发给主机A。

6、此时,主机A会马上访问主机B的外网地址,因为在第4步时,主机B的NAT设备记录了主机A的外网地址,同时在第5步时也记录了主机B要访问主机A的外网地址的信息,因此,主机B的NAT设备会让主机A的流量通过,并且准确的转发到主机B上

经过以上6步,就能建立主机A与主机B之间的端到端的通信了。

至此事情似乎都很完美,但是回头看看我们上面所说的NAT类型的第四个,也就是『对称NAT类型』,在这种类型的NAT面前,此路不通了。我们来推演一下:

当此时NAT类型是第4中类型(对称NAT)时:

1、主机A访问打洞服务器;NAT-A设备记录映射(192.168.0.1:556643.250.201.20:6789),远程服务器记录A的外网地址为43.250.201.20:6789

2、主机B访问打洞服务器;NAT-B设备记录映射(192.168.0.2:7788113.250.201.20:4567),远程服务器记录B的外网地址为113.250.201.20:4567

3、交换2个外网地址信息

4、主机A访问B的外网地址,113.250.201.20:4567 ,NAT直接拒绝,并且不会留下任何记录。理由很简单,这个外网地址建立的访问路径是本机地址-外网IP:端口1-打洞服务器地址 ,流量只能在这条路径上传输,打洞服务器能与外网IP:端口1通信,但是当别的外网地址访问这个B的地址时,直接被拒。也就是说在对称NAT的类型中,本机地址-NAT外网地址-远程外网地址 三者构成了独一无二的通信链路,其他节点无法加入。

需要再三说明的是,这里的地址是指IP地址:端口号这一对组合

image.png

有NAT设备的地方,就有NAT类型。因此两端其实有10种两两组合的情况。

我们不能简单的说端口受限型NAT可以打洞,对称型NAT不能打洞,而是要看双方的组合是什么。

比如 对称型-对称型,这就是不能打洞的。而如果是受限锥型-对称型则可能可以。

那么,用来打洞的通常是UDP流量,还是TCP流量呢?答案是UDP流量。

答案很简单,并非TCP不行,而是用TCP打洞太难太难,操作手法复杂,而用UDP则相对简单很多。

为什么?

回想一下打洞过程:

主机A的某进程需要通过socket访问打洞服务器,为了让主机端口固定,使用bind,将某个固定端口绑定到本机,此后又要创建一个socket访问远程主机B,使用TCP无法创建2个本机IP和绑定端口一致的socket

UDP就没有这样的问题。

这算是『非面向链接』协议的一点点『好处』吧。

远程控制既可以通过C-S模式实现,或者p2p模式实现,至于那种方案更优,相信大家有自己的判断。(在p2p的打洞中,如果打不通,会切换成C-S模式)。当然,C-S的实现模型更简单,而NAT是可以嵌套的,因此情况会更复杂。

以上,就是有关NAT的相关内容。

posted on 2021-12-28 09:36  shadow_fan  阅读(1398)  评论(0编辑  收藏  举报

导航