內網 - 公網穿透 實現原理
概述
閱讀本文前,需要熟悉 NAT 實現原理,因爲內網穿透的核心原理之一,就是需要能夠穿透 NAT 網絡設備,如果讀者還不瞭解 NAT, 可以先閱讀 這篇文章 [1]。
本文以 P2P 網絡場景爲例來講解內網穿透原理,讀者也可以帶入其他應用場景,例如遠程協助工具、遠程會議工具。
內網穿透 (打洞)
在大多數的情況下,參與 P2P 網絡的設備位於使用 NAT 設備後面,這導致這些設備無法直接通過公共互聯網進行通信。
內網穿透 (打洞) 技術的目標是使節點之間能夠繞過 NAT,像兩個正常的公網 IP 一樣直接建立 P2P 連接,常見的實現方案有 TCP 打洞和 UDP 打洞,本文着重介紹 UDP 穿透 (打洞) 的實現原理。
UDP 內網穿透
連接雙方節點通過 “中間服務器” 的協助,可以建立連接並使數據報文能夠穿透對方的 NAT 網關。
假設節點 A 和節點 B 是 P2P 網絡中的兩個節點,且都位於各自的 NAT 網關之後。
-
節點 A 和節點 B 分別登錄 “中間服務器” S,並上報各自的 內網 IP 和端口號,S 分別記錄 A 和 B 的 公網 + 內網 IP 和端口號
-
節點 A 要向節點 B 發起連接建立,但是不知道節點 B 的 IP 地址和端口號,於是向 S 請求相關信息
-
S 將 B 的公網 + 內網 IP 和端口號告訴 A
-
獲取到 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 內網穿透差不多,這裏不再贅述。
擴展閱讀
-
libp2p implementation in Go[2]
-
Command line peer-to-peer data transfer tool [3]
-
NAT 穿透技術原理及企業級實踐 [4]
-
P2P 技術原理淺析 [5]
鏈接
[1]
這篇文章: https://dbwu.tech/posts/network/how-nat-works/
[2]
libp2p implementation in Go: https://github.com/libp2p/go-libp2p
[3]
Command line peer-to-peer data transfer tool : https://github.com/dennis-tra/pcp
[4]
NAT 穿透技術原理及企業級實踐: https://arthurchiao.art/blog/how-nat-traversal-works-zh/
[5]
P2P 技術原理淺析: https://keenjin.github.io/2021/04/p2p/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/H_ngWHKcKkyIMko8nc-83Q