惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
C
Cyber Attacks, Cyber Crime and Cyber Security
S
Secure Thoughts
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Spread Privacy
Spread Privacy
AWS News Blog
AWS News Blog
D
Docker
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
TaoSecurity Blog
TaoSecurity Blog
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
Cyberwarzone
Cyberwarzone
V
V2EX
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
WordPress大学
WordPress大学
P
Palo Alto Networks Blog
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 叶小钗
N
News and Events Feed by Topic
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Project Zero
Project Zero
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
Engineering at Meta
Engineering at Meta
S
Schneier on Security
Google DeepMind News
Google DeepMind News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
P
Proofpoint News Feed
S
SegmentFault 最新的问题
Hacker News: Ask HN
Hacker News: Ask HN
小众软件
小众软件
博客园 - 聂微东
S
Security Affairs
T
Tor Project blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
T
Threatpost
博客园 - 【当耐特】
L
LINUX DO - 热门话题
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
A
About on SuperTechFans
F
Fortinet All Blogs

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes 控制器管理总结 Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis 业务规则引擎演变过程简述 微服务中的熔断算法 漏桶算法和令牌桶算法 jsonparser 为什么比标准库的 encoding/json 快 10 倍 ? zap 高性能设计与实现 HTTP Router 算法演进 fastcache 高性能设计与实现 Web 常见的三个安全问题 ants Code Reading Go 线程安全 map 方案选型 布隆过滤器 死锁、活锁、饥饿、自旋锁 sync.Pool Code Reading Go 内存管理概述 Go netpoll Code Reading goroutine 泄漏与检测 time/Timer Code Reading GMP Scheduler Code Reading Go channel 的 15 条规则和底层实现 为什么 Linux “一切皆文件” context.Context Code Reading runtime/HACKING.md Goland 最佳实践 互联网开发与金庸武学 为什么 Redis 6.0 引入多线程模型? Kubernetes 应用最佳实践 - 金丝雀发布 容器中如何正确配置 GOMAXPROCS ? singleflight Code Reading sync.Map Code Reading sync.Cond Code Reading sync.WaitGroup Code Reading sync.RWMutex Code Reading sync.Mutex Code Reading sync.Once Code Reading Go 无锁编程 sync/atomic Code Reading goroutine 交替打印奇偶数 GODEBUG Go 并发模式 Go 汇编 UUID 通用技术选型 Kubernetes 应用最佳实践 - 水平自动伸缩 Go 高性能 Tips fasthttp 为什么比标准库 net/http 快 10 倍 ? 技术文章配图指南 ChatGPT 初体验 Docker 网络原理概览 iptables 的五表五链 Kubernetes 应用最佳实践 - 亲和性和污点容忍度 Go 的反射与三大定律 Docker 官方提供的最佳实践 Go 语言内置的设计模式 HTTP1 到 HTTP3 的工程优化 Kubernetes 应用最佳实践 - Sidecar 模式 Kubernetes 应用最佳实践 - init 容器和钩子函数 为什么 recover 必须在 defer 中调用? 为什么 defer 的执行顺序和注册顺序不同? Go map 设计与实现 Go 切片扩容底层实现 Go 语言中的零拷贝 Go Delve 云原生和边缘计算简介 Kubernetes Pod 服务质量等级 Kubernetes 应用最佳实践 - 探针 Kubernetes 应用最佳实践 - 资源请求和限制 CDN 原理 Kubernetes 应用最佳实践 - 开篇 缓存策略和模式 Go 内存模型 Kubernetes 核心概念
网络基础: 内网穿透 是如何实现的 ?
2018-02-16 · via 蛮荆

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

2018-02-16 计算机网络

  • 概述
    • 内网穿透 (打洞)
  • UDP 内网穿透
    • 相同 NAT
      • 穿透前
      • 穿透中
      • 穿透后
    • 不同 NAT
      • 穿透前
      • 穿透中
      • 穿透后
    • 多层不同 NAT
      • 穿透前
      • 穿透中
      • 穿透后
    • 心跳
  • TCP 内网穿透
    • TCP 内网穿透基本原理
  • 扩展阅读

概述

阅读本文前,需要熟悉 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 内网穿透差不多,这里不再赘述。


扩展阅读