headscale系列:Tailscale 4via6 在 Windows 子网路由上的 ICMP 行为说明

Tailscale 4via6 在 Windows 子网路由上的 ICMP 行为说明

(为什么能用“合成 IPv6”访问 Web,却 ping -6 不通)

1. 场景与现象

  • 拓扑:
    A、B 两台 Windows 电脑安装 Tailscale;B 同时作为子网路由器(Subnet Router)。
    C 是 OpenWrt 设备,挂在 B 所在现场的内网。

  • 配置:已开启 4via6,B 宣告(advertise)一个 IPv6 子网,并在 Headscale/后台审批通过。

  • 现象:

    • 从 A 能用 “合成的 IPv6 地址” 打开 C 的 Web 服务(HTTP/HTTPS 正常)。
    • 但从 A 执行 ping -6 <C-的IPv6> 不通。抓包能看到 Echo Request 发出,但收不到回包;在 B 上用 Wireshark 看,没有 ICMP 包进入

2. 关键概念:4via6 与“合成 IPv6”

Tailscale 的 4via6 会把内网 IPv4 目标编码进一个保留的 ULA 段(如 fd7a:115c:a1e0::/48),形成合成 IPv6 地址
例:c0a8:0601 对应 192.168.6.1,于是会看到类似
fd7a:115c:a1e0:b1a0:7:c0a8:0601 这样的目的地址。

  • TCP/UDP 流量:Tailscale 会把发往这些“合成 IPv6”的包转换并送到真实的 IPv4 终端,所以浏览器/SSH/Modbus-TCP 等能正常工作。
  • ICMPv6:在 Windows 客户端 上,当前路径不会把 ICMPv6 Echo 转成 ICMPv4 Echo,因此 ping -6 不会得到回应——包也不会被转发到 B 或 C。

3. 根因(简洁版)

  1. Windows + 4via6 的协议支持面
    只对 TCP/UDP 做 IPv6→IPv4 转换;ICMPv6 不在转换范围内,所以 ping -6 失败,而 Web 正常。

  2. Windows 子网路由对“原生 IPv6 子网”的转发能力有限
    即便 B 宣告了 IPv6 子网,Windows 作为子网路由在 IPv6 转发与邻居发现处理上并不如 Linux 完整稳定,导致跨站点的 ICMPv6 转发经常不可用

    换成 Linux/OpenWrt 当子网路由后,同样配置下 ping -6 通常即可恢复正常。

4. 结论一句话

在 Windows 子网路由 + 4via6 的组合中,HTTP/UDP 能走,但 ICMPv6 不会被转换/转发,所以 ping -6 不通是预期行为;Linux 子网路由可以正常转发 ICMPv6。

5. 可选解决/替代方案

方案 A(最省事):用 IPv4 做连通性检查

  • 改用 ping 192.168.6.x(ICMPv4),或
    Test-NetConnection 192.168.6.x -Port 80/22 验证端口。
  • 适合“只想知道在线与否”的场景;无需改动现场。

方案 B(推荐):把 子网路由器 换成 Linux/OpenWrt

在 B 侧放一台小 Linux/OpenWrt 盒子(可以是现有 OpenWrt 路由或小主机/容器)作为 Tailscale 子网路由:

1
2
3
4
5
6
7
8
9
# 1) 开启 IPv6 转发
sysctl -w net.ipv6.conf.all.forwarding=1
# 持久化:/etc/sysctl.conf 里设置 net.ipv6.conf.all.forwarding=1

# 2) tailscaled 启动并宣告 IPv6 前缀
tailscale up --advertise-routes=<你的IPv6前缀>/64 --accept-dns=false
# 按需添加其它前缀,或配合 --accept-routes

# 3) 在 Headscale/管理端批准这些 routes

完成后,从 A 直接 ping -6 <C 的**原生** IPv6>,ICMPv6 会被正常转发;B 上能抓到往返的 ICMPv6。

方案 C(最干净):让 C 直接加入 Tailnet

在 OpenWrt 上安装 Tailscale,让 C 成为独立节点(拥有自己的 fd7a:... 节点地址):

1
2
3
4
5
opkg update
opkg install tailscale
/etc/init.d/tailscaled enable
/etc/init.d/tailscaled start
tailscale up

A 直接 ping -6 <C 的 Tailscale IPv6>,端到端走 Tailscale,绕开子网路由的限制。

方案 D(保留 Windows,但想要 ping -6

在 B 侧增加小型 Linux NAT64/CLAT(例如 Jool EAMT),专门把发往 fd7a:115c:a1e0::/96ICMPv6⇄ICMPv4 做转换;并在 B/网关上加静态路由把该前缀指向这台转换器。

该方案能打通 ping -6,但复杂度与维护成本较高,一般不如方案 B/C。

6. 快速排障自检

  • 能开 Web,ping -6 不通:90% 是 ICMPv6 未转换/未转发的已知限制。

  • 在 A 执行:

    • ping 192.168.6.x(C 的 IPv4):若可达,证明 4via6 通路正常,只是 ICMPv6 不支持。
    • route print -6Get-NetRoute -AddressFamily IPv6:若看到目的合成地址不断触发 Neighbor Solicitation 而无回应,这是主机把目标当“链路内”,但 Tailscale 不会为其应答/转发 ICMPv6 的典型表现。
  • 在 B 抓包(Tailscale/Wintun 接口):若看不到来自 A 的 ICMPv6,则说明包没被交给子网路由(仍印证“Windows 不处理 ICMPv6 转换”)。

7. 常见问答

  • 为什么浏览器能打开?
    因为 TCP(和大多数 UDP)会被 4via6 转换并转发到 IPv4 终端;ICMPv6 不在这个转换面里。
  • UDP 是否也能走?
    能(取决于具体协议/端口与策略),多数 UDP 通过 4via6 没问题;问题集中在 ICMPv6。
  • 以后 Windows 会支持吗?
    以当前版本看没有现成开关可启用。若对 ICMPv6 必须要,建议按方案 B/C 处理。

推荐落地选型

  • 想保留 Windows 当子网路由:接受“不能 ping -6”,用 IPv4 ping/端口探测替代。
  • 需要完整 IPv6(含 ping -6:把子网路由迁到 Linux/OpenWrt;或让终端设备直接跑 Tailscale。