什么是STUN?
P2P网络要求通信双方都能主动发起访问,但是NAT设备的存在,却阻断了这种主动访问,导致P2P应用无法正常运行。STUN是一种解决P2P应用NAT穿越问题的常用技术。它允许网络设备找出通信端点经NAT设备后的IP地址和端口号,并利用这些信息在通信双方之间建立一条可以穿越NAT设备的数据通道,实现P2P通信。
为什么需要STUN?
随着IPv4地址的枯竭,NAT功能部署越来越广泛。为了避免来自外部网络的攻击,保护网络内部的主机,NAT会过滤掉一些外网主动发送到内网的报文。因此,NAT技术虽然在一定程度上解决了IPv4地址短缺的问题,却破坏了点到点P2P(Point to Point)网络的通信。因为在P2P网络中要求通信双方都能主动发起访问,而NAT设备的存在却阻断了这种主动访问。
为了解决NAT设备给P2P网络带来的问题,出现了一些适用于P2P网络的NAT穿越技术。例如反向链接技术、应用层网关ALG(Application Level Gateway)技术、打洞技术(Hole Punching)、中间件技术等。
NAT会话穿越应用程序STUN(Session Traversal Utilities for NAT)是一种由RFC定义的网络协议,用于检测网络中是否存在NAT设备,并获取两个通信端点经NAT设备分配的IP地址和端口号。然后在两个通信端点之间建立一条可穿越NAT的P2P链接,实现P2P通信,这一过程也被形象的称为“打洞”。STUN无需现有NAT设备做任何改动,只需在组网中部署一台STUN服务器,操作起来比较简单,因此得到广泛应用。
什么是STUN服务器?
STUN采用客户端/服务器通信模式,由STUN服务器(STUN Server)组成和STUN客户端(STUN Client)组成,其中:
- STUN服务器:是一个发送STUN绑定响应和接受STUN绑定请求的实体,例如路由器,STUN服务器通常部署在公网上。
- STUN客户端:是一个发送STUN绑定请求和接受STUN绑定响应的实体,例如路由器。
STUN典型组网
在STUN标准中,根据私网IP地址和端口到NAT出口的公网IP地址和端口的映射方式,把NAT分为完全锥型NAT、限制锥型NAT、端口限制锥型NAT及对称NAT四种类型。为了方便用户理解这4种NAT类型,我们把这4种NAT类型的介绍放在了NAT里,详细信息请参见相关词条NAT。
STUN如何工作?
通过STUN客户端与STUN服务器之间的报文交互,STUN服务器可以发现NAT设备的存在,并获取NAT设备分配给STUN客户端的IP地址和端口号,在STUN客户端之间建立一条数据通道。STUN客户端之间建立好数据通道之后,客户端之间可以相互访问。STUN的报文交互流程包含NAT探测和打洞两个阶段,详细的交互过程如下图所示。
STUN报文交互过程
NAT探测阶段
- STUN客户端向STUN服务器发送STUN绑定请求报文(Binding Request)。
- STUN服务器收到绑定请求报文后,获取请求报文中的源IP地址和源端口,并构建STUN绑定响应报文(Binding Response)发送给客户端。报文中携带MAPPED-ADDRESS、XOR-MAPPED-ADDRESS、RESPONSE-ORIGIN属性。
- STUN客户端收到STUN绑定响应报文后,根据该报文中的MAPPED-ADDRESS或XOR-MAPPED-ADDRESS属性,获取IP地址和端口,与之前发送的STUN绑定请求报文中的源IP地址和源端口比较,如果不一致,则STUN客户端前面存在NAT设备。确定探测结果后,STUN客户端将结果返回给有需要的业务模块。
打洞阶段
打洞是指通过中间设备(如路由反射器)的协助在各自的NAT网关上建立相关表项,使P2P连接双方发送的报文能够直接穿透NAT网关的过程。NAT STUN的打洞流程如下:
- STUN客户端通过BGP从路由反射器处获得其他STUN客户端的接口信息(包括NAT前后的IP地址和端口信息)。当Client1需要与Client2进行通信时,Client1会通过BGP通知Client2,它们之间需要通过打洞建立数据通道。
- Client1与Client2互相发送STUN绑定请求进行打洞。Client1使用本端NAT前的IP地址和端口分别与Client2 NAT前和NAT后的IP地址和端口构建STUN绑定请求报文A和B发送给Client2。同时,Client2也同样进行相应的操作。
- Client2收到报文A和B后,处理流程如下,Client1也类似:
- 对于报文A,若Client1与Client2在同一个私网中,也就是同一个NAT设备后侧,则报文A可以成功发送至Client2,否则报文A被丢弃。
- 对于Client1发出的报文B,经Client1前面的NAT设备1时会生成表项,记录Client1到Client2的会话,但是由于Client2前的NAT设备2上没有相关表项,报文B将会被丢弃。
- 同样的,对于Client2发出的报文B,经Client2前面的NAT设备2上将会生成表项,记录Client2到Client1的会话,但是由于Client1前的NAT设备1上没有相关表项,报文B也会被丢弃。
- Client1和Client2持续向对方发送绑定请求报文,当NAT设备1与NAT设备2上的会话表项都生成后,绑定请求报文就可以成功发送至对端Client。
- Client2收到STUN绑定请求报文后,向Client1发送STUN绑定响应报文,Client1也同样进行相应的操作。
完成以上报文交互后,STUN客户端之间成功建立可穿越NAT的数据通道。
STUN如何在SD-WAN网络中应用?
在SD-WAN解决方案中,为了节省IP地址资源,分支站点的用户经常会使用私网IP地址,通过NAT转换后访问总部。因此,分支的网关CPE经常会和NAT设备一起部署。网关CPE发出的报文在经过NAT设备后,IP地址会发生变化,如果无法获取报文转换后的IP地址,则CPE之间的数据通道会建立失败。因此,为了实现分支之间业务流量穿越NAT,需要在SD-WAN场景中部署NAT STUN功能。
SD-WAN的NAT穿越场景
在SD-WAN 的NAT穿越组网中,路由反射器RR(Route Reflector)及分支CPE通过管理通道,向网络控制器iMaster NCE发起注册信息。注册成功后,RR和CPE被iMaster NCE纳管。CPE通过管理通道,向RR发布站点、路由、隧道、NAT转换前后的IP地址和端口号等信息,统称为TNP(Transport Network Port)。分支CPE1与分支CPE2之间通过数据通道,传输它们之间的业务流量。
由于CPE1和CPE2前面都部署了NAT设备,为了实现CPE1和CPE2之间的业务互通,需要在RR和CPE上部署NAT STUN功能,探测CPE之间是否有NAT设备。并获取CPE1、CPE2设备经NAT转换后的IP地址和端口号,建立数据通道,转发它们之间的业务流量。
分支CPE作为STUN客户端,RR作为STUN服务器,它们之间的交互过程如下:
- 分支CPE向RR发起STUN探测请求,发送STUN绑定请求报文。
- RR给分支CPE回应STUN探测请求,发送STUN绑定响应报文。
- CPE收到RR的STUN绑定响应报文后,判断CPE设备前是否有NAT设备。
- 探测成功后,CPE将自己的TNP信息通过BGP协议发送给RR,并通过RR获取其他CPE的TNP信息。TNP信息里包含了CPE设备经NAT转换前和转换后的IP地址及端口号。
- 获取到TNP信息后,CPE之间使用各自NAT转换后的IP地址和端口号建立数据通道,传输分支之间的业务流量。
STUN Server搭建
Dockerfile
FROM ubuntu:latest
EXPOSE 3478/tcp 3478/udp
USER root
RUN set -ex && \
apt-get update && \
apt-get install -y build-essential && \
apt-get install -y libboost-all-dev && \
apt-get install -y libssl-dev && \
apt-get install -y g++ && \
apt-get install -y make && \
apt-get install -y git && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
RUN cd /opt && git clone https://github.com/jselbie/stunserver.git && cd stunserver && make
WORKDIR /opt/stunserver
HEALTHCHECK CMD /opt/stunserver/stunclient localhost
ENTRYPOINT ["/opt/stunserver/stunserver"]
run.sh
docker image build -t stun-server-image .
docker container run -d -p 3478:3478/tcp -p 3478:3478/udp
--name stun-test stun-server-image