IPsec与NAT Traversal(NAT-T)

码域星辰
• 阅读 6883

IPsec与NAT Traversal(NAT-T)

背景

IPsec在两个通信实体之间建立安全的数据传输通道, 但它却与网络中广泛存在的NAT设备(以及PAT)有天生的不兼容性(incompatible)。

我们以一个TCP报文为例来看看在不同IPsec的不同模式(TransportTunnel)和协议(AHESP)下,这种不兼容是如何发生的。

先来看Transport模式

IPsec与NAT Traversal(NAT-T)

AH协议,由于其Authenticate范围是整个IP报文,所以如果两个IPsec之间存在NAT设备,修改了报文IP Header中的地址,就会导致接收方的Authenticate失败。
ESP协议,其Authenticate返回不包括IP Header,所以接收方的Authenticate会通过,但如果中间的NAT设备修改了IP Header中的地址,理论上后面的TCP checksum也会随之修改,但这部分在ESP协议中是
加密的,NAT设备没有办法修改,所以接收端在TCP接收时会出现checksum校验失败。

再来看Tunnel模式

IPsec与NAT Traversal(NAT-T)

AH协议, Tunnel模式和Transport模式没什么不同,Authenticate范围包含了外层IP Header,因此同样会造成接收方Authenticate失败。
ESP协议,与Transport模式不同的是,经过NAT设备。内层IP Header并不会改变,所以TCP checksum也不会变化,接收方不会出现checksum校验失败。

这样看起来,ESP-Tunnel似乎成为了在有NAT设备环境下,唯一可行的协议-模式组合。但即使是这种组合也是有缺点的:它只能支持一对一NAT(NAT设备后面只有一台内网主机)。在很多组网中,NAT设备通常作为网关使用,其背后可能有很多台主机。这时地址转换就不够了,它还需要端口转换,显然,NAT设备对ESP-Tunnel的报文是无能为力的,因为TCP部分已经被加密了,已经没有端口字段了。
所以,IPsec需要想办法能绕开NAT设备的影响,也就是进行NAT穿越(NAT-Tranversal)。

UDP-Encapsulate

IPsec采用的办法是在ESP Header前加上一个UDP Header, 这个方法同时适用于ESP-TransportESP-Tunnel模式。
下图展示了ESP-Transport下的UDP-Encapsulate过程。

IPsec与NAT Traversal(NAT-T)

UDP Header是有端口字段的,有了端口,NAT设备便可以进行端口转换。RFC3948中规定UDP Header中的端口要使用和IKE协商时相同的端口号,这个端口号在RFC3947中规定为4500.

在下面这样的拓扑中,NAT设备背后有两台内网主机,它们都与Server建立IPsec连接。

IPsec与NAT Traversal(NAT-T)

Host 1Host 2发出的IPsec报文都附加了一个UDP HeaderNAT网关替换该报文的Source IPSource Port

还有一个问题, 对于ESP-Transport模式, 内层TCP报文的checksum校验的问题如何解决呢?要知道,经过NAT设备之后,报文的IP地址发生了变化,这势必导致接收端校验失败。IPsec采用的方法是在IKE协商时,就将自己原始IP地址信息发给对端,这样Server在解密出TCP报文后,可以根据这个信息修正checksum

NAT-T 协商过程

IPsec的通信实体之间需要在IKE时完成协商才能使用上面UDP-Encapsulate,完成NAT-T

IKEPHASE1

  • 双方探测出双方都支持NAT-T
  • 双方探测出了报文传输路径上存在NAT设备

IKEPHASE2

  • 双方协商NAT-T的封装模式,UDP-Encapsulated-Tunnel还是UDP-Encapsulated-Transport
  • (UDP-Encapsulated-Transport)模式向对方发送自己的原始IP地址, 让对方可以据此修正后续TCP报文的checksum

为了更好的说明我用虚拟机搭建了下面这个拓扑,用来展示IPsecNAT-T协商过程

IPsec与NAT Traversal(NAT-T)

其中AliceCarol上运行Strongswan, 而Moon作为NAT设备。配置IPsecTransport模式,使用IKEv1进行协商

探测支持 NAT-T

IPsec的两端在PHASE1的消息1和消息2中会通过交换vendor ID payload来向对方通告自己支持NAT, 其内容正是字符串"rfc3947"

IPsec与NAT Traversal(NAT-T)

探测是否存在 NAT

IKE PHASE1的消息3和消息4,通信双方会交换自己的和自己眼中对方的IPPort的哈希值,如果中间存在NAT设备,则该值一定与该报文本身的IPPort计算出的值不一致。

IPsec与NAT Traversal(NAT-T)

改变端口从500到4500

IKE PHASE1的前4个消息都是使用Sport=Dport=500进行通信。但当探测到NAT设备存在时,作为InitiatorAlice就再消息5需要将端口切换到Sport=Dport=4500, 作为ResponderCarol在收到该消息后,如果解密成功,也会使用新的4500端口

IPsec与NAT Traversal(NAT-T)

在此之后,后续的IKE PHASE2和业务流量都会使用4500端口进行UDP-Encapsulate。为了与业务流量进行区分,IKE阶段的流量紧随UDP Header后的是一个32bit全为0的Non-ESP Marker (业务流量的这个地方是填写的是非零的SPI)

IPsec与NAT Traversal(NAT-T)

内核相关实现

内核使用xfrm框架完成IPsec报文收发功能。普通情况下, IP根据协议字段分流IPsec报文和TCP UDP报文。

IPsec与NAT Traversal(NAT-T)

NAT-T场景中,IPsec为报文进行了UDP-Encapsulate,那么,接收端看到的就是一个UDP报文了,会调用udp_rcv()进行报文接收。那么此时又如何进入xfrm框架呢?

答案是:Strongswan通过设置UDP套接字UDP_ENCAP选项,内核为套接字绑定一个回调函数xfrm4_udp_encap_rcv()

int udp_lib_setsockopt(struct sock *sk, int level, int optname,
               char __user *optval, unsigned int optlen,
               int (*push_pending_frames)(struct sock *))
{
    // code omitted
    switch (optname) {
        case UDP_ENCAP:
        switch (val) {
            case 0:
            case UDP_ENCAP_ESPINUDP:
            case UDP_ENCAP_ESPINUDP_NON_IKE:
                up->encap_rcv = xfrm4_udp_encap_rcv;
        // code omitted
    }
}

而在udp_rcv()接收过程中,最终会调用到该回调函数

IPsec与NAT Traversal(NAT-T)

REF

RFC 3947 Negotiation of NAT-Traversal in the IKE
RFC 3948 UDP Encapsulation of IPsec ESP Packets

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Amazon VPC 常见问答2
可以使用哪种客户网关设备连接AmazonVPC?您可以创建的VPN连接有两种:静态路由VPN连接,以及动态路由VPN连接。支持静态路由VPN连接的客户网关设备必须能够:使用预共享密钥建立IKE安全关联以隧道模式建立IPsec安全关联利用AES128位加密功能
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这