NAT穿透 + CS144 Lab简述

本文最后更新于:2 年前

在面试完之后,发现自己计算机网络知识讲得非常不清楚。其中印象特别深的一个问题“两个不同内网的设备如何通信”回答得极差。于是自己在寒假里听了一遍中科大郑烇老师的课后,快速过了一遍CS144的Lab(参考学习了其他同学的实现)。本文将主要记录上述面试题的一部分答案——NAT穿透的细节并简述CS144 Lab的内容。【别问我为什么把这两个内容放在一起嘿嘿】

本文不会介绍NAT的基本概念。直接讨论NAT潜在的问题与穿透技术。

NAT/NAPT 都使用自己的转换表,因此会有以下的问题:

  • 外部无法主动与 NAT 内部服务器建立连接,因为 NAPT 转换表没有转换记录。
  • 转换表的生成与转换操作都会产生性能开销。
  • 通信过程中,如果 NAT 路由器􏰿启了,所有的 TCP 连接都将被􏰿置。

针对以上的问题主要有两种方法。

第一种方法就是改用IPv6,这个很好理解,每个设备都有公网IP了就不需要转换地址了。不过目前还未全面普及。

另一种方法是NAT穿透技术。也就是本文将重点关注的技术。

NAT穿透

回到面试题“两个不同内网的设备如何通信”上,两个均位于NAT设备之后的设备无法主动发起相互通讯的请求。一个简单的想法就是通过一个位于公网的服务器充当建立维护连接与转发的工作,但是很明显的缺点也就是带宽可能会被这个位于公网的服务器限制,会产生效率瓶颈。要避免这个限制,即可在公网服务器建立好双方连接后,让双方直连。下面便会讨论这种方法的实现。

下面介绍NAT的4种限制类型,也就是NAT网关的类型,根据两个NAT网关不同的限制类型,共有10种组合方式(其中有当双方都为对称型或一方对称型一方端口限制型锥型时不易穿透)。

NAT的限制类型

根据NAT访问限制可以分为全锥型、IP受限锥型、端口受限锥型与对称型4种,限制逐渐增大。

  1. 完全锥型:如果在NAT网关已经建立了一个NAT映射,那么任何外网的机器都可以通过这个映射来访问内网的电脑
  2. IP限制型锥型:如果在NAT网关已经建立了一个NAT映射,那么只有与其建立映射的ip才能通过NAT访问内网的电脑。
  3. 端口限制型锥型:这个类型名称不是很准确,其实限制的不只是端口,也同样限制IP可以理解为IP+端口限制型锥型。
  4. 对称型NAT:对称型NAT和端口限制型锥型是一样的,即对IP和端口都有限制,只有和其建立”连接”的IP和端口向其发送数据才不会被丢弃。 但是对称型NAT在内网设备即使主动使用相同的端口去和不同服务器或相同的服务器不同的端口建立连接时,NAT网关也会给其重新分配一个端口映射维护此“连接”关系。也就是一个完全1:1的关系,不会复用之前创建的映射关系。

两设备能否轻易打洞通信需要看双方NAT网关的类型。

第一种:完全锥型NAT和完全锥型NAT进行穿透

假设A和B都是完全锥型NAT,在A和B都连接到server后,A和B都可以借助server的转发互相发送消息,那么此刻A和B就可以知道对方的公网ip,以及对方和server连接的时候,使用的端口是什么(假设是100),因为两者和server1进行通信的端口已经进行了NAT映射,所以二者的100端口其实已经完成映射,又因为二者都在完全锥型NAT下,此刻A只需要直接给B的100端口发送建立连接的请求,B给A的100端口回复同意建立连接的请求,二者即可建立UDP连接(当然UDP连接建立要比这复杂点,不过本文重点不在于如何建立UDP连接)

第二种:IP/端口限制型NAT和IP/端口限制型NAT进行穿透

假设A和B都是ip限制型NAT,在A和B都连接到server后,A和B都可以借助server的转发互相发送消息同样由于已经和Server建立连接,目前A、B都可以通过Server知道彼此NAT网关的IP及转换后的端口,A会先发送一个UDP请求(假设自己的NAT网关端口为100,目标NAT网关端口为200)到B的公网ip上,理论上来说,因为B的NAT网关中,200端口没有建立NAT映射,所以这个数据包会被丢弃,但是在A发送给B的UDP请求后,A会通过server给B发送一个邀请,邀请B也发送一个UDP请求给A(此刻B自己用的端口是200,目标端口是100),注意,在B收到来自A的UDP请求后,虽然A的数据包被B丢弃了,但是此刻,网关A暂时的建立了一个NAT映射,等待B返回的信息,虽然数据包已经被丢弃了,但是A不知道,所以A会稍微等一会B。这时,B收到了A的邀请,给A发送了一个建立连接的请求,此刻A的NAT网关恰巧暂时建立了NAT映射,所以A就可以收到B的UDP请求,接着A会给B发送一个同意建立连接的请求,因为此刻B刚发完请求在等A的回信,所以B的NAT网关也会暂时的建立一个NAT映射,所以A同意建立连接的请求就不会被B的NAT网关丢弃,最终,二者就建立了一个稳定的UDP连接

NAT穿透

第三种:对称型NAT与端口限制型/对称型NAT的穿透

借助上述思想,我们就可以完成不需要服务器一直进行中转的内网穿透,那么为什么对称型NAT不能使用上述思想和对称型NAT穿透呢。

在第二种方法中,A邀请B给其发送一个UDP请求,在邀请的信息中,A指明了B的UDP请求的目标端口,因为在锥型NAT中,主机A的一个端口和NAT网关的映射是固定的,所以主机A可以通过Server知道自己给B发送请求是打开的端口是哪一个,也可以知道B给自己发送请求是打开的端口是哪一个,但是当换到对称NAT中时,由于一个连接对应NAT网关上的一个端口,所以主机A无法确定自己通过哪一个端口给B发信息,同样无法确定B会通过哪一个端口给自己发信息,所以二者无法建立连接。

那么端口限制型又为什么不能和对称型进行穿透呢,因为端口限制型对端口存在要求,但是我们无法确定对称型中分配的端口是哪一个,对称型NAT新建的映射信息是会被锥型NAT拒收的,数据无法进入内网,这个新的映射端口就只能靠猜了

简单说就是网关后的主机没法直接知道网关分配的端口是什么,如果锥型网关可以借助Server获取分配的端口信息,而对称型的由于无法确定这个分配的端口而难以进行打洞。

如果这里对称NAT使用顺序增加策略,那么,B将两次被分配的NAT端口发送给Server后,Server就可以通知A在这个端口范围内猜测刚才B发送给它的socket中被NAT映射后的端口,A很有可能在孔有效期内成功猜测到端口号,从而和B成功通信。

NAT打洞与P2P的关系

由于目前大多数端设备都在NAT网关之后,所以若两台设备想要进行P2P连接就必须先进行NAT打洞操作。

参考

NAT基本原理及穿透详解(打洞)

B站最详细内网穿透原理

突然换题!主要是写给自己看的…

CS144 Lab

简单记录各个Lab完成的任务,自己还没能全部完成。

lab 0

  • 使用telnet & netcat等命令发送和接受应用层的数据
  • 使用sponge自带的功能那个接口写一个简单的套接字webget程序
  • 实现ByteStream作为TCP连接的最底器的容器(有限的内存网络流队列)

lab 1

  • 在ByteStream的基础上实现StreamReassembler

    • 解决数据包的重复重叠乱序问题(暂时不用考虑丢包问题)

      使用拥有排序能力的数据结构如 红黑树 存储未整合好的数据,新插入的数据包分别不断和左右节点合并看是否整合完毕。

lab 2 & 3

  • TCP receiver
    • 基于收到包的连续性的假设,正确转换32位的seq到64位的index
    • 将收到的包传给StreamReassembler
    • 计算出正确的ackno
    • 计算出正确的window_size,以通知发送方实现流量控制
  • TCP sender
    • TCP可靠数据传输机制的实现
      • 定时器(单一的重传定时器)
      • 序号和确认(确认是被捎带在数据报文段中的)
    • TCP连接管理:SYN和FIN
    • 实现流量控制的另一部分,发送方根据接收方的窗口大小调整流量

lab 4

  • TCP connection
    • 整合sender和receiver实现TCP过程
    • TCP状态机:三次握手,正常通信,四次挥手(jyy所说的画状态机模型真的很有用,每个Lab都用到了!)

lab 5

  • 实现一个network interface,也就是网卡以及arp协议
  • 网卡中有一个arp表,实现ip地址和mac地址的转换,它通过arp协议自动生成,每个表项都会过期
  • arp协议是一个链路层和网络层中间的协议,它在层次关系上和ip(网络层)很像,我们需要把arp包封装为一个以太网包(或其他链路层帧)发送出去,因而在体系结构上位于链路层之上,但是一个arp分组具有包含链路层地址的字段,因而也可认为是链路层协议。
  • 借由链路层的广播帧,可以将arp分组发送给所有人,从而得到预期的回复

lab 6

  • 实现一个路由器,对应网络层的数据平面,根据输入的规则,按照最长前缀匹配规则转发
  • 基于lab 5的network interface:一个路由器有多个interface

基本除了拥塞控制,其余TCP主体部分都有相应的实现。自己实验做得很粗糙,就不多妄加评论了。

参考

CS144-计算机网络学习笔记


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!