NAT穿透,STUN,TURN与ICE

本文施工状态

未完成

本文是什么

本文为WebRTC系列的文章,主要介绍WebRTC中P2P通信的部分。内容为介绍如何进行NAT穿透,以及所涉及到的STUN和TURN,ICE等方法。其中不会涉及协议具体的实现,仅进行介绍其原理。关于这些协议的具体细节,将在后续的系列文章中进行提及。

  • 本文介绍的方法一般使用UDP协议进行实现。
  • 本文中两个对端之间IP的交换通过公网的信令服务器进行。

NAT

什么是NAT

在进行网络通信的时候,我们用IP:Port来定位到网络中的某个设备或服务。但是在目前大量使用的IPV4协议里,IP是一个32位的地址。随着网络的普及导致越来越多的设备接入到网络中,32位的IP地址已经接近枯竭,这个问题在网络建设较晚的我国则更加严重。在IPV6尚未普及的环境下,网络地址转换(Network Address Translation, NAT)被用来解决地址紧缺的问题。NAT将网络划分为公网与私网,大部分的设备都处于私网之中,并且复用了相同的IP地址。比如设备A的IP是192.168.0.100,设备B的IP也是192.168.0.100,但是设备A和设备B在不同的私网中,所以不会产生IP地址的冲突。因此NAT确实能够缓解IP地址紧缺的问题。

经过NAT发送消息

设备A发送了一个消息给设备B,消息的源私网IP:Port为A:a,发送目的公网IP:Port为 C:c。消息经过NAT后转换为源公网IP:Port (A:a=>B:b),通过网络传输找到了C:c,再经过NAT转换为目的私网IP:Port (C:c=>D:d),最终消息到达了设备B。而设备B如果想要应答这个消息则发送一个目的IP:Port为B:b的消息即可,发送的过程也和A到B类似。这样的过程就像下图一样,各自在NAT上进行打洞,使得两台设备能够直接进行通信。这个打洞的过程就叫做NAT穿透。

有状态防火墙

在介绍NAT穿透之前,我们有必要介绍有状态防火墙。因为现在的NAT不仅在进行网络地址转换,也会基于安全性的考虑使用防火墙过滤一些不符合规则的网络消息,即NAT=网络地址转换+防火墙。我们要进行NAT穿透也必须打通防火墙。

防火墙策略

有状态防火墙有很多种类,具有各种各样的过滤策略,但基本都有一些默认的行为。比如,如果A向B发送了一个消息,防火墙会写下一条A->B的记录。这时候B向A返回的应答消息就可以通过防火墙。这时候,如果C向A发送了消息,防火墙发现没有A->C的记录,则防火墙会过滤这条消息。
总结下来就是:防火墙允许所有的出向消息,允许有记录的入向消息(如果不是这样,就成为了单向通行)。

防火墙打洞

下图里,2.2.2.2的设备想请求7.7.7.7的服务器,防火墙放行该请求消息,并记录2.2.2.2->7.7.7.7,这样就在防火墙上打了个洞,7.7.7.7的应答消息也能经过这个洞到达2.2.2.2。而6.6.6.6的服务器也想发送信息给2.2.2.2,但没有洞给6.6.6.6放行,于是这条消息就被防火墙挡住。

在上面的图中,仅有客户端有防火墙,服务器则没有。因此到达服务器的消息都会被接受。而如果两个设备都具有防火墙,之间如何打通通信呢?首先有一个前提,双方都要向对方发起通信。如果没有这个前提,就必有一端的防火墙没法打洞,无法建立通信。在这个前提下的防火墙打洞如下图。 首先,2.2.2.2向7.7.7.7发送第一个消息,并在2.2.2.2的防火墙上记录2.2.2.2->7.7.7.7,进行打洞。但是这条消息会被7.7.7.7的防火墙挡住,因为此时7.7.7.7的防火墙上并没有7.7.7.7->2.2.2.2的记录。
然后,7.7.7.7向2.2.2.2发送第二个消息,并在7.7.7.7的防火墙上记录7.7.7.7->2.2.2.2,这边的防火墙也完成了打洞。这条消息也会通过2.2.2.2的防火墙的洞到达2.2.2.2。此时,两边的防火墙都打通了。
此时,两边的防火墙都成功打通了。两边能够开始进行通信了。这个过程中,一些信息是没法发送成功的,但是对于UDP来说,丢包是可以接受的。

NAT类型

在介绍NAT穿透前,我们有必要先介绍NAT有哪些类型。总的来说,NAT可以分为锥型(或终点无关映射, Endpoint-Independent Mapping, EIM)和对称型(或终点相关映射, Endpoint-Dependent Mapping, EDM)。
对于锥形NAT,如果A发送消息给B和C,源私网IP:PortA:a都会转换为一样的B:b,B和C收到的IP:Port都是B:b。而对于对称型NAT,A同样发消息给B和C,发向B的IP:Port会从A:a转换为B:b,发向C的IP:Port会从A:a转换为C:c,这样B和C收到的IP:Port都不一样。
在锥形NAT中,又分为完全锥型,受限锥形和端口受限锥形。前面提到NAT=网络地址转换+防火墙。对于完全锥型NAT,防火墙则是一道空气墙。任何NAT外的设备都可以通过IP:Port访问到NAT内的设备。对于受限锥型NAT,防火墙则像上文里提到的防火墙,只允许有IP记录的NAT外设备才能访问对应的NAT内设备。对于端口受限型NAT,则防火墙更加严格,在IP的基础上也对Port进行了限制。
从上面的NAT类型我们可以看到,对于不同的锥型NAT,它们的差距主要在于防火墙的限制条件,而防火墙打洞方法也在上文进行了提及。因此,我们后面关于NAT穿透的方法仅区分锥形NAT和对称型NAT的区别。

NAT穿透难题

两台设备可以通过一台信令服务器来相互交换各自的IP:Port。如果两台设备处于公网之中,经过交换后就可以进行通信了。但如果两台设备位于NAT之后,交换得到的IP:Port是对方的私网IP:Port。如果这两台设备不在同一个私网当中,就会向下图一样,无法建立通信。

STUN

为了解决这个难题,需要设备不仅知道自己的私网IP:Port,也需要知道在经过NAT之后的公网IP:Port。通过信令服务器交换的IP就不是私网IP:Port,而是公网IP:Port。信息发送到对端公网IP:Port之后再经过NAT转换为对端私网IP:Port到达对端设备。STUN(Session Traversal Utilities for NAT)协议被用来协助NAT穿透。
STUN的原理很简单。当NAT之后的设备向STUN服务器发送请求,STUN收到请求的时候能够看到经过NAT转换后公网IP:Port,再把服务器看到的公网IP:Port作为应答的内容返回给NAT之后的设备。这样NAT之后的设备就知道了自己的公网IP:Port。

STUN流程

STUN除了告诉NAT之后设备的公网IP:Port,还能检测设备是否在NAT之后和NAT类型的检测。根据下图进行STUN流程的解释,在这里,需要关注设备向什么IP:Port发送请求,并要求使用什么IP:Port返回应答。

  1. 设备向STUN服务器的5.5.5.5:12345发送UDP请求,并要求使用5.5.5.5:12345返回应答。如果一直没有收到UDP应答,则代表UDP不可用,否则进行下一步检测。
  2. 如果收到了消息,就检查应答中得到的公网IP:Port和自己私网IP:Port是否一致。如果一致则代表该设备不在NAT之后,否则设备在NAT之后并记录这个公网IP:Port。
  3. 在设备不在NAT之后的情况下,再向5.5.5.5:12345发送UDP请求,但要求6.6.6.6:23456返回应答。如果没有收到应答,则设备在对称防火墙之后,否则该设备处于公网当中。
  4. 在设备在NAT之后的情况下,则检测NAT类型。向STUN服务器A的5.5.5.5:12345发送请求,但要求6.6.6.6:23456返回应答。如果能收到应答,则NAT类型为完全锥型,否则进行下一步检测。
  5. 设备向6.6.6.6:23456发送请求,并要求使用6.6.6.6:23456返回应答。收到请求后查看返回的公网IP:Port和之前记录的公网IP:Port是否一致。如果不一致,则NAT类型为对称型,否则进行下一步测试。
  6. 设备向5.5.5.5:12345发送请求,并要求使用5.5.5.5:23456返回请求。如果能收到请求,则NAT类型为受限锥型,否则为端口受限锥型。

STUN局限

对于锥型NAT,STUN服务器看到A的公网IP:Port和B看到A的公网IP:Port是一致的。这样,A在得到从STUN服务器取回的公网IP:Port后分享给B,B也能够使用这个IP:Port向A发送信息。这样,锥型NAT之间的NAT穿透就完成了。但是对于对称型NAT,STUN服务器看到A的公网IP:Port和B看到A的公网IP:Port则是不一致的了,A把STUN服务器返回的IP:Port分享给B,但B却无法使用这个IP:Port来访问A。对于对称型NAT,则会使用下文提到的TURN协议。

TURN

TURN(Traversal Using Relays around NAT)协议的原理也比较简单,就是通过公网的TURN服务器将消息进行中继发给对端。让我们根据下图来了解TURN协议的整个过程。

TURN客户端与TURN服务器建立连接

TURN客户端的IP:Port是198.51.1002.49721,向IP:Port为192.0.2.15:3478TURN发送请求。请求经过NAT后,IP:Port变为了192.0.2.1:7000,这也是TURN服务器看到的请求IP:Port,也叫客户端的反射地址。随后,TURN服务器为TURN客户端分配了一个中继IP:Port 192.0.2.15:50000。在TURN服务器上为客户端提供了两个Port,一个3478用于连接,一个50000用于中继。客户端会将192.0.2.15:50000发送给其他的对端。

TURN客户端与B进行通信

B得到TURN客户端的中继地址。B与TURN服务器处于同一个网络之中,所以B并没有反射地址。

  • TURN客户端=>B:客户端发送消息到192.0.2.15:3478,TURN服务器将该消息转发给50000端口并发出到B的IP:Port 192.0.2.210:49191中。
  • B=>TURN客户端:B将消息发送到192.0.2.15:50000。TURN服务器在5000端口收到消息后转发给3478端口并发到客户端的反射地址192.0.2.1:7000,经过NAT之后转换为198.51.100.2:49721到达客户端。

TURN客户端与A进行通信

A得到TURN客户端的中继地址。但A与TURN服务器不在同一个网络之中,所以A的IP:Port经过NAT之后从203.0.113.2:49582转换为192.0.2.150:32102。TURN服务器也会记录A的反射地址。

  • TURN客户端=>A:客户端发送消息到192.0.2.15:3478,TURN服务器将该消息发给50000端口并发出到A的反射IP:Port 192.0.2.150:32102,再经过NAT之后到达A的IP:Port 203.0.113.2:49582。
  • A=>TURN客户端:与B=>TURN客户端类似。

TURN的缺点

理论来说,无论是锥型NAT还是对称型NAT都可以使用TURN服务来进行中继,从而实现P2P通信。但如果完全依赖于TURN服务,会占有大量的服务器带宽和转发处理,并且这个过程还必不可免地产生而外的网络延迟。所以TURN一般只有在STUN失败后使用。

posted on 2024-02-19 00:31  winterYANGWT  阅读(199)  评论(0编辑  收藏  举报