蛮荆

网络基础: 内网穿透 是如何实现的 ?

2018-02-16

概述

阅读本文前,需要熟悉 NAT 实现原理,因为内网穿透的核心原理之一,就是需要能够穿透 NAT 网络设备,如果读者还不了解 NAT, 可以先阅读 这篇文章

本文以 P2P 网络场景为例来讲解内网穿透原理,读者也可以带入其他应用场景,例如远程协助工具、远程会议工具。

内网穿透 (打洞)

在大多数的情况下,参与 P2P 网络的设备位于使用 NAT 设备后面,这导致这些设备无法直接通过公共互联网进行通信。

内网穿透 (打洞) 技术的目标是使节点之间能够绕过 NAT,像两个正常的公网 IP 一样直接建立 P2P 连接,常见的实现方案有 TCP 打洞和 UDP 打洞,本文着重介绍 UDP 穿透 (打洞) 的实现原理。


UDP 内网穿透

连接双方节点通过 “中间服务器” 的协助,可以建立连接并使数据报文能够穿透对方的 NAT 网关。

假设节点 A 和节点 B 是 P2P 网络中的两个节点,且都位于各自的 NAT 网关之后。

  1. 节点 A 和节点 B 分别登录 “中间服务器” S,并上报各自的 内网 IP 和端口号,S 分别记录 A 和 B 的 公网+内网 IP 和端口号
  2. 节点 A 要向节点 B 发起连接建立,但是不知道节点 B 的 IP 地址和端口号,于是向 S 请求相关信息
  3. S 将 B 的公网+内网 IP 和端口号告诉 A
  4. 获取到 B 的 IP 和端口号之后,A 开始向 B 发起连接,发送数据

当然,针对节点 A 和节点 B 所在的 NAT 网关位置,具体的细节可能稍有差异,下面一起来看看不同的场景下的具体实现细节。

相同 NAT

在这种场景中,节点 A 和节点 B 的 NAT 网关是同一个,如图所示,A 和 B 的 NAT 设备 IP 都是 155.99.25.11

两个节点位于同一个 NAT

穿透前

  • 节点 A (10.0.0.1:4321) 通过 NAT 网关 (155.99.25.11) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 155.99.25.11, 62000

  • 节点 B (10.1.1.3:4321) 通过 NAT 网关 (155.99.25.11) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 155.99.25.11, 62005

穿透中

  • 节点 A 向 S 请求节点 B 的 IP 地址和端口号,数据包中包含 A 的内网地址+端口 (10.0.0.1:4321)
  • S 向节点 A 发送数据包,其中包含了节点 B 的 公网+内网 IP 和端口号,节点 A 收到数据包后,保存了 B 的地址和端口信息
  • S 向节点 B 发送数据包,其中包含了节点 A 的 公网+内网 IP 和端口号,节点 B 收到数据包后,保存了 A 的地址和端口信息
  • 此时节点 A 和节点 B 都有了对方的 IP 地址和端口号,穿透完成

穿透后

  • 节点 A 和节点 B 分别向对方发送数据 (使用 NAT 内网地址通信)

不同 NAT

在这种场景中,节点 A 和节点 B 属于不同的 NAT 网关,如图所示,A 的 NAT 设备 IP 是 155.99.25.11, B 的 NAT 设备 IP 都是 138.76.29.7

两个节点位于不同的 NAT

穿透前

  • 节点 A (10.0.0.1:4321) 通过 NAT 网关 (155.99.25.11) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 155.99.25.11, 62000

  • 节点 B (10.1.1.3:4321) 通过 NAT 网关 (138.76.29.7) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 138.76.29.7, 31000

穿透中

  • 节点 A 向 S 请求节点 B 的 IP 地址和端口号,数据包中包含 A 的内网地址+端口 (10.0.0.1:4321)
  • S 向节点 A 发送数据包,其中包含了节点 B 的 公网+内网 IP 和端口号,节点 A 收到数据包后,保存了 B 的地址和端口信息
  • S 向节点 B 发送数据包,其中包含了节点 A 的 公网+内网 IP 和端口号,节点 B 收到数据包后,保存了 A 的地址和端口信息
  • 此时节点 A 和节点 B 都有了对方的 IP 地址和端口号,穿透完成

穿透后

  • 节点 A 和节点 B 分别向对方发送数据 (使用两个 NAT 地址通信)

多层不同 NAT

在这种场景中,节点 A 和节点 B 属于不同且层次较多的 NAT 网关,如图所示,A 的 NAT 设备 IP 是 10.0.1.1, B 的 NAT 设备 IP 都是 10.0.1.3, 与此同时,节点 A 的 NAT 和节点 B 的 NAT 又属于同一个 NAT: 155.99.25.11

两个节点位于不同的 NAT

穿透前

  • 节点 A (10.0.0.1:4321) 首先通过 NAT 网关 (10.0.1.1),然后通过 NAT 网关 (155.99.25.11) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 10.0.1.1, 45000 (第一次 NAT 转换)

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 155.99.25.11, 62000 (第二次 NAT 转换)

  • 节点 B (10.1.1.3:4321) 首先通过 NAT 网关 (10.0.1.1),然后通过 NAT 网关 (155.99.25.11) 登录 “中间服务器” S

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 10.0.1.3, 55000 (第一次 NAT 转换)

  • 数据包中的 源 IP 地址、源端口分别被 NAT 网关改为 155.99.25.11, 62005 (第二次 NAT 转换)

穿透中

  • 节点 A 向 S 请求节点 B 的 IP 地址和端口号,数据包中包含 A 的内网地址+端口 (10.0.0.1:4321)
  • S 向节点 A 发送数据包,其中包含了节点 B 的 公网+内网 IP 和端口号,节点 A 收到数据包后,保存了 B 的地址和端口信息
  • S 向节点 B 发送数据包,其中包含了节点 A 的 公网+内网 IP 和端口号,节点 B 收到数据包后,保存了 A 的地址和端口信息
  • 此时节点 A 和节点 B 都有了对方的 IP 地址和端口号,穿透完成

穿透后

  • 节点 A 和节点 B 分别向对方发送数据 (使用外层 NAT 155.99.25.11 通信)

心跳

一般情况下, NAT 网关/设备针对 UDP 连接有自动关闭/删除的机制 (例如空闲/超时计数),所有 UDP 穿透存在有效期限制,所以通信的节点双方,需要定时向对方发送心跳包,保证内网穿透可以维持正常。


TCP 内网穿透

TCP 内网穿透比 UDP 内网穿透复杂,因为 TCP 是一个面向连接的协议,需要通过三次握手建立连接。相比之下,UDP 只需要处理一个套接字的收发通信,而 TCP 需要处理多个套接字绑定同一个端口(端口复用),此外大部分 NAT 网关/设备对 UDP 协议支持更友好一些。

TCP 内网穿透基本原理

  • 节点 A 和节点 B 通过 NAT 网关登录 “中间服务器” S, 同时监听各自的本地 TCP 端口,等待外部的请求建立连接
  • 节点 A 向 S 请求节点 B 的 IP 地址和端口号,数据包中包含 A 的内网地址+端口
  • S 向节点 A 发送数据包,其中包含了节点 B 的 公网+内网 IP 和端口号,节点 A 收到数据包后,保存了 B 的地址和端口信息
  • S 向节点 B 发送数据包,其中包含了节点 A 的 公网+内网 IP 和端口号,节点 B 收到数据包后,保存了 A 的地址和端口信息
  • 此时 节点 A 有了节点 B 的 IP 地址和端口号,向节点 B 发起 TCP 连接

连接建立后,双方通过相同的 (或者不同的) NAT 网关/设备进行通信,流程和前文中提到的 UDP 内网穿透差不多,这里不再赘述。


扩展阅读

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。