sdlan-rs/docs/protocol.md
2024-02-26 14:05:43 +08:00

9.6 KiB
Raw Blame History

sdlan交互协议

sdlan协议的总体格式如下

+-----------+------------+--------+----------+
| 2字节的长度 | 2字节消息id |  消息头 |  消息体   |
+-----------+------------+--------+----------+

1. 长度

2字节长度包含后面的所有字节的长度不包括自身

2. 消息id

用于匹配请求和响应占用2字节目前可以都先填写0

3. 消息头

在sdlan中有一个common头使用二进制协议消息头主要包含了协议版本消息来源id从哪个节点发出的节点标识消息ttl以及一些协议的flagflag标志以后可以扩充

消息头二进制格式如下:

+---------+----+-----+------+-----+
| version | id | ttl | flag | pc  |
+---------+----+-----+------+-----+

其中version占用一个字节用于标识协议版本。id用于唯一标识某个客户端为长度为32字节的uuid之后的ttl占用一个字节当ttl为0则直接丢弃该数据包。后面的flag占用2字节用于标识数据包属性目前拥有的标识如下

  • from_sn: 0x0020,表示这个数据包是从服务端发送过来的。
  • socket: 0x0040表示这个数据包里面包含了有用的socket信息通常在服务端转发的时候有用

最后的pc表示后面数据包的类型目前数据包类型如下

  • 0 —— invalid pkt type,表示无效的数据包类型
  • 1 —— register super,表示客户端像服务端发送的注册请求
  • 2 —— packet表示客户端向服务端或者客户端发送给客户端发送的来自tun的数据包。
  • 3 —— register数据包,表示客户端之间打洞请求消息
  • 4 —— register_ack数据包,表示客户端之间打洞请求消息
  • ... TODO

4. 消息体

消息提根据协议头中的pc不同拥有不同的结构结构体在json化之后添加到消息体的位置。

4.1. RegisterSuper包类型

register superpc为1表示客户端像服务端发送的注册请求该请求每隔15-20秒发送一次RegisterSuper的json结构如下该结构没有用aes加密为了安全以后如果没有加密的结构体可以预置一个32为aes密钥

{
    "pass": "初始密钥,用于最初始的简单验证, 目前固定为`encrypt!`",
    // 客户端自己随机生成的整数用于匹配RegisterSuper和RegisterSuperACK, RegisterSuperNAK, RegisterSuperAcknowledge
    "cookie": $uint32,

    // sock在客户端发送时不需要填写在Supernode之间转发的时候会带上这个表示客户端的信息。
    "sock": {
        // family表示客户端的ip协议版本2表示ipv410表示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信息如果没有ipv6则不用填写
    "v6_info": {
        // 客户端的ipv6的端口
        "port": $uint16,
        // 客户端的ipv6地址
        "v6": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
    },
    // 客户端tun设备的ip信息
    "dev_addr": {
        // 客户端的tun的ip地址如果没有ip第一次上线等情况则填写0
        "net_addr": $uint32,
        // 子网掩码的位数一般为24 如果没有ip则填写0
        "net_bit_len": $uint8,
    },
    // 客户端自己的公钥在服务端返回加密的aes密钥时会使用这个公钥加密
    "pub_key": "",
    // 字符串用于标识一个用户的id或者一个由用户生成的token
    "token": "",
}

4.2. Packet数据包

packetpc为2表示客户端向服务端或者客户端发送给客户端发送的来自tun的数据包。packet数据包的json化之后的结构如下

{
    // 来源ip
    "src_ip": $uint32,
    // 目的ip
    "dst_ip": $uint32,
    // sock信息, sock信息包含了来自客户端的信息
    // 通常客户端发送一个packet数据包的时候不需要携带这个信息。
    // 而在服务端收到一个packet的数据包的时候在转发的时候会将客户端的信息放入这个结构然后发送给需要转发的客户端。所以在客户端收到转发的packet数据包的时候可以通过这个sock结构得知发送的客户端的端口信息。在sock结构有用的时候flags中的socket0x0040标识也会被置位。
    "sock": {
        // family表示客户端的ip协议版本2表示ipv410表示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]
    },
    // data为二进制的字节数组为aes加密之后的tun流量。
    "data": []
}

其中,src_ip为tun的ip数据包的来源ipdst_ip为tun的ip数据包的目的ip。均为大端序的32位无符号整数。sock为转发时由sn填充的发送客户端的信息发送短不需要处理。data为tun的数据流量使用network_pass进行加密后的二进制数据。

在发送packet数据包之前消息体部分的数据为json化之后的packet使用header_pass进行aes加密后的二进制数据。

4.3. Register数据包

registerpc为3客户端向另一个客户端发送打洞请求的数据包。json格式如下

{
    // 源ip
    "src_ip": $uint32,
    // 目的ip
    "dst_ip": $uint32,
    // supernode转发时开到的发送者的外网ip信息
    "sock": {
        // family表示客户端的ip协议版本2表示ipv410表示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]
    },
    // 发送者的tun的ip信息
    "dev_addr": {
        // tun设备的ip大端序的32位无符号
        "net_addr": $uint32,
        // 子网掩码1的位数一般为24
        "net_bit_len": $uint8,
    },
}

4.4. RegisterACK数据包

pc为4,

4.5. RegisterSuperACK数据包

pc为5。在服务端收到registersuper之后如果服务端判断该节点已经授权而且是刚上线就会返回一个RegisterSuperACK告知该节点ip信息。另外如果在服务端界面上进行操作将本来没有授权的节点进行授权或者将已将寂静授权的节点转移到另一个网络里面服务端也会主动向客户端发送一个RegisterSuperAck消息将客户端需要的信息新网络的加密aes等主动告诉客户端。

RegisterSuperACK的消息格式如下暂时该结构没有加密以后可以将该结构整个用客户端的rsa公钥加密

{
    // 匹配RegisterSuper
    "cookie": $uint32,

    // 服务端看来的该客户端的外网ip信息
    "sock": {
        // family表示客户端的ip协议版本2表示ipv410表示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]
    },
    // 服务端给客户端分配的tun设备的ip信息
    "dev_addr": {
        // 客户端分配到的ip地址
        "net_addr": $uint32,
        // 客户端分配到的ip地址的掩码位数
        "net_bit_len": $uint8,
    },
    // rsa加密之后的加密头的aes密钥
    "header_key": "",
    // rsa加密之后的tun流量的加密aes密钥
    "encrypted_key": "",
    // 多久之后客户端应该再发起RegisterSuper消息暂时可以忽略
    "lifetime": $int16
}

4.6. RegisterSuperAcknowledge数据包

在服务端收到registersuper之后如果服务端判断该节点已经授权而且不是第一次收到该节点的RegisterSuper消息这个时候该RegisterSuper消息类似于心跳数据包了就会返回一个RegisterSuperAcknowledge告知该节点服务端已经收到你的消息了。另外如果服务端判断该节点没有授权也会返回一个RegisterSuperAcknowledge消息这个时候可以理解为服务端收到你的消息了但是你没有授权我这边不会给你分配ip你就这样等着吧。

RegisterSuperAcknowledge的消息格式如下该结构没有用aes加密

{
    // 来自registerSuper消息的cookie
    "cookie": $uint32,
}

4.6. RegisterSuperNAK数据包

服务端在节点发送RegisterSuper时候如果该数据包里面的初始验证信息等无效则会直接返回RegisterSuperNAK消息此时客户端应该直接退出。

量外如果服务端判断到比如一个用户最多可以创建10个客户端但是这个节点是第11个了在收到该客户端的RegisterSuper以后和会直接返回RegisterSuperNAK消息客户端同样应该直接退出。

RegisterSuperNAK结构如下该结构没有用aes加密

{
    // 来自registerSuper消息的cookie
    "cookie": $uint32,
}