From b7adef51e81361232403d58bf9e87dd4fe36f97b Mon Sep 17 00:00:00 2001 From: asxalex Date: Tue, 27 Feb 2024 10:36:51 +0800 Subject: [PATCH] changed the packet document --- docs/protocol.md | 148 +++++++++++++++++++++++++++++---- src/packet/common.rs | 12 +++ src/packet/mod.rs | 9 ++ src/packet/peer_info.rs | 12 +++ src/packet/query_peer.rs | 15 ++++ src/packet/register.rs | 3 +- src/packet/register_ack.rs | 13 +++ src/packet/unregister_super.rs | 6 ++ 8 files changed, 200 insertions(+), 18 deletions(-) create mode 100644 src/packet/peer_info.rs create mode 100644 src/packet/query_peer.rs create mode 100644 src/packet/unregister_super.rs diff --git a/docs/protocol.md b/docs/protocol.md index e22b62b..d6a1b60 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -28,6 +28,7 @@ sdlan协议的总体格式如下: * `from_sn`: `0x0020`,表示这个数据包是从服务端发送过来的。 * `socket`: `0x0040`,表示这个数据包里面包含了有用的socket信息(通常在服务端转发的时候有用)。 +* `v6_info`: `0x0080`,表示这个数据包里面包含了ipv6信息(专门用在packet数据包里面,目前只有这个数据包使用了该标识)。 最后的pc表示后面数据包的类型,目前数据包类型如下: @@ -35,11 +36,24 @@ sdlan协议的总体格式如下: * `1` —— `register super`,表示客户端像服务端发送的注册请求 * `2` —— `packet`,表示客户端向服务端(或者客户端发送给客户端)发送的来自tun的数据包。 * `3` —— `register`数据包,表示客户端之间打洞请求消息 -* `4` —— `register_ack`数据包,表示客户端之间打洞请求消息 -* ... TODO +* `4` —— `register_ack`数据包,表示客户端之间打洞ACK消息 +* `5` —— `register_super_ack`数据包,表示可以对节节点进行授权,返回对应的ip地址等信息 +* `6` —— `register_super_acknowledge`数据包,表示对`register_super`的一般返回,只是告诉客户端:服务端收到他的注册请求了 +* `7` —— `register_super_nak`数据包,表示服务端不接受该客户端的上线请求,客户端应该直接退出 +* `8` —— `unregister_super`数据包,客户端告诉服务端,客户端自己要下线了。 +* `9` —— `query_info`数据包,客户端向服务端查询对端的ip信息 +* `10` —— `peer_info`数据包,服务端向客户端返回对端的ip等信息。 ## 4. 消息体 -消息提根据协议头中的pc不同,拥有不同的结构,结构体在json化之后,添加到消息体的位置。 +消息提根据协议头中的pc不同,拥有不同的结构,结构体在json化之后,需要加密的数据包进行加密,然后将内容添加到消息体的位置。 + +其中,RegisterSuper, RegisterSuperACK, RegisterSuperAcknowledge, RegisterSuperNAK,这几个消息,在发送和接收的时候,消息提内容没有进行加密,直接是json序列化之后的内容(为了提高安全性,服务端和客户端在实际编写的时候,可以商量一个预置的aes密钥,这几个消息类型就用这个密钥进行加解密)。 + +在服务端返回RegisterSuperACK的时候,会有`enctypted_key`和`header_key`。这两个字段为使用客户端rsa公钥加密之后的两个aes密钥。 +其中: + +* `encrypted_key`:用于加密PACKET数据包中的data流量,也就是用于加密tun网卡的流量。 +* `header_key`:用于加密之后传输的结构本身,比如客户端在向服务端发送一个PACKET数据包的时候,在将json序列化之后,再将序列化之后的内容使用`header_key`aes再加密一次,然后放到消息提的位置。 ### 4.1. RegisterSuper包类型 `register super`,pc为1,表示客户端像服务端发送的注册请求,该请求每隔15-20秒发送一次,RegisterSuper的json结构如下(该结构没有用aes加密,为了安全,以后如果没有加密的结构体,可以预置一个32为aes密钥): @@ -105,6 +119,13 @@ sdlan协议的总体格式如下: // 如果family为10,则v6有用,一个16字节的数组,表示ipv6地址 "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] }, + // 如果对端有ipv6,则填写该结构,否则就省略 + "v6_info": { + // ipv6的端口 + "port": $uint16, + // v6 + "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + } // data为二进制的字节数组,为aes加密之后的tun流量。 "data": [] } @@ -114,16 +135,34 @@ sdlan协议的总体格式如下: 在发送packet数据包之前,消息体部分的数据,为json化之后的packet使用`header_pass`进行aes加密后的二进制数据。 +在客户端收到PACKET消息的时候,如果数据包来自服务端(转发过来的),则可以向sock字段表示的对端信息发送register消息 + +在实际发送的时候,为了效率,packet数据包被直接打包成二进制进行处理。二进制规则如下: + +1. 在消息头的`flags`里面会给相应的标志位置位,比如,如果有sock信息,则flags会有`SOCKET`标识,如果有`ipv6`信息,则会有`v6_info`标识。 +2. 消息体序列化`src_ip`,大端序表示`uint32`的来源ip地址 +2. 消息体序列化`dst_ip`,大端序表示`uint32`的目的ip地址 +3. 如果消息头的`socket`置位了,接下来就是序列化sock信息: + 1. 一个字节的family + 2. 2个字节(大端序的`uint16`)端口号 + 1. 如果family是2(表示是ipv4),则接下来是4个字节的ipv4 + 2. 如果family是10(表示是ipv6,目前不会有这种情况,只是为了兼容以后服务端也支持ipv6的情况),则接下来是16个字节的ipv6地址 +4. 如果消息头的`v6_info`置位了,则接下来就是ipv6信息: + 1. 2个字节(大端序的`uint16`)ipv6端口号 + 2. 16个字节的ipv6地址 +5. 之后是`network_pass`加密后的二进制tun接口的流量数据。 + ### 4.3. Register数据包 -`register`,pc为3,客户端向另一个客户端发送打洞请求的数据包。json格式如下: +`register`,pc为3,客户端向另一个客户端发送打洞请求的数据包。在完全不知情的情况下,当一个客户端需要向另一个客户端发送数据的时候(tun接口的流量),发送端首先查看自己是否知道对方的地址,此时不知道,发送端就向服务端发起一个QueryInfo的数据包,同时,发送端的tun数据包首先通过服务端中转到对端客户端,一方面,发送端在收到QueryInfo的返回PeerInfo数据包的时候,会向接收端发起一个(或者一系列)Register数据包;另一方面,接收端在收到Packet数据包之后,也会发起一个(或者一系列)Register数据包。两边同时发起Register进行打洞,当有一个数据包能被正确接收,则马上返回RegisterACK数据包,两边打洞成功。在json格式如下: ```json { - // 源ip + "cookie": $uint32, + // 源tun ip "src_ip": $uint32, - // 目的ip + // 目的tun ip "dst_ip": $uint32, - // supernode转发时开到的发送者的外网ip信息 + // supernode转发时开到的发送者的外网ip信息,发送的时候,不需要填写 "sock": { // family表示客户端的ip协议版本,2表示ipv4,10表示ipv6 "family": 2|10, @@ -134,18 +173,33 @@ sdlan协议的总体格式如下: // 如果family为10,则v6有用,一个16字节的数组,表示ipv6地址 "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] }, - // 发送者的tun的ip信息 - "dev_addr": { - // tun设备的ip,大端序的32位无符号 - "net_addr": $uint32, - // 子网掩码1的位数,一般为24 - "net_bit_len": $uint8, - }, } ``` ### 4.4. RegisterACK数据包 -pc为4, +pc为4,结构如下: + +```json +{ + // 匹配Register + "cookie": $uint32, + // 源tun ip + "src_ip": $uint32, + // 目的tun ip + "dst_ip": &uint32, + // 发送者的外网ip信息 + "sock": { + // family表示客户端的ip协议版本,2表示ipv4,10表示ipv6 + "family": 2|10, + // 客户端开放的端口, u16 + "port": $uint16, + // 如果family为2,则v4有用,一个四字节的数组,表示ipv4 + "v4": [1,2,3,4], + // 如果family为10,则v6有用,一个16字节的数组,表示ipv6地址 + "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + } +} +``` ### 4.5. RegisterSuperACK数据包 pc为5。在服务端收到registersuper之后,如果服务端判断该节点已经授权,而且是刚上线,就会返回一个RegisterSuperACK,告知该节点ip信息。另外,如果在服务端界面上进行操作,将本来没有授权的节点进行授权,或者将已将寂静授权的节点,转移到另一个网络里面,服务端也会主动向客户端发送一个RegisterSuperAck消息,将客户端需要的信息(新网络的加密aes等)主动告诉客户端。 @@ -196,7 +250,7 @@ RegisterSuperAcknowledge的消息格式如下(该结构没有用aes加密) } ``` -### 4.6. RegisterSuperNAK数据包 +### 4.7. RegisterSuperNAK数据包 服务端在节点发送RegisterSuper时候,如果该数据包里面的初始验证信息等无效,则会直接返回RegisterSuperNAK消息,此时客户端应该直接退出。 量外,如果服务端判断到,比如一个用户最多可以创建10个客户端,但是这个节点是第11个了,在收到该客户端的RegisterSuper以后,和会直接返回RegisterSuperNAK消息,客户端同样应该直接退出。 @@ -208,4 +262,64 @@ RegisterSuperNAK结构如下(该结构没有用aes加密): // 来自registerSuper消息的cookie "cookie": $uint32, } -``` \ No newline at end of file +``` + +### 4.8. UnregisterSuper数据包 +当客户端没有收到RegisterSuperNAK的情况下(没有被服务端拒绝接入),在客户端结束之前,可以发送UnregisterSuper数据包,该数据包告诉服务端,客户端将要下线了,可以将这个客户端的相关信息删除了。 + +UnregisterSuper数据包结构如下: + +```json +{ + // 客户端被分配的ip,如果没有分配到,则设置为0 + "src_ip": $uint32 +} +``` + +### 4.9. QueryInfo数据包 +在发送端发送数据包PACKET消息的时候,如果对方ip找不到(没有打洞),则会发起一个QueryInfo数据包,服务端在收到相应信息之后,会返回锁查找的接收端的信息PEERINFO数据包,发送端收到PEERINFO之后,便发起Register进行打洞。 + +结构如下: + +```json +{ + // 自身tun ip + "src_ip": $uint32, + // 需要查找的目标的tun ip + "dst_ip": $uint32, +} +``` + +### 4.10. PeerInfo数据包 +PeerInfo结构如下: + +```json +{ + // 标识为,暂时没有用,都填写0 + "flag": $uint16, + // 之前发起短的tun ip + "src_ip": $uint32, + // 目标的tun ip + "dst_ip": $uint32, + + // 需要查找的对端的外网通信信息 + "sock": { + // family表示客户端的ip协议版本,2表示ipv4,10表示ipv6 + "family": 2|10, + // 客户端开放的端口, u16 + "port": $uint16, + // 如果family为2,则v4有用,一个四字节的数组,表示ipv4 + "v4": [1,2,3,4], + // 如果family为10,则v6有用,一个16字节的数组,表示ipv6地址 + "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + }, + // 朝兆的对端的ipv6信息,如果有的话 + "v6_info": { + // 端口 + "port": $uint16, + // v6地址 + "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + } + +} +``` diff --git a/src/packet/common.rs b/src/packet/common.rs index c841eeb..f3cba28 100644 --- a/src/packet/common.rs +++ b/src/packet/common.rs @@ -89,6 +89,12 @@ pub enum PacketType { PKTRegisterSuperACK, PKTRegisterSuperAcknowledge, PKTRegisterSuperNAK, + PKTUnregisterSuper, + + // 客户端向服务端发送另一个客户端的信息 + PKTQueryPeer, + // 服务端向客户端返回消息 + PKTPeerInfo, } impl std::convert::From for PacketType { @@ -102,6 +108,9 @@ impl std::convert::From for PacketType { 5 => Self::PKTRegisterSuperACK, 6 => Self::PKTRegisterSuperAcknowledge, 7 => Self::PKTRegisterSuperNAK, + 8 => Self::PKTUnregisterSuper, + 9 => Self::PKTQueryPeer, + 10 => Self::PKTPeerInfo, _ => Self::PKTInvalid, } } @@ -118,6 +127,9 @@ impl PacketType { Self::PKTRegisterSuperACK => 5, Self::PKTRegisterSuperAcknowledge => 6, Self::PKTRegisterSuperNAK => 7, + Self::PKTUnregisterSuper => 8, + Self::PKTQueryPeer => 9, + Self::PKTPeerInfo => 10, } } } diff --git a/src/packet/mod.rs b/src/packet/mod.rs index a29cb06..637df7f 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -21,3 +21,12 @@ pub use register_super_acknowledge::*; mod register_super_nak; pub use register_super_nak::*; + +mod unregister_super; +pub use unregister_super::*; + +mod query_peer; +pub use query_peer::*; + +mod peer_info; +pub use peer_info::*; diff --git a/src/packet/peer_info.rs b/src/packet/peer_info.rs new file mode 100644 index 0000000..e026cc6 --- /dev/null +++ b/src/packet/peer_info.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +use crate::peer::SdlanSock; + +#[derive(Serialize, Deserialize)] +pub struct PeerInfo { + // query peer some flag. + pub flag: u16, + pub src_ip: u32, + pub dst_ip: u32, + pub sock: SdlanSock, +} diff --git a/src/packet/query_peer.rs b/src/packet/query_peer.rs new file mode 100644 index 0000000..23db42a --- /dev/null +++ b/src/packet/query_peer.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; + +use crate::peer::SdlanSock; + +#[derive(Serialize, Deserialize)] +pub struct QueryPeer { + // query peer some flag. + pub flag: u16, + // 询问人的ip + pub src_ip: u32, + // 询问对方的ip + pub dst_ip: u32, + // sock信息,用于转发 + pub sock: Option, +} diff --git a/src/packet/register.rs b/src/packet/register.rs index b77304c..1be5a6f 100644 --- a/src/packet/register.rs +++ b/src/packet/register.rs @@ -4,6 +4,7 @@ use crate::peer::{IpSubnet, SdlanSock}; #[derive(Serialize, Deserialize)] pub struct Register { + pub cookie: u32, // 源ip pub src_ip: u32, // 目的ip @@ -11,5 +12,5 @@ pub struct Register { // supernode转发时开到的发送者的外网ip信息 pub sock: Option, // 发送者的tun的ip信息 - pub dev_addr: IpSubnet, + // pub dev_addr: IpSubnet, } diff --git a/src/packet/register_ack.rs b/src/packet/register_ack.rs index e69de29..9b8769b 100644 --- a/src/packet/register_ack.rs +++ b/src/packet/register_ack.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; + +use crate::peer::SdlanSock; + +pub struct RegisterACK { + pub cookie: u32, + // 返回方的tun接口的ip地址 + pub src_ip: u32, + // 发送Register的客户端的tun ip地址 + pub dst_ip: u32, + // + pub sock: Option, +} diff --git a/src/packet/unregister_super.rs b/src/packet/unregister_super.rs new file mode 100644 index 0000000..0b242ee --- /dev/null +++ b/src/packet/unregister_super.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct UnregisterSuper { + pub src_ip: u32, +}