-
Notifications
You must be signed in to change notification settings - Fork 5k
Description
English
Summary
Xray's XTLS Vision performance optimization relies on Linux's splice(2) syscall for zero-copy TCP forwarding between sockets. This code path is gated behind //go:build linux and is therefore entirely unavailable on OpenBSD, even though OpenBSD has had its own equivalent mechanism — SO_SPLICE via setsockopt(2) — since OpenBSD 4.9 (2011).
Background
On Linux, when Vision flow control is active and conditions are met (inbound is a plain TCP connection, outbound is VLESS+XTLS), Xray hands the socket pair to the kernel via splice(). The kernel forwards TLS-encrypted bytes directly between socket buffers without copying through Xray's userspace memory. This eliminates CPU overhead for in-kernel data transfer.
OpenBSD implements the same concept through a different API:
// Set source socket to splice into drain socket fd
setsockopt(source_fd, SOL_SOCKET, SO_SPLICE, &drain_fd, sizeof(drain_fd));The kernel function sosplice() / somove() transfers mbufs directly from the source socket's receive buffer to the drain socket's send buffer. From the [OpenBSD kernel docs](https://man.openbsd.org/sosplice.9):
Socket splicing for TCP first appeared in OpenBSD 4.9; support for UDP was added in OpenBSD 5.3.
Current Behavior
On OpenBSD, xtls-rprx-vision functions correctly as a censorship circumvention tool (inner TLS handshake padding, Reality handshake) but provides zero performance benefit from splice — all data is copied through Xray's Go runtime heap on every packet.
Proposed Change
Add an OpenBSD-specific splice implementation behind a build tag, analogous to the existing Linux implementation:
transport/internet/xtls/splice_openbsd.go // build tag: openbsd
transport/internet/xtls/splice_linux.go // existing
transport/internet/xtls/splice_other.go // stub for unsupported platforms
The OpenBSD implementation would call setsockopt(SO_SPLICE) instead of splice(2). The idle timeout parameter (struct timeval) maps naturally to Xray's existing connection timeout logic.
Why This Matters
OpenBSD is a legitimate deployment target for Xray — its hardened kernel, W^X enforcement, pledge(2)/unveil(2) sandboxing, and memory allocator mitigations make it attractive for privacy-focused gateway deployments (home routers, embedded boards like NanoPi). These users currently get the censorship-resistance of Vision without any of its performance benefits.
Prior Art in This Repository
Issue #31 ("Splice won't apply to Android") established the precedent of extending splice support beyond the initial Linux-only implementation. The fix was to include goos=android in the platform check — because Android runs a Linux kernel and the same splice(2) syscall is available there.
OpenBSD differs from the Android case in that it requires a separate API implementation (SO_SPLICE via setsockopt) rather than a simple build tag addition. However, the willingness to support splice on non-default platforms is already demonstrated by that fix.
References
- OpenBSD
sosplice(9)kernel manual: https://man.openbsd.org/sosplice.9 - OpenBSD
setsockopt(2): https://man.openbsd.org/setsockopt.2 - Xray Linux splice implementation:
transport/internet/xtls/ - OpenBSD splice first commit: OpenBSD 4.9 (Alexander Bluhm)
- Issue Splice won't apply to Android. #31 — Splice won't apply to Android (precedent for non-default platform support)
中文 / Mandarin
摘要
Xray 的 XTLS Vision 性能优化依赖 Linux 的 splice(2) 系统调用,实现 TCP 套接字之间的零拷贝转发。该代码路径通过 //go:build linux 编译标签限定,因此在 OpenBSD 上完全不可用——尽管 OpenBSD 自 4.9 版本(2011年)起就通过 setsockopt(2) 提供了功能等价的机制:SO_SPLICE。
背景
在 Linux 上,当 Vision 流控激活且满足条件时(入站为纯净 TCP 连接,出站为 VLESS+XTLS),Xray 通过 splice() 将套接字对交给内核处理。内核直接在套接字缓冲区之间转发 TLS 加密字节,无需经过 Xray 的用户态内存拷贝,从而消除了数据转发的 CPU 开销。
OpenBSD 通过不同的 API 实现了相同的机制:
// 将源套接字 splice 到目标套接字 fd
setsockopt(source_fd, SOL_SOCKET, SO_SPLICE, &drain_fd, sizeof(drain_fd));内核函数 sosplice() / somove() 直接将 mbufs 从源套接字的接收缓冲区移动到目标套接字的发送缓冲区。引用 [OpenBSD 内核文档](https://man.openbsd.org/sosplice.9):
TCP 套接字 splice 功能最早出现于 OpenBSD 4.9;UDP 支持在 OpenBSD 5.3 中加入。
当前行为
在 OpenBSD 上,xtls-rprx-vision 作为反审查工具可以正常工作(内层 TLS 握手随机填充、Reality 握手),但无法获得 splice 带来的任何性能提升——每个数据包都需要经过 Xray 的 Go 运行时堆进行内存拷贝。
建议改动
仿照现有 Linux 实现,通过编译标签添加 OpenBSD 专用的 splice 实现:
transport/internet/xtls/splice_openbsd.go // 编译标签: openbsd
transport/internet/xtls/splice_linux.go // 现有实现
transport/internet/xtls/splice_other.go // 不支持平台的桩实现
OpenBSD 实现将调用 setsockopt(SO_SPLICE) 替代 splice(2)。空闲超时参数(struct timeval)可以自然对应到 Xray 现有的连接超时逻辑。
重要性
OpenBSD 是 Xray 的合理部署目标——其硬化内核、W^X 强制执行、pledge(2)/unveil(2) 沙箱机制以及内存分配器防护,使其对注重隐私的网关部署极具吸引力(家庭路由器、NanoPi 等嵌入式开发板)。这类用户目前只能获得 Vision 的反审查能力,而无法享受其性能优势。
本仓库中的先例
Issue #31("Splice won't apply to Android")已经为在非默认平台上扩展 splice 支持建立了先例。当时的修复方案是在平台检查中加入 goos=android——因为 Android 运行的是 Linux 内核,splice(2) 系统调用同样可用。
OpenBSD 与 Android 的情况有所不同:它需要一套独立的 API 实现(通过 setsockopt 调用 SO_SPLICE),而非简单地添加编译标签。但对非默认平台提供 splice 支持的意愿,已经被 #31 的修复所证明。
参考资料
- OpenBSD
sosplice(9)内核手册:https://man.openbsd.org/sosplice.9 - OpenBSD
setsockopt(2):https://man.openbsd.org/setsockopt.2 - Xray Linux splice 实现:
transport/internet/xtls/ - OpenBSD splice 初始提交:OpenBSD 4.9(Alexander Bluhm)
- Issue Splice won't apply to Android. #31 — Splice won't apply to Android(非默认平台支持的先例)