AP-KCP

Armor-Piercing KCP (装甲穿透KCP/破甲KCP)

基于KCP修改和优化,用于穿透恶劣网络环境的高性能可靠传输协议(ARQ)。使用 Rust 实现。拥有基于 ring 的密码学支持,和基于 smol 的异步运行时。

下图为在校园网的繁忙时期,进行的简单带宽测试。

左为TCP(BBR拥塞控制)对照组,右为 AP-KCP(基于UDP传输)隧道。

speedtest

警告:AP-KCP可能会以侵占其他用户的带宽为代价换取使用者更好的网络吞吐效率,请勿进行大规模部署和使用,否则可能导致大范围的网络拥塞甚至瘫痪。

概览

AP-KCP 与 KCP 一样,基于不可靠包传输建立可靠流式传输。它与下层协议实现无关,只需保证下层是基于封包的传输即可,即包不会被切分或合并。无需保证传输顺序和传输可靠性,甚至无需保证每个包的正确性和完整性(误码和篡改均会被纠正)。例如 UDP 和 ICMP 都可用作底层传输。

AP-KCP 与 KCP 有几点主要区别:

AP-KCP 本身是一个异步库,可以被用于在任何不可靠的基于封包的底层传输上,建立高效的可靠流式传输。AP-KCP 的二进制发布版本是一个隧道程序,基于 UDP 建立了AP-KCP 连接。

使用

AP-KCP-TUN(作为可执行二进制程序使用)

AP-KCP 的二进制发行版本是一个基于 UDP 的加密隧道。

假设

客户端

shell ap-kcp-tun --client --password mypassword --local 127.0.0.1:3000 --remote 233.233.233.233:4000

服务端

shell ap-kcp-tun --server --password mypassword --local 0.0.0.0:4000 --remote 1.1.1.1:5000

你可以使用 --kcp-config 指定详细传输参数的配置文件(默认参数配置文件参见目录下 default-config.toml),使用 --algorithm 指定加密方式,支持下面三种 AEAD 加密方式:

AP-KCP(作为库使用)

AP-KCP 本身与底层协议实现无关。如果你需要在自己的协议上使用 AP-KCP,在 Cargo.toml 中添加依赖后,实现下面的 KcpIo trait 即可直接使用。

```rust

[asynctrait::asynctrait]

pub trait KcpIo { async fn sendpacket(&self, buf: &mut Vec) -> std::io::Result<()>; async fn recvpacket(&self, buf: &mut Vec) -> std::io::Result<()>; } ```

buf 需要可变的原因,是使下层对包进行操作时实现零内存分配。AP-KCP 将假定调用 send_packet 后 buf 内容无意义并立即清空。

下面是一个示例,为 smol::net::UdpSocket 实现了 KcpIo

```rust #[asynctrait::asynctrait] impl KcpIo for smol::net::UdpSocket { async fn send_packet(&self, buf: &mut Vec) -> std::io::Result<()> { self.send(buf).await?; Ok(()) }

    async fn recv_packet(&self, buf: &mut Vec<u8>) -> std::io::Result<()> {
        let size = self.recv(buf).await?;
        buf.truncate(size);
        Ok(())
    }
}

```

之后便可使用 UdpSocket 建立 AP-KCP 会话。

rust let udp = UdpSocket::bind("0.0.0.0:10000").await.unwrap(); udp.connect("233.233.233.233:20000").await.unwrap(); let kcp_handle = KcpHandle::new(udp, KcpConfig::default())?; let mut stream1 = kcp_handle.connect().await.unwrap(); let mut stream2 = kcp_handle.connect().await.unwrap(); let mut stream3 = kcp_handle.accept().await.unwrap(); stream1.read(buf).await.unwrap(); stream2.write(buf).await.unwrap(); stream3.read(buf).await.unwrap();

同一个 KcpHandle 可以建立多个传输流,使用 connect() 向远端发起连接,或使用 accept() 从远端接受连接。KcpStream 实现了 AsyncReadAsyncWrite, 可以与其他异步传输流一样使用 read() write() 读写。

KcpHandle 一旦 drop 将释放所有资源,并将其上建立的所有传输流全部强行关闭(不再收发任何包),请确保使用 KcpStreamKcpHandle 在作用域内。

KcpStream 的 drop 将使其优雅停机(四次挥手,等待超时),但仍然建议使用 close() 方法显式地关闭流。

编译

编译需要使用 rust 工具链。

shell git clone https://github.com/black-binary/ap-kcp.git cd ap-kcp make

如果你需要静态链接,可以换用 x86_64-unknown-linux-musl 或者其他 musl 工具链

shell make x86_64-unknown-linux-musl

然后去喝一杯咖啡。

细节

规格

AP-KCP 封包结构 | 2字节 | 1字节 | 2字节 | 4字节 | 4字节 | 4 字节 | 2字节 | len 字节 | | -------------- | ------------ | --------------------------------- | ----------------- | -------------- | -------------------------- | -------------- | --------- | | 流ID streamid | 指令 command | 可用接收窗口大小 recvwindowsize | 时间戳 timestamp | 包序号sequence | 接收窗口起始序号 recvnext | 包数据长度 len | 数据 data |

加密封包结构

| | n 字节 | 16 字节 | 12字节 | | ---------------------- | ---------------- | -------- | ---------------- | | 明文封包(n 字节) | 明文(AP-KCP封包) | 无此字段 | 无此字段 | | 密文封包(n+16+12 字节) | 密文 | 标签 tag | 一次性密钥 nonce |

无论 AP-KCP 包是否相同,每一次发送均重新生成一次 nonce 并重新加密。

加解密操作均为原地(in-place),全程零内存分配。

特点

AP-KCP 与 KCP 一样,基于不可靠包传输建立可靠流式传输,保留了 KCP 的优化策略:

同时做出了一些改进

其他

这个项目是我的计算机网络课程的课程设计,目前还很 Buggy,请不要过于自信地部署使用,或是用于渗透等非法用途。代码参考了原始 C 语言实现,tokio-kcp 和 mkcp。

使用它进行长时间高强度的网络传输约等于进行 DDoS,链路上各节点的负载会很高,使用前请三思。

请网络中心的老师们不要打我,都是你们换的长城宽带逼的。