STUN技术

RFC定义

旧标准:
https://datatracker.ietf.org/doc/rfc3489/
RFC 3489 STUN协议设计,2003年发布
https://datatracker.ietf.org/doc/rfc5389/
RFC 5389 STUN工程实现和最佳实践,并对RFC3489进行了更新,2008年发布
新标准:
https://datatracker.ietf.org/doc/rfc5780/
RFC 5780, STUN协议设计,2010年发布
https://datatracker.ietf.org/doc/rfc8489/
RFC 8489 STUN工程实现和最佳实践,2020年发布

STUN的主要作用有两个:
1. 获取通信双方的公网IP地址和端口
2. 检测NAT类型(用于确定后续穿透方案)

RFC 3489定义的四种NAT类型

  1. Full Cone NAT

    内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,任何外部主机只要知道这个(PublicIP:PublicPort)就可以发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包

  2. Restricted Cone NAT

    内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机IP发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,任何端口)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包

  3. Port Restricted Cone NAT

    内网主机建立一个UDP socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机(IP,Port)发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,Port)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包

  4. Symmetric NAT

    内网主机建立一个UDP socket(LocalIP,LocalPort),当用这个socket第一次发数据给外部主机1时,NAT为其映射一个(PublicIP-1,Port-1),以后内网主机发送给外部主机1的所有数据都是用这个(PublicIP-1,Port-1); 如果内网主机同时用这个socket给外部主机2发送数据,第一次发送时,NAT会为其分配一个(PublicIP-2,Port-2), 以后内网主机发送给外部主机2的所有数据都是用这个(PublicIP-2,Port-2).

RFC 5780定义的九种NAT类型

这九种包含了RFC 3489中的四种类型
RFC 5780中分为了映射类型和过滤类型

  • NAT映射类型

    如图1所示,假设一个内网主机HostX的内网IP地址为X,端口号为x,经NAT映射后的外网IP地址为M,端口号为m。为方便描述,将内网的Endpoint记为Endpoint(X,x),映射后外网的Endpoint记为Endpoint(M,m)。内网Endpoint(X,x)发往外网HostD1的IP地址和端口号记为目的Endpoint(D1,d1);发往外网HostD2的IP地址和端口号记为目的Endpoint(D2,d2)。

    1. EIM(Endpoint-Independent Mapping)外部地址无关映射:对于一个内网Endpoint(X,x),其映射的外网Endpoint(M,m)是固定的。即从相同的Endpoint(X,x)发送到任何外部IP地址和任何外部端口的报文在NAT设备上使用相同的映射。
    2. ADM(Address-Dependent Mapping)外部地址相关映射
      于一个内网Endpoint(X,x),发往目的Endpoint(D1,d1)的报文,Endpoint(X,x)被映射成Endpoint(M1,m1);发往目的Endpoint(D2,d2)的报文,Endpoint(X,x)被映射成Endpoint(M2,m2)。只要D1=D2,不管d1和d2是多少,都有Endpoint(M1,m1)=Endpoint(M2,m2)。即从相同的Endpoint(X,x)发送到相同外部IP地址和任何外部端口的报文在NAT设备上使用相同的映射。
    3. APDM(Address and Port-Dependent Mapping)外部地址和端口相关映射
      对于一个内网Endpoint(X,x),发往目的Endpoint(D1,d1)的报文,Endpoint(X,x)被映射成Endpoint(M1,m1);发往目的Endpoint(D2,d2)的报文,Endpoint(X,x)被映射成Endpoint(M2,m2)。只有当D1=D2,且d1=d2,才有Endpoint(M1,m1)=Endpoint(M2,m2)。即从相同的Endpoint(X,x)发送到相同外部IP地址和相同外部端口的报文在NAT设备上使用相同的映射。

    图1 NAT映射类型

  • NAT过滤类型

    如图2所示,假设一个内网主机HostX的内网IP地址为X,端口号为x,经NAT映射后的外网IP地址为M,端口号为m。为方便描述,将内网的Endpoint记为Endpoint(X,x),映射后外网的Endpoint记为Endpoint(M,m)。内网Endpoint(X,x)发往外网HostD1的IP地址和端口号记为目的Endpoint(D1,d1);发往外网HostD2的IP地址和端口号记为目的Endpoint(D2,d2)。
    图2 NAT过滤类型

    1. EIF(Endpoint-Independent Filtering)外部地址无关过滤
      对于一个内网Endpoint(X,x),只要它曾经向外网发送过数据,外网主机就可以获取到它经NAT映射后的外网Endpoint(M,m)。那么只要是发给Endpoint(M,m)的报文,不管来源于D1还是D2,都能被转换并发往内网,其他报文被过滤掉。
    2. ADF(Address-Dependent Filtering)外部地址相关过滤
      对于一个内网Endpoint(X,x),只有它曾经向IP地址为D1的外网主机发送过报文,那么来自外网HostD1返回的任何端口的报文,都能被转换并发往内网,其他报文被过滤掉。
    3. APDF(Address and Port-Dependent Filtering)外部地址和端口相关过滤
      对于一个内网Endpoint(X,x),只有它曾经向IP地址为D1,端口号为d1的外网目的Endpoint(D1,d1)发送过报文,那么也只有外网HostD1中来自Endpoint(D1,d1)返回的报文,才能被转换并发往内网,其他报文被过滤掉。

把映射类型和过滤类型组合起来就是9种NAT类型,分别对应如下:

以上只是RFC的分类方法,不同厂家设备实现细节上可能还有不一样。

全椎型是打通速度最快的,受限锥型可能会延个几秒(防火墙需要等互相收到对方发过来的包才能建立会话),对称型虽然不能直接打通,但是猜测对方端口在一个范围内进行扫描也是可以的,大部分防火墙来说NAT端口分配都是连续的,下一个连接总是在上一个连接的端口号上加一,如果一次没猜中多猜几次就好了,再加上请求的时候 可以一次性打开多个端口,这样更容易猜中。

STUN服务器实现

https://github.com/coturn/coturn
支持STUN,TURN,ICE,但是对STUN的支持并不完整,不支持探测NAT类型
https://github.com/jselbie/stunserver
仅支持STUN,但完整实现了RFC的所有特性,但是要开启所有特性需要服务器有两个公网IP

STUN客户端

https://github.com/HMBSbige/NatTypeTester/tree/master
另外stunserver中自带的也有个客户端

关于多出口

如果网络是多出口的对NAT类型探测和穿透还是有很大影响的,在地址受限测试中客户端访问不同的STUN Server IP如果请求从不同的出口出去的话会导致IP和端口都发生变化,这样就成了端口地址受限NAT。在后面的穿透工作中如果访问NAT Server和实际通信用的不是一个出口(IP),就会出现通信振荡不稳定。所以对于多出口的环境最后能绑定线路,或者把有多出口的一端当成对称型NAT来用,由另一端进行穿透。这种情况在一端进行穿透反而比两端同时进行穿透要稳定。如果STUN能增加多线路的支持就更好了,还能利用多个线路同时传输数据。

参考:

https://arthurchiao.art/blog/how-nat-traversal-works-zh/#41-stun-%E5%8E%9F%E7%90%86
https://blog.csdn.net/wh445306/article/details/127831832
https://blog.csdn.net/momo0853/article/details/105387675/
https://zhaoyanbai.com/articles/RFC8489_STUN_zh_CN
https://gist.github.com/mondain/b0ec1cf5f60ae726202e

发表回复