Skip to content

Feature Request: Native OpenBSD SO_SPLICE support for zero-copy TCP forwarding #5756

@DavidOsipov

Description

@DavidOsipov

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


中文 / 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 的修复所证明。

参考资料

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions