26 KiB
SDLAN 协议说明
本文档描述 Client 端和当前服务端的交互协议。当前控制面支持两种接入方式:
- QUIC 接入:实现位于
src/quic,主入口为sdlan_quic_server和sdlan_quic_transport。 - SSL/TLS 接入:实现位于
src/ssl,主入口为sdlan_ssl_server和sdlan_ssl_transport。
两种接入方式在传输层不同,但进入 sdlan_session 后使用同一套应用层包类型和 protobuf 消息。Client 完成控制面注册后,还需要通过 UDP STUN 服务上报 NAT 映射并承载节点之间的数据转发。
1. 加密说明
服务端在网络启动时为每个网络生成独立密钥,Client 通过 RegisterSuper 响应拿到当前网络的加密参数。
algorithm:
aes AES-256
chacha20 ChaCha20
key:
长度 32 字节。
RegisterSuperAck.key 会使用客户端 RegisterSuper.pub_key 中的 RSA 公钥加密。
AES 参数:
blockMode: cbc
padding: pkcs7Padding
iv: key 的前 16 字节
ChaCha20 参数:
key: RegisterSuperAck.key 解密后的 32 字节密钥
region_id: RegisterSuperAck.region_id,用于客户端侧逻辑分区/nonce 生成
SDLData.data 字段承载隧道中的原始二层/三层数据,Client 需要按 RegisterSuperAck.algorithm 和 key 做加解密;包头中的 network_id、src_mac、dst_mac、ttl、session_token、identity_id 不参与该字段加密。
2. 通用应用层帧
QUIC 和 SSL/TLS 接入最终交给 sdlan_session:handle_frame/2 的应用层帧格式一致:
Frame = <<PacketType:8, ProtobufPayload/binary>>
PacketType:1 字节包类型,取值见第 3 节。ProtobufPayload:protobuf 编码后的消息体。没有消息体的包只包含PacketType。- Event 和 Command 当前已经改为 protobuf
oneof结构,不再使用旧文档中的二级编码字节。
3. 包类型定义
包类型以 include/sdlan.hrl 为准。
| 宏 | 值 | 方向 | 消息 | 说明 |
|---|---|---|---|---|
PACKET_EMPTY |
0x00 |
双向 | 无 | 空包,当前主流程未使用。 |
PACKET_REGISTER_SUPER |
0x01 |
Client -> Server | SDLRegisterSuper |
控制连接注册,必须在收到 Welcome 后发送。 |
PACKET_REGISTER_SUPER_ACK |
0x02 |
Server -> Client | SDLRegisterSuperAck |
注册成功,返回网络加密参数和 session_token。 |
PACKET_REGISTER_SUPER_NAK |
0x04 |
Server -> Client | SDLRegisterSuperNak |
注册失败,返回错误码和错误消息,随后连接关闭。 |
PACKET_UNREGISTER |
0x05 |
Client -> Server | 无 | 主动注销当前控制连接,服务端清理网络绑定后关闭连接。 |
PACKET_QUERY_INFO |
0x06 |
Client -> Server | SDLQueryInfo |
查询目标 MAC 的 NAT/IPv6 信息,用于发起 P2P 打洞。 |
PACKET_PEER_INFO |
0x07 |
Server -> Client | SDLPeerInfo |
QUERY_INFO 的响应。找不到目标时返回空 v4_info/v6_info。 |
PACKET_PING |
0x08 |
Client -> Server | 无 | 控制连接心跳。 |
PACKET_PONG |
0x09 |
Server -> Client | 无 | 心跳响应。 |
PACKET_EVENT |
0x10 |
Server -> Client | SDLEvent |
服务端主动事件推送,Client 不需要 Ack。 |
PACKET_COMMAND |
0x11 |
Server -> Client | SDLCommand |
服务端主动命令推送,需要 Client 回 COMMAND_ACK。 |
PACKET_COMMAND_ACK |
0x12 |
Client -> Server | SDLCommandAck |
命令处理结果,pkt_id 必须等于 SDLCommand.pkt_id。 |
PACKET_FLOW_TRACER |
0x15 |
Client -> Server | 历史保留 | sdlan_session 当前未处理该包。 |
PACKET_REGISTER |
0x20 |
Client <-> Client | SDLRegister |
节点之间 UDP 打洞握手请求。 |
PACKET_REGISTER_ACK |
0x21 |
Client <-> Client | SDLRegisterAck |
节点之间 UDP 打洞握手响应。 |
PACKET_STUN_REQUEST |
0x30 |
Client -> STUN | SDLStunRequest |
周期上报 NAT 映射和 IPv6 信息,同时维持 NAT 映射。 |
PACKET_STUN_REPLY |
0x31 |
STUN -> Client | SDLStunReply |
STUN 心跳响应。 |
PACKET_STUN_PROBE |
0x32 |
Client -> STUN | SDLStunProbe |
NAT 类型探测请求。 |
PACKET_STUN_PROBE_REPLY |
0x33 |
STUN -> Client | SDLStunProbeReply |
NAT 类型探测响应,返回服务端看到的公网 IP/端口。 |
PACKET_STUN_PROBE_RELAY |
0x3a |
STUN 内部 | 内部二进制 | STUN 辅助节点内部转发探测响应,Client 不直接使用。 |
PACKET_WELCOME |
0x4f |
Server -> Client | SDLWelcome |
控制连接建立后服务端首先下发的欢迎包。 |
PACKET_ARP_REQUEST |
0x50 |
Client -> Server | SDLArpRequest |
查询虚拟网络 IP 对应的 MAC。 |
PACKET_ARP_RESPONSE |
0x51 |
Server -> Client | SDLArpResponse |
ARP 查询响应。 |
PACKET_POLICY_REQUEST |
0xb0 |
Client -> Server | SDLPolicyRequest |
查询源身份到目标身份的访问规则。 |
PACKET_POLICY_REPLY |
0xb1 |
Server -> Client | SDLPolicyResponse |
权限规则响应。 |
PACKET_EXPOSED_SERVICE_REQUEST |
0xb2 |
Client -> Server | SDLExposedServiceRequest |
查询当前节点暴露服务端口列表。 |
PACKET_EXPOSED_SERVICE_RESPONSE |
0xb3 |
Server -> Client | SDLExposedServiceResponse |
暴露服务端口响应。 |
PACKET_STUN_DATA |
0xff |
Client <-> STUN/Client | SDLData |
UDP 数据包,支持服务端转发和 P2P 直连。 |
STUN 探测属性同样定义在 include/sdlan.hrl:
STUN_ATTR_CHANGE_NONE = 0 使用收到请求的同一个 socket 响应
STUN_ATTR_CHANGE_PORT = 1 通过 stun_peer_assist 路径转发响应,通常用于测试变化 IP/端口路径
STUN_ATTR_CHANGE_PEER = 2 通过 stun_port_assist 路径响应,通常用于测试变化端口路径
4. Protobuf 消息
protobuf 定义以 proto/sdlan.proto 为准,Erlang 生成文件为 src/sdlan_pb.erl 和 include/sdlan_pb.hrl。
4.1 公共类型
message SDLV4Info {
uint32 port = 1;
bytes v4 = 2;
uint32 nat_type = 3;
}
message SDLV6Info {
uint32 port = 1;
bytes v6 = 2;
}
SDLV4Info.port:服务端观察到的客户端 NAT 端口。SDLV4Info.v4:4 字节 IPv4 地址,网络字节序。SDLV4Info.nat_type:Client 上报的 NAT 类型。SDLV6Info.port:IPv6 辅助通道端口。SDLV6Info.v6:16 字节 IPv6 地址。
4.2 NAT 类型约定
0 blocked 网络不可达
1 noNat 当前设备在公网地址下
2 fullCone Full Cone NAT
3 portRestricted Port Restricted Cone NAT
4 coneRestricted Restricted Cone NAT
5 symmetric Symmetric NAT
5. QUIC 接入
QUIC 接入代码位于 src/quic。服务端监听配置来自 quic_server:
- 默认端口:
443 - ALPN:
punchnet/1.0 - 服务端允许 1 条双向 stream:
peer_bidi_stream_count => 1 - 默认最大应用包:
max_packet_size = 16384 - 默认心跳间隔:
heartbeat_sec = 15
5.1 QUIC 传输帧
QUIC 在单条双向 stream 上承载控制面消息。stream 上的实际传输格式为:
QuicStreamData = <<Len:16, Frame:Len/binary>>
Frame = <<PacketType:8, ProtobufPayload/binary>>
Len:2 字节无符号长度,表示后续Frame字节数,不包含Len自身。Frame:第 2 节定义的通用应用层帧。- 服务端接收时支持一次 QUIC data 中包含多个完整 frame,也支持半包累积。
- 当
Len > max_packet_size时,服务端关闭连接,原因是frame_too_large。
5.2 QUIC 连接建立流程
- Client 与服务端完成 QUIC/TLS 握手,ALPN 必须匹配
punchnet/1.0。 - Client 打开一条双向 stream。
- 服务端接受第一条 stream 后立即发送
PACKET_WELCOME。 - Client 收到 Welcome 后发送
PACKET_REGISTER_SUPER。 - 注册成功后服务端返回
PACKET_REGISTER_SUPER_ACK并进入registered状态。 - 注册失败时服务端返回
PACKET_REGISTER_SUPER_NAK,随后关闭连接。 - 进入
registered后,Client 必须按 Welcome 中的heartbeat_sec周期发送PACKET_PING。
5.3 QUIC Welcome
Server -> Client
<<Len:16, 0x4f:8, SDLWelcome/binary>>
message SDLWelcome {
uint32 version = 1;
uint32 max_bidi_streams = 2;
uint32 max_packet_size = 3;
uint32 heartbeat_sec = 4;
SDLV6Info ipv6_assist = 5;
}
version:协议版本,当前为1。max_bidi_streams:服务端允许的双向 stream 数,当前为1。max_packet_size:单个应用层 frame 最大长度。heartbeat_sec:心跳间隔,Client 应以小于或等于该值的周期发送 Ping。ipv6_assist:IPv6 辅助器地址。未配置时为空。
5.4 QUIC RegisterSuper
Client -> Server
<<Len:16, 0x01:8, SDLRegisterSuper/binary>>
message SDLRegisterSuper {
string client_id = 1;
uint32 network_id = 2;
bytes mac = 3;
uint32 ip = 4;
uint32 mask_len = 5;
string hostname = 6;
string pub_key = 7;
string access_token = 8;
}
client_id:客户端节点 ID,不能为空。network_id:客户端要加入的网络 ID。mac:虚拟网卡 MAC,不能为空,且不能是广播或组播 MAC。ip:HTTP 接口已分配的虚拟网络 IPv4,按 32 位整数传输。mask_len:虚拟网络掩码长度。hostname:客户端主机名,服务端用于维护域名映射。pub_key:客户端 RSA 公钥 PEM,服务端用它加密网络密钥。access_token:客户端通过 HTTP 登录或令牌换取的访问凭证,服务端注册时调用 API 校验。
注册成功:
Server -> Client
<<Len:16, 0x02:8, SDLRegisterSuperAck/binary>>
message SDLRegisterSuperAck {
string algorithm = 1;
bytes key = 2;
uint32 region_id = 3;
bytes session_token = 4;
}
algorithm:当前网络加密算法,取值为aes或chacha20。key:使用pub_key加密后的网络密钥,Client 需要用私钥解密。region_id:服务端根据虚拟 IP 生成的逻辑分区 ID。session_token:本次控制会话 token。后续 UDPSDLStunRequest和SDLData必须携带,用于校验端和网络绑定关系。
注册失败:
Server -> Client
<<Len:16, 0x04:8, SDLRegisterSuperNak/binary>>
message SDLRegisterSuperNak {
uint32 error_code = 1;
string error_message = 2;
}
error_code:错误码。网络/API 错误当前使用4,服务内部错误当前使用5,API 返回业务错误时透传 API code。error_message:错误描述。服务端发送 NAK 后关闭连接。
5.5 QUIC 心跳
Client -> Server
<<Len:16, 0x08:8>>
Server -> Client
<<Len:16, 0x09:8>>
- Client 只在注册成功后发送 Ping。
- 服务端收到 Ping 后立即回复 Pong,并记录本轮心跳已收到。
- 服务端按
heartbeat_sec检查上一周期是否收到过 Ping;如果没有收到,关闭连接。
5.6 QUIC 查询 PeerInfo
Client -> Server
<<Len:16, 0x06:8, SDLQueryInfo/binary>>
message SDLQueryInfo {
bytes dst_mac = 1;
}
dst_mac:目标节点 MAC。- 服务端查找目标节点的 NAT 映射和 IPv6 信息。
- 如果目标存在,服务端同时会向目标节点推送
SDLEvent.SendRegister,让目标主动向查询方发送打洞包,提高 P2P 成功率。
响应:
Server -> Client
<<Len:16, 0x07:8, SDLPeerInfo/binary>>
message SDLPeerInfo {
bytes dst_mac = 1;
optional SDLV4Info v4_info = 2;
optional SDLV6Info v6_info = 3;
}
dst_mac:原样返回查询的目标 MAC。v4_info:目标节点最近一次STUN_REQUEST上报形成的公网 IPv4/端口和 NAT 类型。v6_info:目标节点上报的 IPv6 辅助信息。没有时为空。- 找不到目标或目标没有可用 NAT 信息时,服务端仍返回
SDLPeerInfo,但v4_info和v6_info为空。
5.7 QUIC ARP 查询
Client -> Server
<<Len:16, 0x50:8, SDLArpRequest/binary>>
message SDLArpRequest {
uint32 target_ip = 1;
uint32 origin_ip = 2;
bytes context = 3;
}
target_ip:要查询 MAC 的虚拟网络 IPv4。origin_ip:发起查询的虚拟网络 IPv4。context:Client 自定义上下文,服务端原样回写,方便客户端匹配本地 ARP 请求。
响应:
Server -> Client
<<Len:16, 0x51:8, SDLArpResponse/binary>>
message SDLArpResponse {
uint32 target_ip = 1;
bytes target_mac = 2;
uint32 origin_ip = 3;
bytes context = 4;
}
target_mac:查询成功时为目标 MAC;查询失败时为空字节串。- 其它字段与请求一致或对应请求上下文。
5.8 QUIC Policy 查询
Client -> Server
<<Len:16, 0xb0:8, SDLPolicyRequest/binary>>
message SDLPolicyRequest {
uint32 src_identity_id = 1;
uint32 dst_identity_id = 2;
uint32 version = 3;
}
src_identity_id:源端身份 ID。dst_identity_id:目标端身份 ID。version:Client 侧规则版本,服务端原样回写,Client 用于判断是否覆盖本地缓存。
响应:
Server -> Client
<<Len:16, 0xb1:8, SDLPolicyResponse/binary>>
message SDLPolicyResponse {
uint32 src_identity_id = 1;
uint32 dst_identity_id = 2;
uint32 version = 3;
bytes rules = 4;
}
rules:稀疏序列化规则列表,每条规则格式为<<Proto:8, Port:16>>。Proto:协议号,例如 TCP/UDP 对应的协议编号。Port:允许访问的目标端口。- 服务端只下发 allow 规则,deny 规则在服务端侧已过滤。
- 如果请求解析或规则查询失败,当前服务端可能不返回响应包。
5.9 QUIC ExposedService 查询
Client -> Server
<<Len:16, 0xb2:8, SDLExposedServiceRequest/binary>>
message SDLExposedServiceRequest {
uint32 version = 1;
}
version:Client 当前缓存版本,服务端响应中原样返回。
响应:
Server -> Client
<<Len:16, 0xb3:8, SDLExposedServiceResponse/binary>>
message SDLExposedServiceResponse {
uint32 version = 1;
repeated uint32 tcp_ports = 2;
repeated uint32 udp_ports = 3;
}
tcp_ports:当前节点允许暴露的 TCP 端口列表。udp_ports:当前节点允许暴露的 UDP 端口列表。- 服务端只返回
1..65534范围内的端口。 - API 查询失败时当前服务端可能不返回响应包。
5.10 QUIC Event 推送
Server -> Client
<<Len:16, 0x10:8, SDLEvent/binary>>
message SDLEvent {
message NatChanged {
bytes mac = 1;
uint32 ip = 2;
}
message SendRegister {
bytes dst_mac = 1;
uint32 nat_ip = 2;
uint32 nat_port = 3;
uint32 nat_type = 4;
optional SDLV6Info v6_info = 5;
}
message ExposedServiceChanged {
}
message NetworkShutdown {
string message = 1;
}
oneof event {
NatChanged nat_changed = 1;
SendRegister send_register = 2;
NetworkShutdown shutdown = 3;
ExposedServiceChanged exposed_service_changed = 4;
}
}
nat_changed:某个 MAC 的虚拟 IP 或 NAT 映射发生变化。Client 应清理对应 peer 缓存、ARP 缓存或重新查询 peer 信息。send_register:服务端要求当前 Client 向dst_mac对应节点发送 UDPPACKET_REGISTER打洞请求。nat_ip/nat_port/nat_type/v6_info是目标可达地址信息。exposed_service_changed:当前节点暴露服务配置变化。Client 应重新发送PACKET_EXPOSED_SERVICE_REQUEST获取端口列表。shutdown:网络关闭。Client 应停止当前网络会话并释放本地资源。- Event 不需要 Client 回复 Ack。
5.11 QUIC Command 下发和 ACK
Server -> Client
<<Len:16, 0x11:8, SDLCommand/binary>>
message SDLCommand {
uint32 pkt_id = 1;
message ExitNodeControl {
int32 action = 1;
string remark = 2;
}
oneof command {
ExitNodeControl exit_node = 2;
}
}
pkt_id:服务端生成的命令序号,Client 回复时必须原样带回。exit_node.action:出口节点控制动作,具体动作值由业务层约定。exit_node.remark:操作备注或调试说明。
Client 处理完成后回复:
Client -> Server
<<Len:16, 0x12:8, SDLCommandAck/binary>>
message SDLCommandAck {
uint32 pkt_id = 1;
int32 code = 2;
string message = 3;
bytes data = 4;
}
pkt_id:必须与收到的SDLCommand.pkt_id一致。code:处理结果码,建议0表示成功,非0表示失败。message:处理结果说明。data:可选业务数据。- 服务端收到 ACK 后,会把结果转发给等待该命令响应的内部调用方。
5.12 QUIC Unregister
Client -> Server
<<Len:16, 0x05:8>>
- Client 主动退出网络时发送。
- 服务端收到后调用
sdlan_network:unregister/3清理当前client_id + mac的绑定,并关闭控制连接。
6. SSL/TLS 接入
SSL/TLS 接入代码位于 src/ssl。服务端通过 Ranch SSL 监听,配置来自 ssl_server:
- 默认端口:
1443 - ALPN:
punchnet/1.0 - TLS 版本:
tlsv1.3、tlsv1.2 - 默认最大应用包:
max_packet_size = 16384 - 默认心跳间隔:
heartbeat_sec = 15
6.1 SSL/TLS 传输帧
SSL/TLS 接入使用 Erlang socket {packet, 2} 分包。对非 Erlang Client 来说,线上格式仍然是:
TlsStreamData = <<Len:16, Frame:Len/binary>>
Frame = <<PacketType:8, ProtobufPayload/binary>>
Len:2 字节无符号长度,表示后续Frame字节数,不包含Len自身。- 服务端
ssl层会自动剥离Len后把Frame交给sdlan_session。 - 服务端发送时也依赖
{packet, 2}自动加长度前缀。 - 单个发送包长度必须不超过
65535,业务上应遵守 Welcome 中的max_packet_size。
6.2 SSL/TLS 连接建立流程
- Client 与服务端建立 TCP 连接。
- Client 完成 TLS 握手,ALPN 使用
punchnet/1.0。 - 服务端 TLS 握手成功后立即发送
PACKET_WELCOME。 - Client 收到 Welcome 后发送
PACKET_REGISTER_SUPER。 - 注册成功后服务端返回
PACKET_REGISTER_SUPER_ACK并进入registered状态。 - 注册失败时服务端返回
PACKET_REGISTER_SUPER_NAK,随后关闭连接。 - 进入
registered后,Client 必须按 Welcome 中的heartbeat_sec周期发送PACKET_PING。
6.3 SSL/TLS 消息交互
SSL/TLS 接入的应用层消息与 QUIC 完全一致,区别只在传输层:
- Welcome:见
5.3,包类型0x4f。 - RegisterSuper / RegisterSuperAck / RegisterSuperNak:见
5.4,包类型0x01/0x02/0x04。 - Ping / Pong:见
5.5,包类型0x08/0x09。 - QueryInfo / PeerInfo:见
5.6,包类型0x06/0x07。 - ARP 查询:见
5.7,包类型0x50/0x51。 - Policy 查询:见
5.8,包类型0xb0/0xb1。 - ExposedService 查询:见
5.9,包类型0xb2/0xb3。 - Event 推送:见
5.10,包类型0x10。 - Command / CommandAck:见
5.11,包类型0x11/0x12。 - Unregister:见
5.12,包类型0x05。
Client 如果同时支持 QUIC 和 SSL/TLS,建议抽象出同一个应用层 Frame 编解码模块,仅替换底层连接、握手和 stream/socket 读写逻辑。
7. UDP STUN 和数据转发
UDP 逻辑不属于 src/quic 或 src/ssl,但它依赖控制面注册返回的 session_token 和网络密钥,是 Client 完整接入流程的一部分。相关服务端实现位于:
src/sdlan_stun.erlsrc/sdlan_stun_port_assist.erlsrc/sdlan_stun_peer_assist.erl
UDP 包没有 2 字节长度前缀:
UdpPacket = <<PacketType:8, ProtobufPayload/binary>>
7.1 STUN Request
Client -> STUN
<<0x30:8, SDLStunRequest/binary>>
message SDLStunRequest {
string client_id = 1;
uint32 network_id = 2;
bytes mac = 3;
uint32 ip = 4;
uint32 nat_type = 5;
optional SDLV6Info v6_info = 6;
bytes session_token = 7;
}
client_id/network_id/mac/ip:必须与控制面RegisterSuper一致。nat_type:Client 通过STUN_PROBE判断出的 NAT 类型。v6_info:Client 可用的 IPv6 辅助信息,没有时为空。session_token:控制面RegisterSuperAck.session_token,服务端用它校验当前 Client 的 NAT 上报。- 服务端以 UDP 源地址和源端口作为该节点的最新 NAT 映射,并通知其它节点
nat_changed。
响应:
STUN -> Client
<<0x31:8, SDLStunReply/binary>>
message SDLStunReply {
}
- 空消息体,仅表示服务端已收到并处理 STUN 心跳。
- Client 需要周期性发送该包维持 NAT 映射,建议周期小于 NAT 超时时间。
7.2 STUN Probe
Client -> STUN
<<0x32:8, SDLStunProbe/binary>>
message SDLStunProbe {
uint32 cookie = 1;
uint32 attr = 2;
uint32 step = 3;
}
cookie:Client 生成的随机值,用于匹配响应。attr:探测属性,取值见第 3 节STUN_ATTR_*。step:Client 侧探测步骤编号。当前sdlan_stun主响应代码未写回该字段,响应里通常为 protobuf 默认值0,Client 需要兼容。
响应:
STUN -> Client
<<0x33:8, SDLStunProbeReply/binary>>
message SDLStunProbeReply {
uint32 cookie = 1;
uint32 step = 2;
uint32 port = 3;
uint32 ip = 4;
}
cookie:原样返回请求中的cookie。step:当前实现通常为默认值0。port:服务端看到的 Client 公网 UDP 源端口。ip:服务端看到的 Client 公网 IPv4,按 32 位整数传输。
典型 NAT 判断流程:
- 向主 STUN 地址发送
attr = 0,拿到公网地址 A。 - 如果公网地址 A 等于本地 UDP 地址,则为
noNat。 - 向另一组 STUN 地址发送
attr = 0,拿到公网地址 B。 - 如果 A 和 B 的 IP 不同,则倾向判断为
symmetric。 - 向主 STUN 地址发送
attr = 1,如果能收到辅助 peer 路径响应,则说明 NAT 对来源 IP/端口限制较少。 - 向主 STUN 地址发送
attr = 2,如果能收到辅助端口路径响应,则说明 NAT 对来源端口限制较少;如果收不到则按端口限制型或降级策略处理。 - 任何关键步骤无响应时,可按
blocked或降级策略处理。
7.3 Client 间 UDP 打洞
当 Client 通过 QUERY_INFO 或 send_register 事件拿到对端 NAT 地址后,向对端 UDP 地址发送:
Client A -> Client B
<<0x20:8, SDLRegister/binary>>
message SDLRegister {
uint32 network_id = 1;
bytes src_mac = 2;
bytes dst_mac = 3;
}
network_id:当前虚拟网络 ID。src_mac:发送方 MAC。dst_mac:目标方 MAC。- 该包用于在双方 NAT 设备上建立映射,不经过控制连接。
对端收到后回复:
Client B -> Client A
<<0x21:8, SDLRegisterAck/binary>>
message SDLRegisterAck {
uint32 network_id = 1;
bytes src_mac = 2;
bytes dst_mac = 3;
}
src_mac:ACK 发送方 MAC。dst_mac:ACK 目标方 MAC。- 收到 ACK 后,Client 可以优先使用 P2P 直连发送
SDLData。
7.4 UDP 数据包
Client -> STUN/Peer
<<0xff:8, SDLData/binary>>
message SDLData {
uint32 network_id = 1;
bytes src_mac = 2;
bytes dst_mac = 3;
bool is_p2p = 4;
uint32 ttl = 5;
bytes data = 6;
bytes session_token = 7;
uint32 identity_id = 8;
}
network_id:当前虚拟网络 ID。src_mac:发送方 MAC。dst_mac:目标 MAC。广播或组播 MAC 会触发服务端向网络内其它在线节点广播转发。is_p2p:Client 直连发送时为true;服务端转发时会改写为false。ttl:转发跳数。STUN 服务端转发前会执行ttl - 1。data:加密后的真实业务数据。session_token:控制面注册返回的会话 token。identity_id:源端身份 ID,对端用于本地权限判断。
服务端转发逻辑:
- STUN 服务端收到
PACKET_STUN_DATA后解析SDLData。 - 按
network_id找到当前网络 ETS 表。 - 校验
src_mac当前在线且存在 endpoint。 - 如果
dst_mac是广播或组播,转发给除src_mac外所有有 NAT hole 的 endpoint。 - 如果
dst_mac是单播,查找目标 endpoint 的 NAT hole 并转发。 - 转发前将
ttl减 1,并把is_p2p改为false。 - 服务端按网络带宽限制做限流;限流或找不到目标时丢弃。
8. 推荐 Client 完整接入流程
- 通过 HTTP/API 完成登录和网络/IP 分配,拿到
access_token、network_id、虚拟 IP、掩码、身份 ID 等信息。 - 优先尝试 QUIC 接入;如果网络环境不支持 QUIC,可降级到 SSL/TLS 接入。
- 完成控制连接握手并接收
PACKET_WELCOME。 - 发送
PACKET_REGISTER_SUPER。 - 收到
PACKET_REGISTER_SUPER_ACK后,用私钥解密key,保存algorithm、region_id、session_token。 - 启动控制连接心跳,按
heartbeat_sec周期发送PACKET_PING并处理PACKET_PONG。 - 使用
PACKET_STUN_PROBE判断 NAT 类型。 - 周期性发送
PACKET_STUN_REQUEST,上报 NAT 类型、IPv6 信息和session_token。 - 有目标通信需求时,先使用
PACKET_ARP_REQUEST或本地缓存确定目标 MAC。 - 通过
PACKET_POLICY_REQUEST查询访问策略,按rules在本地做放行判断。 - 通过
PACKET_QUERY_INFO查询目标 NAT 信息,并根据SDLEvent.SendRegister做双向 UDP 打洞。 - P2P 打洞成功后优先直连发送
PACKET_STUN_DATA;失败时通过 STUN 服务端转发。 - 处理服务端推送的
SDLEvent和SDLCommand。Command 必须回复SDLCommandAck。 - Client 主动退出时发送
PACKET_UNREGISTER并关闭本地控制连接和 UDP 资源。
===端口说明====
组件 默认端口/配置 」 说明 」
QUIC | 443 | ALPN punchnet/1.0 |控制面主入口,允许 1条双向 stream。
SSL/TLS | 1443 | ALPN punchnet/1.0 |控制面备用入口,与 QUIC 共用sdlan session
STUN | 1365 | acceptor_nums=5 |UDP_NAT 上报、探测和服务端转发
STUN port assist | 1366 |同节点变端 probe reply
STUN peer assist |配置的 punchnet.aioe.tech:1366|转发到另一辅助节点后回复。
IPv6 assist | 1367 |IPv6 可达性探测。