1. 前言
IPSec提供了端到端的IP通信的安全性,但在NAT環境下對IPSec的支持有限,AH協議不能進行NAT,這和AH設計的理念是相違背的;ESP協議在NAT環境下最多只能有一個***主機能建立***通道,無法實現多臺機器同時在NAT環境下進行ESP通信。關於IPSec在NAT環境下的需求問題在RFC3715中進行了描述。
NAT穿越(NAT Traversal,NAT-T)就是爲解決這個問題而提出的,在RFC3947,3948中定義,在RFC4306中也加入了NAT-T的說明,但並沒廢除RFC3947,3948,只是不區分階段1和階段2。該方法將ESP協議包封裝到UDP包中(在原ESP協議的IP包頭外添加新的IP頭和UDP頭),使之可以在NAT環境下使用的一種方法,這樣在NAT的內部網中可以有多個IPSec主機建立***通道進行通信。
2. IKE協商使用UDP封裝
RFC3947主要描述如何檢測是否存在NAT設備,並如何在IKE中協商使用UDP來封裝IPSec數據包。
2.1 檢測
功能是檢測通信中是否存在NAT設備和對方是否支持NAT-T。
正常的IKE協商使用的UDP包的源和目的端口都是500,如果存在NAT設備,大多數情況下該UDP包的源端口部分會改變,只有少數情況不改。接收方如果發現UDP源端口不是500,那可以確定數據是經過了NAT設備。另外,確定NAT的位置也是重要的,在檢測對方失效(DPD)時,應該儘量由在NAT設備後面的一方主動進行DPD探測,而從另一方探測有可能會失敗。
檢測對方是否支持NAT-T是通過交換vendor ID載荷來實現的,如果自身支持NAT-T,在IKE開始交互就要發送這種載荷,載荷內容是“RFC 3947”的MD5值,也就是十六進制的“
判斷是否在NAT設備後面是通過發送NAT-D(NAT-Discovery)載荷來實現的,載荷內容是IP地址和UDP端口的HASH值,NAT-D載荷格式如下,載荷類型值是20:
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+---------------+---------------+---------------+---------------+
| Next Payload | RESERVED | Payload length |
+---------------+---------------+---------------+---------------+
~ HASH of the address and port ~
+---------------+---------------+---------------+---------------+
HASH值的計算方法如下,具體HASH是根據協商來確定的:
HASH = HASH(CKY-I | CKY-R | IP | Port)
CKY-I和CKY-R是協商發起方和響應方的cookie。
協商中雙方各自至少要發送兩個NAT-D載荷,第一個載荷是對方的地址和端口的HASH,後面的載荷是自己的地址和端口,如果本地有多個地址,則要發送多個載荷,包括所有地址和端口的HASH,對方接收到載荷後重新根據收到的包的實際地址端口來計算HASH值後進行比較,就可以知道是否有NAT設備以及哪一方在NAT設備之後了。
有些的NAT設備具有端口固定的功能,也就是進行NAT轉換後只改變地址而不改變端口,而且針對IKE通信使用cookie來區分內部各個IPSec設備的IKE連接,後續IKE協商如果繼續用500端口協商就會出現問題。對於這類設備,解決方法是改變IKE協商端口從500到4500,因爲這些設備只對500端口進行特殊處理而不對4500端口處理。在協商時,發現了自己在NAT設備之後的一方立即要將協商端口從500該爲4500,源和目的端口都是4500,此時協商數據包格式爲:
IP UDP(4500,4500) <non-ESP marker> HDR*, IDii, [CERT, ] SIG_I
其中“non-ESP marker”爲4字節,在後面介紹其值。
接收方接收此包解密並認證通過後,要改變自己的狀態將原來處理500端口狀態改爲處理4500端口,後續的協商過程都使用4500端口進行,以後500端口收到的不是新協商的包都將被丟棄,協商過程爲:
Initiator Responder
------------ ------------
UDP(500,500) HDR, SA, VID -->
<-- UDP(500,X) HDR, SA, VID
UDP(500,500) HDR, KE, Ni,
NAT-D, NAT-D -->
<-- UDP(500,X) HDR, KE, Nr,
NAT-D, NAT-D
UDP(4500,4500) HDR*#, IDii,
[CERT, ]SIG_I -->
<-- UDP(4500,Y) HDR*#, IDir,
[ CERT, ], SIG_R
如果支持NAT-T,在ID載荷中的端口值要設置爲0。
2.2 UDP封裝協商
UDP封裝方式有兩種,一種是UDP通道模式封裝,一種是UDP傳輸模式封裝,取值爲:
UDP-Encapsulated-Tunnel 3
UDP-Encapsulated-Transport 4
當發現存在NAT設備後,就要選擇這兩者模式中的一種,而一般正常的通道模式封裝和傳輸模式封裝取值分別爲1和2。在協商封裝模式時,提議一方或者是提議NAT模式下的3、4,或者是提議非NAT下的1、2,而不應該同時提議1、2、3、4這四種模式。
由於在計算TCP/UDP校驗和的時候要用到IP地址部分,因此需要知道對方的實際原始地址值,因此協商時雙方要發送各自的原始地址,使用NAT-OA (NAT Original Address)載荷進行發送,對應發起方來說,NAT-OA載荷中的地址就是自己實際地址和對方的公網地址,而對於響應方來說,NAT-OA載荷中的對方的公網地址和自己的實際地址。
NAT-OA載荷格式爲,這種載荷的類型爲21:
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
+---------------+---------------+---------------+---------------+
| Next Payload | RESERVED | Payload length |
+---------------+---------------+---------------+---------------+
| ID Type | RESERVED | RESERVED |
+---------------+---------------+---------------+---------------+
| IPv4 (4 octets) or IPv6 address (16 octets) |
+---------------+---------------+---------------+---------------+
其中ID Type只能爲ID_IPV4_ADDR或者ID_IPV6_ADDR,保留字段值都要置0。
值得注意的是只是在傳輸模式下需要傳NAT-OA載荷,但通道模式下就沒必要傳了,具體原因看下節UDP封裝方法後再說明。
交換過程如下:
Initiator Responder
------------ ------------
HDR*, HASH(1), SA, Ni, [, KE]
[, IDci, IDcr ]
[, NAT-OAi, NAT-OAr] -->
<-- HDR*, HASH(2), SA, Nr, [, KE]
[, IDci, IDcr ]
[, NAT-OAi, NAT-OAr]
HDR*, HASH(3) -->
3. UDP封裝通信數據
RFC3948描述如何對ESP包進行UDP封裝和解封裝,主要面向實際的通信數據處理。
封裝ESP包的UDP包所用的端口和IKE協商的端口是相同的,一般都是4500,這樣NAT設備也不需要處理兩個端口了,區分一個UDP封裝包的是IKE協商數據還是實際ESP數據是根據ESP頭中的SPI字段來進行的,SPI爲0表示是IKE數據,否則爲實際ESP數據。
3.1. UDP封裝ESP包格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ESP header [RFC4303] |
~ ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
所定義的UDP頭是由RFC768定義的標準UDP頭,源端口和目的端口都必須是相同的,校驗和字段應該設爲0,ESP本身有完整性認證功能,不需要UDP的校驗和,ESP頭的最開始4個字節是SPI字段,該字段絕不能是0。
因此傳輸模式下一個TCP上層包被封裝爲:
應用ESP/UDP之前
----------------------------
IPv4 |orig IP hdr | | |
|(any options)| TCP | Data |
----------------------------
應用ESP/UDP之後
-------------------------------------------------------
IPv4 |orig IP hdr | UDP | ESP | | | ESP | ESP|
|(any options)| Hdr | Hdr | TCP | Data | Trailer |Auth|
-------------------------------------------------------
|<----- encrypted ---->|
|<------ authenticated ----->|
數據包經過NAT設備後,只修改外部IP頭和UDP頭中的數據,TCP頭中的數據是不可能修改的(實際NAT設備也根本看不到TCP頭數據,因爲是被ESP加密了的),經過ESP/UDP協議解封裝處理,到達應用層時的數據包剩下修改後的外部IP頭和原始TCP頭,因此TCP頭中的校驗和必須和修改後的IP頭中的地址匹配,否則在應用層看來就是錯誤的,因此IKE時必須交換NAT-OA載荷,使ESP/UDP處理層知道如何修改TCP校驗和,而對於上層的UDP協議,可以簡單的將校驗和置0即可。
通道模式下數據封裝爲:
應用ESP/UDP前
----------------------------
IPv4 |orig IP hdr | | |
|(any options)| TCP | Data |
----------------------------
應用ESP/UDP後
--------------------------------------------------------------
IPv4 |new h.| UDP | ESP |orig IP hdr | | | ESP | ESP|
|(opts)| Hdr | Hdr |(any options)| TCP | Data | Trailer |Auth|
--------------------------------------------------------------
|<------------ encrypted ----------->|
|<------------- authenticated ------------>|
內部IP頭也是ESP載荷的一部分,因此TCP校驗和已經是根據內部IP頭計算的了,因此在IKE時不用交換NAT-OA載荷,解封處理只需要按順序依次解開UDP和ESP就能還原原始數據包。
3.2 UDP封裝IKE包格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Non-ESP Marker |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IKE header [RFC4306] |
~ ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
源和目的端口是4500;UDP校驗和可以計算也可以不用設,沒作限定,但該字段絕不能作爲判斷包類型的依據;Non-ESP Marker字段必須設置爲0,該字段位置等價於ESP頭的SPI字段,因此該字段爲0表示是IKE數據,不爲0表示是ESP數據。
3.3. NAT連接保持包
NAT連接保持包是爲了檢查NAT映射是否一直存在,或者是在沒有其他包發送給對方是發送此包用來保持連接,但接收到該包不能用作連接有效的判據。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xFF |
+-+-+-+-+-+-+-+-+
源和目的端口是4500;UDP校驗和字段應該設爲0;數據部分就一個字節0xff,接收方不進行迴應。
4. 衝突
在某些情況下NAT-T會導致一些衝突,如在通道模式下,對於下面的拓撲結構:
+----+ \ /
| |-------------|----\
+----+ / \ \
A NAT 1 \
\
+----+ \ / \ +----+ +----+
| |-------------|----------+------| |----------| |
+----+ / \ +----+ +----+
B NAT 2 遠程***網關 遠程服務器
A和B具有相同的內部IP都通過NAT後訪問遠程服務器,但對於遠程***網關和服務器來說,解開包後看到的內部IP都是相同的,因此就不知道該將數據發給誰,這時就會發生衝突,解決方法是***通信的內部IP完全由***網關來進行分配,而不是本地設置,這樣***網關就能區分不同的遠程***客戶的訪問,在IKEv2(RFC4306)中已經包括了內部IP分配的內容。
在傳輸模式下更容易發生問題,對於下面的拓撲結構:
+----+
| |
+----+ \
A \
\
+----+ \ / +----+
| |-----+-----------------| |
+----+ / \ +----+
B NAT 服務器
/
/
+----+ /
| |/
+----+
C
A和B通過***通道和服務器連接,如果都訪問服務器上的相同服務,由於傳輸模式沒有原始IP頭信息,因此服務器包無法區分包到底是來自A還是B的,因此不能確定使用哪個SA來加密通信;如果增加第3個客戶端C,使用的是明文訪問服務器,服務器將更暈頭轉向了。解決方法還是需要用通道模式進行通信。