標籤:

IPSec 的 NAT 探測與穿越機制(二)怎麼辦

IPSec 的 NAT 探測與穿越機制(二)怎麼辦

來自專欄計算機網路診斷科

前言

上一篇文已經聊完了為什麼 ESP 包沒法過 NAT。有些情況下,實在是沒有辦法避免 NAT, 又一定要用 IPSec 的話,那就只能想辦法讓它過 NAT。這篇就聊一聊如果一定要讓 ESP 包過 NAT 應該怎麼辦。


目錄

  1. 一個故事
  2. 沒有什麼是加一層封裝不能解決的
  3. 整理問題
  4. 解決問題

一個故事

A 要和 B 見面。A 高高興興的出門了,走著走著撲通一聲掉進了河裡,沒見成 B ,濕漉漉的回家了。第二天,A 又出門去要見 B, A 又到了河邊,這河很寬,跳是跳不過去的。那麼,要麼學習游泳技術,自己游過去;要麼坐船過去。A 顯然選擇坐船,因為自己學游泳太麻煩了啊!A坐上船過了河到了渡口,下船一看,B 呢?哎呀,我沒告訴他我坐船來! B 在原來約好的地方等了半天還沒看見 A ,兩人還是沒見著。第三天, A 學聰明了,它先派出了自己有十八般武藝的手下 C 去探路。一條河對 C 那是小意思,輕鬆游過去。C 很快回來了,向 A 彙報 「老大,路上有河,要坐船。B 也知道了,我已經讓他在渡口等您了。」這次所有問題都解決了,A 也終於見到了 B。

沒有什麼是加一層封裝不能解決的

對 ESP 來說,擺在眼前兩條路:要麼改造自己,加一些技能;要麼藉助他人,把自己當成貨物裝進別的能過 NAT 的協議里,讓別的協議帶著自己過 NAT。

當然選擇把自己裝進別的協議里了,改造自己多麻煩啊。那用什麼協議呢?既然是要過 NAT,那當然選一個一定可以過 NAT 的協議了。互聯網上大部分流量都是 TCP 和 UDP 一個 NAT 設備,如果不支持 TCP 或 UDP 的穿越,那麼這個 NAT 設備基本可以扔進垃圾桶里了,所以我們在 TCP 或 UDP 里二選一。當然選 UDP,因為 TCP 比 UDP 多了保證送達和有序,ESP 本來直接裝在 IP 包里,IP 本來也不保證送達和有序,所以用 TCP 是不必要的。UDP 就相當於給原來沒有埠號的 ESP 包加了埠號,於是他就可以輕鬆過 NAT 了。

UDP封裝的ESP包格式(RFC3948)

但是,把 ESP 裝 UDP 就行了嗎?回想前一篇文章里提到的 IP 網路成功通信的兩個必要條件:① IP 包送達。②正確理解 IP 包。對方本來期望收到一個直接裝在 IP 包里的 ESP ,現在突然送過去一個裝在 UDP 里的 ESP ,是能過 NAT 了,但是對方不知道怎麼解包了,不還是沒法通信嘛。

那我們需要想辦法讓雙方知道:① 要不要用 UDP 封裝。② 用 UDP 封裝了以後,源埠和目的埠分別是什麼。只有知道了這兩個信息,設備才能知道,接收數據的時候,我需要等的是一個目的埠號是 y,源埠號 x 的 UDP 包,打開裡面是 ESP 然後根據 SPI 查出 SA。發送數據給對方的時候,我需要把 ESP 裝進 UDP ,目的埠號是 x ,源埠號是 y 。 如果不知道這些信息,設備只能一直等裝在 IP 包里的 ESP 包,然後什麼也等不到,又或者是不知道要怎麼發數據包給對方。

有 NAT 的網路

再次考慮前文的那個場景,假設:R1 和 R2 都與 R3 建立了 IPSec 隧道。在建立隧道的過程中也想辦法讓 NAT 設備知道了映射關係。假設映射關係如下。

NAT 表項一:

協議:UDPNAT 前:源地址:10.0.0.1源埠:4500目的地址:1.1.1.2目的埠:4500NAT 後:源地址:1.1.1.1源埠:16000

NAT 表項二:

協議:UDPNAT 前:源地址:10.0.0.2源埠:4500目的地址:1.1.1.2目的埠:4500NAT 後:源地址:1.1.1.1源埠:17000

現在 R3 又要分別給 R2 和 R1 發包。

那麼 R3 給 R1 的包是:

源地址:1.1.1.2源埠:4500目的地址:1.1.1.1目的埠:16000協議:UDP(17)

給 R2 的包是:

源地址:1.1.1.2源埠:4500目的地址:1.1.1.1目的埠:17000協議:UDP(17)

目的埠不一樣了,NAT 設備就知道哪個包應該給誰了,那麼包就能送到正確的設備上。經過協商,設備也能正確的理解 IP 包了,那麼通信就沒有問題了!

整理問題

再來整理一下上面的討論,看看為了穿越 NAT 我們需要做什麼。

  • 了解對方是否支持 NAT 穿越功能

如果對方不支持 NAT 穿越功能,那就沒辦法配合自己探測路上有沒有 NAT,也沒辦法拆開 UDP 包找到裝在裡面的 ESP。

  • 了解路上有沒有 NAT

確定對方支持 NAT 穿越以後,雙方配合共同探測路上有沒有 NAT 。如果沒有 NAT 那就直接把 ESP 裝在 IP 包里就好了,沒有必要再加一層,浪費帶寬也浪費設備的處理能力。如果有 NAT 那就加一層 UDP 封裝,由 UDP 帶著 ESP 穿過 NAT。

  • 為 UDP 封裝的傳輸做好準備

因為需要雙向通信,發起端需要先發出一個 UDP 包觸發 NAT 設備建立 NAT 表項。並且響應端需要知道 UDP 的包目的埠是什麼。只有這樣,響應端的回包才能穿過 NAT 回到發起端的手上。

  • 保持 NAT 表項不被刪除

IPSec 隧道建立了以後,還不能高枕無憂。如果兩端都沒有數據要發,一段時間以後,NAT 設備就會老化掉相應的 NAT 表項,這可不是我們希望看到的情況,所以我們需要定時發送一些數據讓 NAT 設備不要刪掉相應的 NAT 表項。

解決問題

  • 了解對方是否支持 NAT 穿越

在 IKE message 1 和 2 裡帶上代表本實現支持 NAT-Traversal 的 vendor id,兩條消息交換過後,雙方都知道是否支持 NAT-Traversal。

IKE message 1

IKE message 2

好了,現在雙方都知道對方支持 NAT-Traversal 了。

  • 探測鏈路上是否有 NAT

NAT 的全稱 Network Address Translation,網路地址轉換。要知道鏈路上有沒有 NAT ,讓對方看看自己的地址有沒有被轉換就好啦。我們可以在 IKE 包里把本地看見的 IP 地址發給對方,然後讓對方檢查一下 IKE 包里的 IP 地址是不是和 IP 包的源地址一樣就知道路上有沒有 NAT,以及誰在 NAT 後面。把埠號和IP 以及發起方及響應方的 SPI 做一個 hash。

HASH = HASH(CKY-I|CKY-R|IP|Port)

CKY-I 是發起方的 SPI,CKY-R 是響應方的 SPI。把他們一起放進 HASH 里可以增加安全性。如果對方看見的 IP 和埠號和自己看見的 IP 和埠號不一樣,說明自己的IP和埠號在路上被人改了,很顯然自己是在 NAT 後面了。

IKE message 3 中的 NAT-D 載荷

IKE message 4 中的 NAT-D 載荷

上圖中有每個包中兩個 NAT-D 載荷。第一個是對方 IP 和 埠的 HASH,第二個是自己的介面 IP 和 埠的 HASH 。比對一下,就可以知道誰在 NAT 後面。

  • 為 UDP 封裝的傳輸做好準備

好了,萬事俱備了。我們現在要用 UDP 流量去觸發 NAT 設備建立 NAT 表項,準備好 ESP 的傳輸。

NAT 後的設備改成 UDP 封裝,源目埠均為 4500,發出。NAT前的設備收到一個目的埠為 4500 ,源埠為 Y(被NAT修改過) 的數據包,解密這個包並把源埠 Y 記錄下來。

以後 NAT 前的設備發出的 IKE 和 ESP 包都是 UDP 封裝,目的埠為 Y ,源埠為 4500。到達 NAT 設備後,NAT 設備會將目的埠改為 4500 然後送給 NAT 後的設備。這樣一來,既讓 NAT 設備建立了相關的映射表項,又讓對端知道了應該發往哪個埠,一舉兩得。

又有問題了,IKE 包和 ESP 包都跑在 UDP 4500 上了,如何區分呢?

我們給其中一種包做特殊標記,如果沒有標記,就是另一種包。如果是 IKE 的包,在 UDP 包頭後面加 4 bytes 的零,叫做 Non-ESP Marker。

裝在UDP 里的 IKE

裝在 UDP 里的 ESP 沒有標記

  • 保持 NAT 表項不被刪除

定期的發送一個 keepalive 包,刷新 NAT 設備的老化定時器,這樣就使得自己的 NAT 表項不被刪掉。

NAT keepalive 包格式

所有的問題都解決了,雙方就可以愉快的通信了!

參考閱讀:

UDP Encapsulation of IPsec ESP Packets?

tools.ietf.org

Negotiation of NAT-Traversal in the IKE?

tools.ietf.org


推薦閱讀:

計算機網路教程之物理層
如何判斷本地網路好壞?高帶寬等於低延遲嗎?
Python網路編程中的套接字名和DNS解析。
各種加密代理協議的簡單對比
負載均衡伺服器nginx詳細安裝教程及網路部署

TAG:計算機網路 |