IPSec的NAT穿越

来源:互联网 发布:淘宝潘多拉代购曝光 编辑:程序博客网 时间:2024/04/30 01:16
参考资料:RFC3715,3947,3948

1. 前言

IPSec提供了端到端的IP通信的安全性,但在NAT环境下对IPSec的支持有限,AH协议是肯定不能进行NAT的了,这和AH设计的理念是相违背的;ESP协议在NAT环境下最多只能有一个VPN主机能建立VPN通道,无法实现多台机器同时在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主机建立VPN通道进行通信。

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值,也就是十六进制的“4a131c81070358455c5728f20e95452f”。

判断是否在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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Source Port         |     Destination Port       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Source Port         |     Destination Port       |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |       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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Source Port         |     Destination Port       |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |       Length         |       Checksum         |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   0xFF     |
  +-+-+-+-+-+-+-+-+

源和目的端口是4500;UDP校验和字段应该设为0;数据部分就一个字节0xff,接收方不进行回应。

4. 冲突

在某些情况下NAT-T会导致一些冲突,如在通道模式下,对于下面的拓扑结构:

  +----+         / /
  |   |-------------|----/
  +----+         / /   /
    A         NAT 1   /
                    /
  10.1.2.3               /
  +----+         / /     /     +----+       +----+
  |   |-------------|----------+------|   |----------|   |
  +----+         / /           +----+       +----+
    B         NAT 2         远程VPN网关     远程服务器
  10.1.2.3

A和B具有相同的内部IP都通过NAT后访问远程服务器,但对于远程VPN网关和服务器来说,解开包后看到的内部IP都是相同的,因此就不知道该将数据发给谁,这时就会发生冲突,解决方法是VPN通信的内部IP完全由VPN网关来进行分配,而不是本地设置,这样VPN网关就能区分不同的远程VPN客户的访问,在IKEv2(RFC4306)中已经包括了内部IP分配的内容。

在传输模式下更容易发生问题,对于下面的拓扑结构:

        +----+
        |   |
        +----+ /
          A   /
        10.1.2.3 /
              /
        +----+   / /           +----+
        |   |-----+-----------------|   |
        +----+   / /           +----+
          B   NAT           服务器
        10.1.2.4 /
              /
            /
        +----+ /
        |   |/
        +----+
        C
      10.1.2.5

A和B通过VPN通道和服务器连接,如果都访问服务器上的相同服务,由于传输模式没有原始IP头信息,因此服务器包无法区分包到底是来自A还是B的,因此不能确定使用哪个SA来加密通信;如果增加第3个客户端C,使用的是明文访问服务器,服务器将更抓瞎了。解决方法还是需要用通道模式进行通信。

5. 结论

NAT-T技术解决了IPSec在NAT环境下的使用异常的问题,虽然也可能会引起一些新问题,但都是可以解决的。 
原创粉丝点击