STUN协议
- 网络
- 2024-11-26
- 198热度
- 0评论
NAT墙
完全锥形
IP限制型锥形
端口限制型锥形
对称型
NAT穿透
STUN协议简介
STUN(Session Traversal Utilities for NAT)是一个用于帮助客户端发现其所在NAT之后的公网IP地址和端口号。通信的双方都能知道所在的公网IP,并且能够让公网的NAT帮助转发,从而实现NAT穿透。STUN协议广泛应用在WebRTC实时通信系统中。STUN交互过程主要有以下几个步骤:
- 客户端向 STUN 服务器发送请求。
- STUN 服务器处理并响应。
- 客户端接收响应并解析公共IP和端口。
- 客户端根据收到的公共 IP 和端口进行 NAT 穿越和路径选择。
绑定请求与响应
客户端发STUN请求
客户端发送STUN请求,包的类型为Binding Request。请求的目标是让STUN 服务器返回客户端的公网IP地址和端口。
STUN Message Header:
- Message Type: 0x0001 (Binding Request) //类型
- Message Length: 0x0000
- Magic Cookie: 0x2112A442 //固定值,用于标识STUN
- Transaction ID: 0x63f96d584f90a66d18b68202 //用于唯一标识该请求的ID,匹配响应
STUN Attributes (Optional): //以下是附加信息
- Username: <username>
- Integrity: <HMAC-SHA1 signature>
- Fingerprint: <CRC-32C of the message>
STUN服务端响应
STUN服务器收到客户端的请求后,会放binding response回复,内容中携带了客户端的公网IP和端口号。
STUN Message Header:
- Message Type: 0x0101 (Binding Response) //表明是一个 Binding Response
- Message Length: 0x000C
- Magic Cookie: 0x2112A442
- Transaction ID: 0x63f96d584f90a66d18b68202
STUN Attributes:
- Mapped Address:
Family: IPv4
Address: 203.0.113.1 //公网的IP
Port: 3478 //公网的端口
- Username: <username>
- Integrity: <HMAC-SHA1 signature>
- Fingerprint: <CRC-32C of the message>
连接检查
以webrtc举例,当需要通信的双方,通过信令交互SDP(在libpeer文章中,是通过mqtt)中交互的双方各自的公网IP地址信息后,可以使用STUN的binding request和binding response检查P2P的连通性,以确保P2P链路可达。
首先一般由一端率先发起binding request。
STUN Message Header:
- Message Type: 0x0001 (Binding Request)
- Message Length: 0x0008
- Magic Cookie: 0x2112A442
- Transaction ID: 0x63f96d584f90a32d18b68202
STUN Attributes:
- Username: <peer-username>
- Message Integrity: <HMAC-SHA1 signature>
接着等待对端回复binding response
STUN Message Header:
- Message Type: 0x0101 (Binding Response)
- Message Length: 0x0008
- Magic Cookie: 0x2112A442
- Transaction ID: 0x63f96d584f90a32d18b68202
STUN Attributes:
- Mapped Address:
Family: IPv4
Address: 203.0.113.10
Port: 3479
当发起方接受到binding response后,表明连通性正常,下面是代码的流程示例。
agent_connectivity_check()
{
/*1. 发送 binding request*/
agent_create_binding_request(agent, &msg)
stun_msg_create(msg, STUN_CLASS_REQUEST | STUN_METHOD_BINDING);
stun_msg_write_attr(msg, STUN_ATTR_TYPE_USERNAME, strlen(username), username);
stun_msg_write_attr(msg, STUN_ATTR_TYPE_PRIORITY, 4, (char*)&agent->nominated_pair->priority);
stun_msg_finish(msg, STUN_CREDENTIAL_SHORT_TERM, agent->remote_upwd, strlen(agent->remote_upwd));
agent_socket_send(agent, &agent->nominated_pair->remote->addr, msg.buf, msg.size);//发送,这里目标的ip和端口就是对端的ip地址和端口号。
/*2. 等待接受*/
agent_recv
switch (stun_msg.stunclass) {
/*收到对端发送的binding request,构建一个response发送回去。*/
case STUN_CLASS_REQUEST:
agent_process_stun_request
agent_create_binding_response
stun_set_mapped_address
agent_socket_send
/*收到对端回复的binding response,将state设置为success*/
case STUN_CLASS_RESPONSE:
agent_process_stun_response
agent->nominated_pair->state = ICE_CANDIDATE_STATE_SUCCEEDED;
}
}
实践流程分析
本次实验使用的stun服务器是,有两个设备,设备A是一块开发板,设备B是电脑,分别连接到同一个路由器上。
Resolved stun.l.google.com -> 74.125.250.129,
Resolved stun/turn server 74.125.250.129:19302
设备A(开发板)的IP地址信息是
inet addr:192.168.51.127
设备B(电脑)的IP地址信息是
192.168.51.227
设备发送向STUN服务端发送请求
开发板发送binding request
这里是设备向stun.l.google.com:74.125.250.129:19302服务发送binding request请求。
电脑发送binding request
设备的电脑一下发了3个binding request,其中一个是发送给google stun服务器的,但是另外两个实际上是发给设备A的本地IP和NAT墙的IP,可能是此前连接过,浏览器缓存了信息。
服务端回复
设备A收到回复
stun.l.google.com回复了binding success response。并表明了设备的公网IP为120.236.240.177,其端口为8139。
电脑B收到回复
通过message transaction ID可以看到电脑收到了Google STUN回复的binding success response,告知其NAT地址为120.236.240.177,端口号是8140。因为连接的是同一个路由器,所以NAT是一样的。同时板子A设备192.168.51.127也对齐回复了binding success response,告知其地址为192.168.51.227:59777。
板子A请求检查
板子A发送了一个binding request,对端回复了一个binding success response,则表示联通已建立。
TURN协议
TURN协议是在STUN的基础上增加了中继功能,解决的是对称型NAT无法穿透的问题。如下图的结构体,需要注意的是,使用TURN作为中继转发,传输的设备一方式TURN Client,另一方是Peer A。即TURN Client需要与TURN Server建立TURN协议交互,而Peer A与TURN Server只需要做STUN协议交互。此前调试了两个通信设备,两端都是使用TURN协议发起连接,导致一端checking失败,最后将一端修改为STUN协议交互即可。
Peer A
Server-Reflexive +---------+
Transport Address | |
192.0.2.150:32102 | |
| /| |
TURN | / ^| Peer A |
Client’s Server | / || |
Host Transport Transport | // || |
Address Address | // |+---------+
10.1.1.2:49721 192.0.2.15:3478 |+-+ // Peer A
| | ||N| / Host Transport
| +-+ | ||A|/ Address
| | | | v|T| 192.168.100.2:49582
| | | | /+-+
+---------+| | | |+---------+ / +---------+
| || |N| || | // | |
| TURN |v | | v| TURN |/ | |
| Client |----|A|----------| Server |------------------| Peer B |
| | | |^ | |^ ^| |
| | |T|| | || || |
+---------+ | || +---------+| |+---------+
| || | |
| || | |
+-+| | |
| | |
| | |
Client’s | Peer B
Server-Reflexive Relayed Transport
Transport Address Transport Address Address
192.0.2.1:7000 192.0.2.15:50000 192.0.2.210:49191
Allocate
请求服务器,分配一个中继服务。中继建立连接后,客户端需要进行保活,定期发送refresh request给服务器端。
TURN TURN Peer Peer
client server A B
|-- Allocate request --------------->| | |
| | | |
|<--------------- Allocate failure --| | |
| (401 Unauthorized) | | |
| | | |
|-- Allocate request --------------->| | |
| | | |
|<---------- Allocate success resp --| | |
| (192.0.2.15:50000) | | |
/ / / /
| | | |
|-- Refresh request ---------------->| | |
| | | |
|<----------- Refresh success resp --| | |
| | | |
如上示例,客户端先发送不带验证信息的Allocate请求,此时STUN服务器会返回error response,客户端收到错误后加上验证信息再次请求。以IPC通信为例,摄像头端是Peer A(资源有限,不支持证书请求等协议),发起的是STUN协议,而手机预览端是TURN Client,发起的是TURN交互协议。
Send机制
客户端和peer直接通信可以通过TURN server进行转发信息,主要有两种方式,第一种是使用Send/data方式,第二种是使用channels方式。其主要目的是通过某种方式告知服务器,从client接收到的数据应该发给那个peer。下面是Send/data机制。
TURN Server在进行转发前,需要client先安装一个到对等端的许可(permission),许可证可以通过CreatePermission request/resp来进行交互。
TURN TURN Peer Peer
client server A B
| | | |
|-- CreatePermission req (Peer A) -->| | |
|<-- CreatePermission success resp --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<-------------- Data ind (Peer A) --| | |
| | | |
| | | |
|--- Send ind (Peer B)-------------->| | |
| | dropped | |
| | | |
| |<== data ==================|
| dropped | | |
| | | |
Chanel机制
等同于send/data机制。
TURN TURN Peer Peer
client server A B
| | | |
|-- ChannelBind req ---------------->| | |
| (Peer A to 0x4001) | | |
| | | |
|<---------- ChannelBind succ resp --| | |
| | | |
|-- [0x4001] data ------------------>| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |
|--- Send ind (Peer A)-------------->| | |
| |=== data ===>| |
| | | |
| |<== data ====| |
|<------------------ [0x4001] data --| | |
| | | |
参考: