使用STUN进行NAT行为发现
NAT模式
对NAT行为模式进行分类,经历了几个阶段。
最初,使用术语“完全锥形”、“受限锥形”、“端口限制锥形”和“对称”来表示NAT的不同变体。
完全锥形-Full Cone
限制锥形-Restricted Cone
端口限制锥形-Port Restricted Cone
对称-Symmetric
RFC4787对NAT行为模式进行了细分,定义了单独的NAT行为,主要包括:
NAT映射行为
NAT过滤行为
以及各种特殊场景下的NAT行为:
发夹行为(Hairpinning)
应用级网关(ALG)
ICMP目的不可达处理
分片
NAT映射行为
NAT映射行为的定义:
当内部端点通过NAT打开传出会话时,NAT会为会话分配外部IP地址和端口号,以便NAT可以接收、转换和转发外部端点的后续响应报文。这是内部IP地址与端口IP:port和外部IP:port元组之间的映射。它建立了在会话持续期间NAT将要执行的转换。
简单来说,针对从内部端点发出的报文,经过NAT后,在NAT设备上如何为其分配映射后的IP和端口。
有三种映射行为:
端点无关映射(Endpoint-Independent Mapping)
NAT对从相同的内部IP地址和端口发送到任何外部IP地址和端口的后续报文,都复用之前的端口映射。
该行为对应于旧的行为模式“完全锥形”。
地址相关映射(Address-Dependent Mapping)
NAT对从相同的内部IP地址和端口发送到相同的外部IP地址的后续报文复用端口映射,而不考虑外部端口。当外部IP地址改变时,映射也随之改变。
该行为对应于旧的行为模式“限制锥形”。
地址和端口相关映射(Address and Port-Dependent Mapping)
当映射仍处于活动状态时,NAT会对从相同的内部IP地址和端口发送到相同的外部IP地址和端口的后续报文重用端口映射。当外部IP地址或端口之中的任何一个发生变化,映射也随之改变。
该行为对应于旧的行为模式“端口限制锥形”。
NAT过滤行为
NAT过滤行为的定义:
NAT使用什么标准来过滤来自特定外部端点的报文。
有三种过滤行为:
端点无关过滤(Endpoint-Independent Filtering)
从外部到达的报文,只要其目的IP地址和端口匹配NAT上现有的映射,则都会被NAT转发,无论报文的源IP地址和端口是什么。
换句话说,只要内部端点在NAT上建立了映射,所有外部端点都可以使用该映射给内部端点发送报文。
该行为对应于旧的行为模式“完全锥形”。
地址相关过滤(Address-Dependent Filtering)
从外部到达的报文,只要其目的IP地址和端口匹配NAT上现有的映射,且源IP地址与该映射匹配,则都会被NAT转发,无论报文的端口是什么。
换句话说,内部端点在NAT上建立了映射后,只有特定的外部端点可以使用该映射给内部端点发送报文。
该行为对应于旧的行为模式“限制锥形”。
地址和端口相关过滤(Address and Port-Dependent Filtering)
从外部到达的报文,只有其目的IP地址和端口匹配NAT上现有的映射,且源IP地址和源端口与该映射匹配,才会被NAT转发。
换句话说,内部端点在NAT上建立了映射后,只有特定的外部端点使用特定端口,才可以使用该映射给内部端点发送报文。
该行为对应于旧的行为模式“端口限制锥形”。
新旧两种NAT行为模式的对比:
旧NAT行为模式 |
新NAT行为模式 |
完全锥形 |
端点无关映射+端点无关过滤 |
限制锥形 |
端点无关映射+地址相关过滤 |
端口限制锥形 |
端点无关映射+地址和端口相关过滤 |
对称 |
地址和端口相关映射+地址和端口相关过滤 |
一个思考:
NAT映射行为有3种,NAT过滤行为也有3种,完整组合有9种,上面只列出了4种,剩余5种哪里去了?
首先需要理解一点,映射行为比过滤行为更严格是没有意义的。因为更严格的映射应为需要占用更多的资源,而宽松的过滤行为并没有提升安全性。所以,我们可以排除以下几种组合:
地址相关映射+端点无关过滤
地址和端口相关映射+端点无关过滤
地址和端口相关映射+地址相关过滤
剩余以下两种组合:
地址相关映射+地址相关过滤
地址相关映射+地址和端口相关过滤
上述2种组合,之所以未使用,一个猜测是比上不足,比下有余,处境比较尴尬。一方面增加了资源消耗,另一方面,安全性有所提升,但提升的不够。(一家之言,仅供参考)。
序号 |
组合 |
备注 |
1 |
地址相关映射+端点无关过滤 |
这3种组合意义不大,参见上文讨论。 |
2 |
地址和端口相关映射+端点无关过滤 |
|
3 |
地址和端口相关映射+地址相关过滤 |
|
4 |
地址相关映射+地址相关过滤 |
这两种组合,相比“完全锥形”/“限制锥形”/“端口限制锥形”,增加了资源消耗,相比“对称”/“端口限制锥形”,安全性不足。 |
5 |
地址相关映射+地址和端口相关过滤 |
NAT行为发现
重新定义了NAT行为的情况下,NAT行为发现的流程也需要重新设计。
RFC5780提出了一种使用STUN进行NAT行为发现的方案。该方案和RFC3489中提出的类似,但也有部分区别。该RFC规范是实验性的。
发现过程主要分为三部分:
判断NAT映射行为 — 即判断NAT的映射行为属于“端点无关映射”/“地址相关映射”/“地址和端口相关映射”中的哪一种。
判断NAT过滤行为 — 即判断NAT的映射行为属于“端点无关过滤”/“地址相关过滤”/“地址和端口相关过滤”中的哪一种。
绑定生存期发现 — 即发现NAT设备中映射的生存期。
该方案使用STUN协议,需要一台位于公网且有至少2个IP地址的STUN服务器。
下面简述发现过程。下文中,用到的STUN属性会在文末进行说明。
判断NAT映射行为
这最多需要三次测试。
在测试I中,客户端执行UDP连接测试。服务器将在绑定响应中以OTHER-ADDRESS返回其备用地址和端口。如果未返回OTHER-ADDRESS,则服务器不支持此用法,因此无法运行此测试。客户端检查XOR-MAPPED-ADDRESS属性。如果此地址和端口与用于发送请求的套接字的本地IP地址和端口相同,则客户端知道它不是NAT的,有效的映射将是端点无关的。
在测试II中,客户端向备用地址和主用端口发送绑定请求。如果绑定响应中的XOR-MAPPED-ADDRESS与测试I相同,则NAT当前具有端点无关映射。如果不同,则执行测试III:客户端向备用地址和端口发送绑定请求。如果XOR-MAPPED-ADDRESS与测试II匹配,则NAT当前具有地址相关映射;如果不匹配,则当前具有地址和端口相关映射。
判断NAT过滤行为
这也将需要最多三次测试。
在测试I中,客户端执行UDP连通性测试。服务器将在绑定响应中以OTHER-ADDRESS返回其备用地址和端口。如果未返回OTHER-ADDRESS,则服务器不支持此用法,因此无法运行此测试。
在测试II中,客户端向服务器的主用地址发送绑定请求,并将CHANGE-REQUEST属性设置为更改端口和更改IP。这将导致服务器从其备用IP地址和备用端口发送响应。如果客户端收到响应,则NAT的当前行为为端点无关过滤。
如果没有收到响应,必须执行测试III以区分地址相关过滤/地址和端口相关过滤。在测试III中,客户端向原始服务器地址发送绑定请求,并将CHANGE-REQUEST设置为更改端口。如果客户端接收到响应,则当前行为为地址相关过滤;如果没有收到响应,则当前行为为地址和端口相关过滤。
绑定(映射)生存期发现
为了确定绑定生存期,客户端首先从特定的源端口X向服务器发送绑定请求。这将在NAT中创建绑定。来自服务器的响应包含MAPPED-ADDRESS属性,提供NAT上的公共地址和端口。分别称为Pa和Pp。然后,客户端启动一个值为T秒的计时器。当此计时器触发时,客户端使用相同的目的地址和端口,但从不同的源端口Y,向服务器发送另一个绑定请求。此请求包含一个设置为Pp的RESPONSE-PORT属性,用于请求将响应传递给(Pa, Pp)。这将在NAT上创建一个新的绑定,并使STUN服务器发送一个绑定响应,如果旧绑定(Pa, Pp)仍然存在,则与之匹配。如果客户端在端口X上收到绑定响应,则它知道绑定尚未过期。如果客户端在端口Y上收到绑定响应(如果旧绑定已过期,并且NAT为新绑定分配了相同的公共地址和端口,则可能会收到绑定响应),或者根本没有收到响应,则它知道绑定已过期。
由于某些NAT仅在发送出站流量时刷新绑定,因此客户端必须在使用不同的T值开始第二次测试之前,从原始源端口重新发送绑定请求。客户端可以通过对T进行二分查找来找到绑定生存期的值,最终得出一个值,即任何大于T的计时器都没有收到响应,但任何小于T的计时器收到响应。
用到的STUN属性
CHANGE-REQUEST属性
CHANGE-REQUEST属性包含两个标志,用于控制服务器用来发送响应的IP地址和端口。这些标志被称为“更改IP”和“更改端口”标志。仅在绑定请求中允许使用CHANGE-REQUEST属性。“更改IP”和“更改端口”标志可用于确定NAT的当前过滤行为。它们指示服务器从备用源IP地址和/或备用端口发送绑定响应。CHANGE-REQUEST属性在绑定请求中是可选的。
OTHER-ADDRESS属性
OTHER-ADDRESS属性被用于绑定响应中。如果客户端请求“更改IP”和“更改端口”行为,它通知客户端将要使用的源IP地址和端口。除非服务器有第二个IP地址,否则不能将OTHER-ADDRESS属性插入到绑定响应中。
OTHER-ADDRESS使用和RFC3489 [RFC3489]中CHANGED-ADDRESS相同的属性编号,因为它和CHANGED-ADDRESS语义相同,只是一个新名称。对其进行重命名以更清楚的指示它的功能。
RESPONSE-PORT属性
RESPONSE-PORT属性包含一个端口。RESPONSE-PORT属性可以出现在绑定请求中,并指示绑定响应将发送到哪个端口。对于支持RESPONSE-PORT属性的服务器,绑定响应必须传输到绑定请求的源IP地址和RESPONSE-PORT中包含的端口。当不存在此属性时,服务器将绑定响应发送到绑定请求的源IP地址和端口。绑定请求中的RESPONSE-PORT属性是可选的。RESPONSE-PORT的服务器支持是可选的。
MAPPED-ADDRESS属性
MAPPED-ADDRESS属性指示客户端的反射传输地址。这是服务器看到的绑定请求的源IP和源端口。也是NAT设备为客户端分配的映射。
XOR-MAPPED-ADDRESS属性
XOR-MAPPED-ADDRESS属性与MAPPED-ADRESS属性相同,不同之处在于反射传输地址通过XOR函数进行模糊处理。
注意:XOR-MAPPED-ADDRESS和MAPPED-ADRESS仅在传输地址的编码方面不同。前者通过将传输地址与Magic cookie进行异或来对其进行编码。后者直接用二进制编码。RFC3489最初只指定了MAPPED-ADDRESS。然而,部署经验发现,一些NAT重写了包含NAT公共IP地址的32位二进制负载,例如STUN的MAPPED-ADDRESS属性,这是出于善意但被误导的尝试,目的是提供通用的应用级网关(ALG)功能。这种行为会干扰STUN的操作,也会导致STUN的消息完整性检查失败。
后记
本文是阅读了RFC4787和RFC5780等相关标准文档后写作的。部分内容就来自这两篇标准文档。
在目前的互联网中,NAT随处可见。常见的家庭宽带,入户的光猫或者自购的路由器,默认情况下都会做NAT。
NAT的引入最初是为了解决IPv4地址资源不足的问题。这只是一个临时解决方案,互联网应该尽快过渡到IPv6,以彻底解决该问题。不幸的是,IPv4到IPv6的过渡进展缓慢。目前我们不得不继续使用NAT。
由于NAT的普遍性,导致NAT的变种有很多。目前最常见的就是对称NAT。文档中提到的各种锥形NAT,仅是理论上的分类,笔者在实际中从未遇到。各种类型的NAT设备,其实现机制各不相同,导致NAT行为发现很难适配所有场景。这也许是RFC5780的状态是实验性的原因吧。