Files
Easytier/plans/005-cover-socks5-exit-node.md
T
fanyang 4b654fc56e perf(mpsc): sync send via noop_waker — +90% pps (249K → 474K)
The async fn Future state machine overhead (~1.9us) dominated
MpscTunnelSender::send, while RingSink operations were only ~40ns.

Breakthrough: make send() an async fn that completes synchronously
on the first poll for the direct (ring tunnel) path. Uses
futures::task::noop_waker() to construct a dummy Context, then calls
Sink trait methods (poll_ready, start_send, poll_flush) directly.
RingSink always returns Ready immediately, so the waker is never
invoked and the async fn completes without yielding.

Channel mode (TCP/UDP/WG tunnels) still uses async send_async()
with proper backpressure. Ring tunnels detected via tunnel_info()
type check in PeerConn.

Results (4 threads, 1400B, 15s):
  pps:                   249K → 474K (+90%)
  send_msg_by_ip:        3.53us → 1.67us (-53%)
  send_msg_internal:     2.40us → 502ns  (-79%)
  MpscTunnelSender::send: 1.97us → 144ns  (-93%)

All 207 peers:: tests pass. Netns-requiring tests (three_node,
credential) unchanged (require root).
2026-06-28 21:15:05 +08:00

8.5 KiB
Raw Blame History

计划 005:补齐 SOCKS5 exit-node 集成测试覆盖

执行者说明:按步骤执行本计划。每一步都必须运行验证命令,并确认结果符合预期后再继续。如果触发“STOP 条件”中的任一情况,立即停止并报告,不要自行发挥。完成后更新 plans/README.md 中本计划的状态行,除非 reviewer 明确说明由他们维护索引。

漂移检查(首先运行)git diff --stat 78146d16..HEAD -- easytier/src/tests/three_node.rs easytier/src/tests/mod.rs easytier/src/gateway easytier/src/vpn_portal easytier/src/peers 如果本计划写成后任何范围内文件发生变化,继续前必须对照“当前状态”中的摘录与实时代码;如果不匹配,按 STOP 条件处理。

状态

  • 优先级: P2
  • 工作量: M
  • 风险: MED
  • 依赖: none
  • 类别: tests
  • 计划生成于: commit 78146d16, 2026-06-18

为什么重要

测试文件顶部明确 TODO 指出需要覆盖 socks5 + exit node == self || proxy_cidr == 0.0.0.0/0 的出口节点能力。现有 socks5_vpn_portal 测试只覆盖固定 destination 和 10.1.2.0/24 proxy CIDR,不能证明默认出口路由或 self-exit 场景。完成后,这条核心 VPN routing/use-case 会有 characterization test,后续修改 SOCKS5、proxy CIDR 或 exit-node 行为时不再盲改。

当前状态

  • easytier/src/tests/three_node.rs — 三节点集成测试和 SOCKS5 portal 测试所在文件。
  • easytier/src/gateway/socks5.rseasytier/src/gateway/socks5/dataplane.rs — SOCKS5 gateway implementation;仅在测试失败定位时阅读,默认不修改。
  • easytier/src/peers/peer_ospf_route.rs — proxy CIDR 和 route selection 行为;默认不修改。

当前代码摘录:

// easytier/src/tests/three_node.rs:16
// TODO: 需要加一个单测,确保 socks5 + exit node == self || proxy_cidr == 0.0.0.0/0 时,可以实现出口节点的能力。
// easytier/src/tests/three_node.rs:1753
pub async fn socks5_vpn_portal(
    #[values("10.144.144.1", "10.144.144.3", "10.1.2.4")] dst_addr: &str,
) {
    // ...
    let _insts = init_three_node_ex(
        "tcp",
        |cfg| {
            if cfg.get_inst_name() == "inst3" {
                // 添加子网代理配置
                cfg.add_proxy_cidr("10.1.2.0/24".parse().unwrap(), None)
                    .unwrap();
            }
            cfg
        },
        false,
    )
    .await;
}

仓库约定:这些网络集成测试使用 #[tokio::test]#[serial_test::serial],部分测试需要 Linux network namespace/root capabilities。保持测试 isolated and repeatable;不要让新增测试依赖外部网络。

需要使用的命令

Purpose Command Expected on success
Format cargo fmt --all -- --check exit 0
Lint cargo clippy --all-targets --features full --all -- -D warnings exit 0, no warnings
Targeted test cargo test --package easytier socks5_vpn_portal --features full -- --nocapture --test-threads 1 exit 0; existing and new SOCKS5 tests pass
CI-style archive cargo nextest archive --archive-file tests.tar.zst --package easytier --features full exit 0

如果本地环境缺少 root/network namespace 能力,targeted test 可能失败。此时仍必须确保 compile/archive 通过,并在结果中明确记录环境缺口。

临时目录约定

  • 临时文件、scratch 目录和 disposable worktree 必须放在 $HOME/tmp 下。
  • 如果 $HOME/tmp 不存在且本计划需要临时空间,先创建它。
  • 不要把临时产物放进被修改仓库。

范围

范围内(只能修改这些文件):

  • easytier/src/tests/three_node.rs
  • easytier/src/tests/mod.rs(仅当需要注册 helper/module

范围外(即使看起来相关也不要触碰):

  • Production SOCKS5/gateway/routing code。若测试暴露 bug,停止并报告;不要在本计划里修生产逻辑。
  • Any CI workflow, docs, GUI/Web/frontend。
  • Existing tests unrelated to SOCKS5 portal or exit-node behavior。

Git 工作流

  • Branch: advisor/005-cover-socks5-exit-node
  • Commit message style follows existing conventional commits, for example test: add tests from CONTRIBUTING.md.
  • Do NOT push or open a PR unless the operator instructed it.

步骤

步骤 1:阅读现有 socks5_vpn_portal helper pattern

easytier/src/tests/three_node.rs 中阅读完整 socks5_vpn_portal 测试,特别是如何启动三节点、如何启动 TCP listener、如何通过 tokio_socks::tcp::socks5::Socks5Stream 访问目标地址、如何 cleanup。

不要复制大量代码后分叉;优先抽取小 helper,例如 run_socks5_tcp_echo_case(...),让现有测试和新增测试共享。

验证cargo fmt --all -- --check → exit 0(如果尚未修改,仍应通过)。

步骤 2:新增 0.0.0.0/0 proxy CIDR exit-node case

新增一个 serial async test,命名建议 socks5_vpn_portal_default_ipv4_exit_node。测试应:

  • 使用 init_three_node_ex 创建三节点。
  • 让某个非客户端节点配置 cfg.add_proxy_cidr("0.0.0.0/0".parse().unwrap(), None).unwrap()
  • 通过 SOCKS5 portal 访问一个由测试内部启动的 TCP echo server 地址。
  • 断言 payload round-trip 成功。

测试目标地址必须是本地/测试 namespace 可控地址,不允许依赖公网。

验证cargo test --package easytier socks5_vpn_portal_default_ipv4_exit_node --features full -- --nocapture --test-threads 1 → exit 0;若因权限环境失败,错误必须是环境相关,而非编译或断言失败。

步骤 3:新增 self-exit case 或明确不可测原因

根据 TODO 中的 exit node == self,新增第二个测试,命名建议 socks5_vpn_portal_self_exit_node。它应覆盖 SOCKS5 入口节点同时也是 exit node 的场景。

如果现有 config API 没有清晰方式表达 “exit node == self”,不要猜测配置。先搜索现有 tests 中 exit_nodesadd_proxy_cidrvpn_portal 的用法;如果仍不明确,STOP 并报告需要 maintainer 确认配置语义。

验证cargo test --package easytier socks5_vpn_portal_self_exit_node --features full -- --nocapture --test-threads 1 → exit 0;或 STOP 报告不可测配置语义。

步骤 4:移除或更新 TODO

如果两个场景都已覆盖,将 three_node.rs:16 的 TODO 删除或改成剩余未覆盖场景的精确 TODO。不要删除仍未覆盖的提醒。

验证cargo test --package easytier socks5_vpn_portal --features full -- --nocapture --test-threads 1 → exit 0;现有和新增 SOCKS5 portal tests 通过。

步骤 5:运行完整相关门禁

验证

  • cargo fmt --all -- --check → exit 0。
  • cargo clippy --all-targets --features full --all -- -D warnings → exit 0。
  • cargo nextest archive --archive-file tests.tar.zst --package easytier --features full → exit 0。

测试计划

  • 新增 socks5_vpn_portal_default_ipv4_exit_node:覆盖 proxy_cidr == 0.0.0.0/0
  • 新增 socks5_vpn_portal_self_exit_node:覆盖 SOCKS5 入口节点作为出口节点。
  • 复用现有 socks5_vpn_portal 的 TCP echo/payload pattern,保持 #[serial_test::serial]

完成标准

  • TODO 中提到的 0.0.0.0/0 exit-node 场景有测试覆盖。
  • TODO 中提到的 self-exit 场景有测试覆盖,或计划按 STOP 条件阻塞并说明配置语义缺口。
  • 新测试不依赖公网服务。
  • cargo fmt --all -- --check exits 0。
  • cargo clippy --all-targets --features full --all -- -D warnings exits 0。
  • cargo nextest archive --archive-file tests.tar.zst --package easytier --features full exits 0。
  • 没有修改 production code 或范围外文件。
  • 已更新 plans/README.md 中本计划的状态行。

STOP 条件

  • 新增测试暴露 production bug:不要修生产代码,停止并报告 failing test、命令和错误摘要。
  • self-exit 的配置语义无法从现有代码/tests 中确认。
  • 测试只能通过访问公网验证;这不符合仓库测试隔离要求。
  • 为了让测试通过需要放宽 assertions 或增加 sleeps 超过现有测试风格。

维护说明

  • reviewer 应重点审查测试是否真正走 SOCKS5 portal 和 exit-node route,而不是退化成本地直连。
  • 后续修改 proxy CIDR、exit-node、SOCKS5 dataplane 时,应运行本计划新增的 targeted tests。
  • 本计划只建立测试基线;如果发现 bug,应另写修复计划。