Skip to content

fix: support IPv6-disabled Windows (error 1168) - #215

Merged
vnt-dev merged 2 commits into
vnt-dev:mainfrom
btsd321:fix/ipv6-disabled-windows
Oct 28, 2026
Merged

vnt-dev merged 2 commits into
vnt-dev:mainfrom
btsd321:fix/ipv6-disabled-windows

Conversation

@btsd321

@btsd321 btsd321 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

问题

Windows 系统通过 DisabledComponents = 0xFFFFFFFF 完全禁用 IPv6 时(企业安全基线常见),VNT Desktop 的 TUN/TAP 模式 100% 复现 创建 TUN 失败: 找不到元素 (os error 1168),导致组网无法启动。

根因

IPv6 被禁用后,新建的 Wintun 网卡没有 IPv6 的 IP 接口行(GetIpInterfaceEntry(AF_INET6) 返回 ERROR_NOT_FOUND)。VNT 有三处硬依赖 IPv6 可用:

  1. builder.metric(1):tun-rs 2.8.12 的 set_interface_metric() 遍历 [AF_INET, AF_INET6],IPv6 的 GetIpInterfaceEntry 失败即 ? 返回错误,导致整个 build_async() 失败。

  2. 虚拟 IPv6 地址/路由:set_network() 调用 add_address_v6() 和 VirtualIpv6Routes::apply(),CreateUnicastIpAddressEntry 在没有 IPv6 接口行时返回 1168。

  3. 30 秒启动超时:加固企业机器上 Wintun PnP 安装 + NDIS 过滤器绑定实测可达 30~45 秒,30 秒上限会把"刚好完成"的启动误杀。

修复

1. metric 从 build 阶段移到建卡后(vnt-core/src/tun/general.rs)

删除 builder.metric(1),改为建卡后调用 dev.set_metric(1),失败只记 debug 日志。IPv4 跃点仍然生效(tun-rs 先处理 AF_INET),IPv6 不可用时忽略错误。与同函数中 set_mtu 的处理风格一致。

2. 虚拟 IPv6 视为可选增强(vnt-core/src/tun/general.rs)

  • DeviceState 新增 ipv6_unavailable: bool,首次 add_address_v6 失败时置位
  • 置位后跳过虚拟 IPv6 地址添加、旧地址清理和路由安装
  • 新增 is_ipv6_unavailable() 判断错误码:Windows 用 raw_os_error(1168/50/10047),Unix 用 ErrorKind::AddrNotAvailable
  • 其他错误仍照常上抛为 设置虚拟IPv6失败,不掩盖真实故障

3. 启动超时从 30s 放宽到 120s(vnt-web/src/service_http.rs)

提取 START_READY_TIMEOUT 常量,从 30 秒放宽到 120 秒,保留上限防止真卡死。建卡前补一条进度日志 "等待注册并创建虚拟网卡"。

实测验证

修复后在故障机器(Windows 11 24H2, IPv6 完全禁用)上验证:

注册成功 10.26.0.2/24
系统未启用 IPv6,跳过虚拟 IPv6 配置: ... (os error 1168)    ← WARN,只出现一次
创建并应用 tun 虚拟网卡成功
实例状态: running
ping 10.26.0.3 → 7~10 ms ✅

建议上游修复 tun-rs

tun-rs 的 set_interface_metric 在某个地址族不存在时不应让整个 metric 设置失败:

match unsafe { GetIpInterfaceEntry(&mut row) } {
    NO_ERROR => {}
    ERROR_NOT_FOUND => continue,  // 跳过缺失的地址族
    code => return win_result(code),
}

改动范围

  • vnt-core/src/tun/general.rs:+79/−32
  • vnt-web/src/service_http.rs:+13/−1

不改动配置格式、IPC 协议、错误文案,无兼容性影响。启用 IPv6 的机器行为完全不变。

Three fixes for machines with IPv6 disabled via DisabledComponents=0xFFFFFFFF:

1. Move metric(1) from builder to post-build: tun-rs set_interface_metric
   iterates AF_INET then AF_INET6; GetIpInterfaceEntry(AF_INET6) returns
   ERROR_NOT_FOUND(1168) when IPv6 is disabled, failing the entire
   build_async(). Now set_metric is called after build, ignoring IPv6 errors.

2. Make virtual IPv6 optional: add_address_v6 and VirtualIpv6Routes fail
   with 1168 on IPv6-disabled machines. Added ipv6_unavailable flag to
   DeviceState; first failure sets the flag and skips all subsequent IPv6
   configuration with a warning, without blocking IPv4 networking.

3. Increase start timeout from 30s to 120s: hardened enterprise machines
   with multiple NDIS filter drivers (ESET, Sangfor VPN, etc.) can take
   30-45s for Wintun PnP install + filter binding. The old 30s limit
   killed successfully-completed startups. Added a progress log line.
@vnt-dev

vnt-dev commented Oct 28, 2026

Copy link
Copy Markdown
Owner

测试用例没有改,可以改一下吗

ErrorKind::AddrNotAvailable maps to EADDRNOTAVAIL, not EAFNOSUPPORT, so the previous Unix check missed the intended error and matched an unrelated one. Use libc::EAFNOSUPPORT for the platform value and cover both Windows and Unix branches with unit tests.
@btsd321

btsd321 commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

感谢 review。已补充 is_ipv6_unavailable 的单元测试(覆盖 Windows 与 Unix 分支),并修正了 Unix 下的判定:改用 libc::EAFNOSUPPORT。原先的 ErrorKind::AddrNotAvailable 对应的是 EADDRNOTAVAIL,含义不同。

关于 Linux/macOS CI 上失败的 4 个测试(identical_snapshot_needs_nothing_and_password_flags_rebuild、four_serverless_nodes_form_a_multihop_loopback_graph、ipv6_tun_tap_plain_transports、ipv6_tun_tap_encrypted_compressed_fec_transports):它们都卡在 InternalNatInbound::create → start_icmp_nat 创建 raw ICMP socket 这一步,报 Operation not permitted。GitHub 的 Linux/macOS runner 不是 root,所以会失败;Windows runner 是管理员,所以能通过。这几个测试是在 8077e34 引入的,与本 PR 改动的代码无关。

想请您确认倾向哪种处理方式:
A. 只改测试:在测试开头探测能否创建 raw ICMP socket,不能则跳过。生产代码不变。
B. 改生产代码:start_icmp_nat 仅在 PermissionDenied 时记录警告并降级(不提供 ICMP NAT),其他错误照常返回。这样测试在 CI 上能真正跑起来,也让无网卡模式在无特权下可以启动。会作为单独 commit 提交。

@vnt-dev
vnt-dev merged commit 18d9aea into vnt-dev:main Oct 28, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants