跳转到内容

nsplane · 数据面

nsplane 是 NSIO 的数据面库:一个 Rust 库,负责从”本机出现一个 IP 报文”到”加密后的数据报从某条路径发出”之间的全部工作。它源自 cloudflare/boringtun 的分支(BSD-3-Clause),仓库为 dotns/nsplane。本页以 nsplane 仓库的 docs/design.md 与 docs/architecture.md 为准(tag v0.14.0,b28d649,2026-10-13)。从 0.10.x 升级见 0.11 迁移说明,0.12–0.14 的补充在同一页的 §7–§9。

改名:项目于 2026-10-02 由 nstun 改名为 nsplane(ADR 2026-10-02-rename-nsplane):它已经不是一个 TUN 设备,而是节点底层的数据通道,TUN 只是它的一种本地接法。仓库为 dotns/nsplane;crate 为 nsplane-*,原 boringtun 改为 nsplane-noise,原 boringtun-cli 改为 nsplane-cli。

nsplane 拥有报文 I/O(TUN 或进程内协议栈)、WireGuard 引擎、传输、报文过滤(ACL、NAT / 翻译、流计量)以及每个对端路径决策的机制。它与业务无关(ADR 2026-10-06-business-agnostic-scope):只认网段、端口、协议、不透明的标签与规则 ID;主体、组、realm、服务、授权、Quick 地址这类产品概念由使用方在上层编译成通用输入。ns 是使用它的一个产品,nsgw 是另一个。在 NS 的目标架构中它是四层中的第 1 层,其余三层在 ns:

层归属内容
4 功能ns服务发布、反向代理 / 公网入口、子网路由、出口、本地代理、应用(send、第三方)
3 呈现ns覆盖网络在本机的呈现方式:TUN、用户态协议栈监听、DNS、DNS-VIP、SOCKS5 / HTTP / PAC
2 覆盖核心ns身份、身份派生的 IPv6、注册表客户端、对端来源(NSD、Quick、应用会话)与合并后的对端表、策略命名空间、名字、路径阶梯的规则
1 引擎nsplaneWireGuard、传输、路径策略机制、过滤链、netstack、TUN、ACL、4↔6 翻译

每个节点一个引擎。账户模式、Quick 模式与应用只是同一引擎之上的对端来源和呈现选择,“模式”一词不会出现在 nsplane 内部。CLI(nsplane-cli)只是 Linux / macOS 的开发工具。

应用模式的分工(2026-10-02 定,0.11 起用通用术语):nsplane 提供机制:随会话增删的引擎对端、经 nsplane-netstack 与 Splitter 的隧道内连接、按标签的命名空间 ACL(跨命名空间默认拒绝)、定向授权、Pinholes 类命名空间的出向规则、随会话关闭的 pinhole,以及 4↔6 翻译。ns 保留 /rendezvous/v1 会合客户端与应用通道、app:<session> 对端来源的生命周期、按 kind 分发、第三方应用接入、配对与传输的状态机和 relay 客户端载体。应用会话对已有对端复用现有隧道(经对端来源命名空间允许该应用类型的 pinhole),只为未配对的对端安装仅限本次会话的对端。详见应用模式。

现状(2026-10-13):最新发布是 v0.14.0。v0.11.0(2026-10-08)是不兼容版本:ACL 改为通用核心与通用流网关,翻译器改用 EAM 命名,产品概念移到仓库 docs/specs/ 的规格文档;此后冻结在 0.11.x,只按 ns 的请求开过三次例外:v0.12.0(本地多身份)、v0.13.0(按对端的双栈流网关与运行时 ACL 作用域、去 arc-swap)与 v0.14.0(LAN 前缀映射、按本地身份的密钥路由、快照单元读取开销修复)。ns 的账户模式(M4)与 Quick 模式(M5)都只运行在 nsplane 引擎上:2026-10-10 采用 0.11(按迁移说明在 ns 侧实现被移出的语义),2026-10-12 采用 0.12(每个 membership 一个本地身份),2026-10-13 采用 0.13 与 0.14。网关新线(nsgw R2)以 nsplane 引擎为唯一的 WireGuard 实现;网关作为地址固定的 WireGuard 服务端(UDP 与 WebSocket),使用 0.11 新增的 WssServerTransport。

  • 没有控制面、配对、身份、地址分配、注册表或 relay 客户端协议:nsplane 在被告知的路径上承载数据报,并报告哪条路径通过了认证;路径的含义由 ns 决定。
  • 没有产品的身份与策略模型:不解析产品的策略文档,不带产品预设;这些在 docs/specs/ 写成规格,由使用方实现。
  • 不复制、不内嵌任何参考项目(firezone、gotatun、tailscale、NepTUN 等)的代码,无论其许可证如何;只借鉴设计。基础构件用成熟的 crate(bytes、smallvec、zerocopy、smoltcp、aws-lc-rs)。
  • 不做 wg 守护进程产品:UAPI 只为开发工具与 wg show 存在。
  • 不做 DAITA、pcap、替代分配器。
  1. 无 I/O 的核心。 nsplane-core 是一个状态机:输入进、输出出,没有套接字,也没有自己的时钟,由驱动拥有它。测试用假时钟让两个核心互相对话;嵌入方(tokio 驱动、移动端宿主、ns 的 quick-runtime)从自己的事件循环驱动它。
  2. 引擎从不自行改变路径。 PathPolicy 回答”这条消息走哪条路径”和”一条通过认证的消息从这条路径到达,是否采纳”。内置的 StandardRoaming 是标准 WireGuard 漫游;ns 插入自己的阶梯。
  3. 传输是一等公民。 一个引擎可以有多个 Transport(UDP、relay、WSS 载体、候选路径);Path 指明传输与地址,携带 ECN。不用假地址掩饰经 relay 的对端。
  4. 过滤器,而不是特例。 解密到交付、本地读取到加密之间的一切都是 PacketFilter,返回 Accept、Drop 或 Handled:ACL、NAT、4↔6 翻译、节点 L3 门、探测应答、流计量。过滤链按洋葱顺序:从线路侧到本地侧安装,入向按安装顺序、出向按相反顺序执行。
  5. 可选功能不进基础路径。 翻译、端口映射、ACL、流计量、分片阶段、加密工作线程池、netstack 都需要显式启用;未启用的功能不在数据路径上,基础客户端(TUN + UDP + 对端)不为它们多付延迟。卸载(GSO / GRO)默认开启并在不支持时回退。
  6. 零拷贝与有界队列。 PacketBuf(带 32 字节头部预留的 BytesMut)原地加解密;批次是 smallvec;所有通道有界,丢弃计数,网络队列满时反压本地来源。
  7. pma-rust 基线。 edition 2024,MSRV 1.95,工作区 lint 拒绝警告,运行时代码不允许 unwrap / expect / panic,unsafe 只在平台 / FFI 模块并附 SAFETY 说明;没有 CI,just check、just cross、just test-windows、just e2e、just e2e-lib、just e2e-examples 是门禁。
文档内容
architecture.mdcrate 划分、公开接口总览、引擎模型与驱动 trait、反压与统计、可选功能、报文 / 翻译 / MTU、ACL 与应用模式机制、性能、安全基线、作为依赖的使用方式、门禁
仓库 docs/architecture.md现状的完整说明与测量数据(英文)
仓库 docs/design.md目标设计、路线图与决策(英文)
仓库 docs/specs/移出 nsplane 的产品语义的规格与对照数据(英文),供 ns 等使用方实现
0.11 迁移说明从 0.10.x 升级要改什么、可以删什么、行为变化与 ns 待办
rustdoc(cargo doc --workspace --no-deps)每个公开类型、函数与常量的签名和约定
阶段交付状态(2026-10-08)
基线aws-lc-rs 后端、pma-rust lint、协议修复、零拷贝 noise、Windows 设备层、CLI 定位为开发工具已完成
1nsplane-packet、nsplane-core、nsplane 驱动、nsplane-tun、nsplane-uapi、nsplane-e2e、CLI 迁到引擎上、删除上游的 device 层已完成
1b改名为 nsplane、crates/ 目录布局、清理已完成(5578a90)
2多传输、PathPolicy、带 Handled 的过滤链、报文注入、force_handshake、暂停 / 恢复、引擎驱动的定时器已完成
3 + 4按传输的反压、nsplane-netstack、Splitter / MergeSource、nsplane-acl、示例与呈现 × 传输矩阵、ACL 命名空间 / 授权 / 出向规则 / pinhole已完成
5nsplane-nat(翻译、conntrack、端口映射)、分片阶段、洋葱过滤顺序、TUN 与 UDP 卸载、ACL 流钩子、队列高水位、加密工作线程池已完成
5 后续批量核心入口、无工作线程时无锁、精确的发送失败计数、工作线程 / 分片统计、smoltcp 分支已完成(91c4943)
状态按传输的流量计数、EngineHandle::status已完成
ns 请求两轮M4 钩子(inject_outbound_on、inbound_from、observe_every_message、connect_tcp_from…)、数据面搬迁(owns()、重组、UDP 旁路通道、LinkTransport、Nat64Lan、Redirect、force_handshake_on)、每路径 MTU 与 PMTUD、nsplane-wss 的 plain ws / 事件 / 保活已完成
吞吐与 WSS对内核 WireGuard 与 wireguard-go 的基准;引擎快路径;netstack 修复与 TCP 缓冲;nsplane-wss(数据报载体、WsFrame 流客户端与服务端)已完成
ACL 与 L3 门、本地侧按来源的身份标签、分片模式、Ipv6Mode、入站目的地;MapSink / MapSource、pump、pipe、Masquerade、echo_reply_in_place、TunSlot、host_tun已完成
ns 请求第二轮(20261006-0900)本地图原语(Splitter::new_map、MapSink::with_after、SwapSink、AbortSink)、缓冲回收(SharedPacketPool、send_batch_spent)、Windows 服务 TUN(Wintun 固定、独占、遗留网卡替换、双栈 MTU)、netstack PMTU 与尾部丢包探测、datagram_capacity 256、无工作线程时的投递余量(sink 满时反压)、Event::Authenticated 限频、调用方更新的入站目的地址已完成
发布v0.8.0(2026-10-04,首个发布)、v0.9.0(2026-10-04)、v0.10.0(2026-10-05,优化轮:引擎快路径、按对端并行加密、UDP 批处理、TUN 缓冲)、v0.10.1(2026-10-06,修复开加密线程时本地报文在 transmit full 被丢弃)、v0.11.0(2026-10-08,不兼容:业务无关)、v0.12.0(2026-10-12,本地多身份)、v0.13.0(2026-10-12,按对端的 IPv6 流网关、运行时作用域、去 arc-swap;破坏:GatePolicyError::Ipv6 删除,Nat64Lan::new 改收 LanRoutes)、v0.14.0(2026-10-13,LAN 前缀映射、按本地身份的密钥路由、快照单元读取开销修复;全部为增量)已完成
业务无关轮(20261007-0900)去掉 nsplane 中的产品概念:nsplane-acl 的通用核心(标签、规则 ID、策略状态、Flow 判定)与通用流网关 gate 取代身份断言、策略文档与 Node L3 模型(移到 docs/specs/);翻译器 EAM 命名;just check 末尾的产品术语扫描;网关用的 WSS 服务端传输;WsFrame 流载体弃用已完成(2026-10-07),发布于 v0.11.0
发布 v0.11.0 与冻结(2026-10-08)0.11.0 合入 ns 请求第二轮与业务无关轮,破坏公开 API;随后冻结在 0.11.x(ADR 2026-10-08-feature-freeze-0-11):只修 bug,以 patch 版本发布,不加功能、不改 API,使用方需要新 API 时记在它的任务里等待已完成
使用方集成(20261008-0800-consumer-integration)ns 从 v0.10.0 升到 v0.11.0:探测显示 crates/overlay 有 11 处库编译错误(ACL 通用化、来源身份、节点 L3 门、翻译器改名、WsFrame 弃用、统计结构新字段),清单见 0.11 迁移说明;nsgw 在引擎上的重建开始。nsplane 只提供探测、迁移说明与 0.11.x 修复,不改 ns / nsgwns 已完成 0.11(2026-10-10)、0.12(2026-10-12)、0.13 与 0.14(2026-10-13)的采用;nsgw 的重建仍是草案
本地多身份(20261011-0900)一个引擎持有多把静态密钥(本地身份),每个对端绑定一个身份;按 mac1 分派握手发起,所有身份共用一份握手预算、各自有 cookie 秘密;身份可限定在指定传输上以防跨覆盖网探测;单身份时线上字节与开销不变已完成,发布于 v0.12.0(冻结期间的唯一例外)
按对端的流网关(20261012-1800-peer-keyed-gate,GF-1..4)流网关对 IPv4 与 IPv6 一视同仁;GateBinding 不列地址即按 PeerId 绑定该对端任一地址族的全部报文;出向按目的地授权与保留;AclFilter::replace_scopes 在运行时按本地身份原子替换作用域。IPv4-only 策略的行为与开销不变已完成,发布于 v0.13.0(冻结期间第二次例外,应 ns 的请求)
去 arc-swap(20261012-1830-no-arc-swap,AS-1..2)arc-swap 1.9.x 的混合策略在多实例下有可靠性缺陷(跨实例读到别的值、丢更新、非法释放):nsplane-core 新增 std-only 的分片快照单元 SnapshotCell,ACL 引擎、作用域、流网关、翻译器与 NAT64 LAN 路由改用它;Nat64Lan 改收 LanRoutes 句柄已完成,发布于 v0.13.0;安静主机上的完整 A/B 留到发布后
LAN 前缀映射与按本地身份的密钥路由(20261013-1310-lan-prefix-and-identity-routing,LP-1..2、LR-1..2)LanPrefix 的 lan6 可以是 /(96 + IPv4 长度)、低位嵌入另一个 IPv4 网络(RFC 7757 风格),一个远端 LAN 可经本地自己的 IPv4 网段到达,/96 形式不变;允许 IP 按对端的本地身份划分,Core::set_local_sources(identity, sources) 用源地址集合选出向身份,route_from(src, dst) 暴露路由决定,同一远端密钥与地址可同时服务多个身份;单身份或无源集合时开销不变已完成,发布于 v0.14.0(冻结期间第三次例外,应 ns 的请求)
快照单元读取开销(20261013-1330-snapshot-cell-read-cost,OP)SnapshotCell::read 改为按线程缓存的闭包读取:一次引擎快照读取从 0.13.0 的 15.8 ns 降到 2.9 ns(0.12.0 为 8.8 ns),多数 ACL 路径比 0.12.0 更快;残余(出向翻译 +6..7 %、流网关已建立路径 +3..7 %、使用源集合时每往返约 +60 ns)记在任务 20261013-1600-perf-residuals,入向分片路径的 2.7 ns 回退已在 Unreleased 修复已完成,发布于 v0.14.0
6ns 迁移:tunnel-wg 与 quick-runtime 两套数据面都迁到引擎上(在 ns 进行,见新架构 阶段 C、D)两套都已完成(ns M4 2026-10-03、M5 2026-10-04);MG、MR、MT、MD-7 等 ns 请求已在 0.11 提供,ns 侧的删除与 0.11 适配见迁移说明

仓库内未完成的事项:Windows 真机验证(场景见仓库 docs/task/20261006-1700-windows-smoke-for-ns.md,请 ns 的 Windows 冒烟测试执行);把 smoltcp 修复提交上游(待决定);发送端限速的设计(无工作线程时的接收反压已在 0.11 落地);WsFrame 流载体在 ns M6 之后删除;relay v2 节点客户端的归属(建议在 ns 或 nsshared)。阶段 0(ns 固定到 nsplane 的 noise 层,decapsulate 的源地址参数从 IP 改为 SocketAddr)独立于上述阶段,在 ns 当前重构落地后进行。

日期决策
2026-10-01分支基线:ring → aws-lc-rs;没有 CI,本地门禁;unsafe 只在平台 / FFI 模块;接受 Wintun C 库
2026-10-02CLI 是 Linux / macOS 开发工具
2026-10-02完整数据面,无 I/O 核心;Transport 与 PathPolicy 分开;上游 device 层在第 1 阶段后删除
2026-10-02不复制参考项目代码;缓冲区基于 bytes
2026-10-02值类型(PeerId、Path、TransportId、Ecn)放在 nsplane-packet,由 nsplane-core 重新导出
2026-10-02每个 NS 节点一个引擎;4↔6 翻译与分片在引擎内;两套 ns 数据面都迁移
2026-10-02改名为 nsplane,已执行;crate 统一保留 nsplane- 前缀
2026-10-02示例包用真实的 WebSocket over TLS;单端口 relay 使用 WireGuard 未定义的消息类型,标准 WireGuard 对端照常可用(与 ns / nsgw 共享的线上约定,状态 Proposed)
2026-10-02应用模式分工:nsplane 提供对端、隧道内连接、ACL 命名空间、授权、出向规则与 pinhole;ns 负责会合、kind 分发与应用状态机;对已有对端的应用流量只经来源命名空间允许的 pinhole,随会话关闭
2026-10-02ACL 做成按流的钩子:未安装时零开销、按对端旁路、带代际的判定缓存
2026-10-02所有可选功能显式启用,未启用时不在数据路径上;卸载默认开启并可回退
2026-10-03批量核心入口与只取已排队报文的引擎批处理(不等待、无定时器,本地读取受发送余量约束);饱和时接收端 sink 满的丢包被接受,限速另行设计
2026-10-03nsplane-netstack 依赖固定 tag 的 dotns/smoltcp 分支,原有绕过代码删除
2026-10-03每一种数据通道协议(传输与流载体)都在 nsplane 实现,ns 只提供配置与业务决策;重依赖放在可选 crate(nsplane-wss)
2026-10-06业务无关:nsplane 不含产品概念(Node L3、账户、Quick 等),ACL 以通用标签与规则表达;just check 用产品术语扫描把关
2026-10-06被移出的产品语义写成规格文档(docs/specs/)由使用方实现,0.11 不提供兼容层
2026-10-07网关(nsgw)是地址固定的 WireGuard 服务端(UDP 与 WebSocket),不做穿透、不用 relay 客户端;nsplane 提供通用的 WSS 数据报服务端传输,不提供 WsFrame 接受端与按令牌绑定对端
2026-10-08冻结在 0.11.x:只修 bug,patch 版本从 main 发布;新功能、优化、重构与 API 变化等所有者解除冻结;已弃用的 WsFrame 流载体在解冻后才删除
2026-10-11冻结例外一:本地多身份(应 ns 多 membership 的请求),发布为 v0.12.0
2026-10-12冻结例外二:按对端的双栈流网关与运行时作用域、去 arc-swap,发布为 v0.13.0;性能复测推迟到发布后
2026-10-13冻结例外三:LAN 前缀映射、按本地身份的密钥路由(应 ns 的请求)与快照单元读取开销修复,发布为 v0.14.0;性能残余记录后在后续 patch 处理
2026-10-03状态与流量:nsplane 只提供计数(status、transport_stats);速率、指标导出与直连 / relay 标签在 ns

设计参考(只借鉴设计):firezone connlib(无 I/O 节点、I/O 驱动、ip-packet、卸载)、Tailscale tstun / wgengine(过滤钩子、注入、头部预留、零拷贝接收)、mullvad/gotatun(I/O trait、builder)、NordSecurity/NepTUN(加密工作线程)、wireguard-go conn.Bind 与 netbird ICEBind(单一传输对象的替代方案及其代价)、EasyTier(多传输 mesh)、tun2proxy/ipstack 与 netstack-smoltcp(用户态协议栈)。