Tailscale 子网路由:打通两个异地局域网的完整指南

_

家里和机房的设备想互相访问?

Tailscale 的子网路由功能可以做到。本文从零开始,带你一步步配置,并附上实战中踩过的每一个坑。

背景

家里的网络是光猫拨号,下面挂了一台树莓派,子网是 '10.0.1.0/24'。机房那边是 iKuai 软路由 + PVE 虚拟化,跑着一台 Linux VM,子网是 '10.0.0.0/24'。

两台机器通过 Tailscale 已经可以互访了,但问题在于:子网内的其他设备不行。比如家里的手机连不上机房的 NAS,机房的 Windows 虚拟机也访问不了家里的打印机。

Tailscale 恰好提供了「子网路由」(Subnet Router)功能来解决这个问题。思路很简单:让树莓派和 Linux VM 分别充当各自子网的网关,把对端子网的流量通过 Tailscale 隧道转发过去。

网络拓扑

AI Generated Image

两台 Tailscale 节点各自「宣告」自己所在子网的路由,Tailscale 负责把路由同步到所有节点。再加上几条 iptables NAT 规则,两个子网内的所有设备就能互相访问了。

第一步:开启 IP 转发

子网路由本质上就是转发——树莓派收到一个目标 IP 是 `10.0.0.x` 的数据包,它需要判断"这不是给我的,但我可以帮忙转过去"。这个行为由内核参数 `net.ipv4.ip_forward` 控制,默认为 0(关闭)。

两台设备都执行:

sysctl -w net.ipv4.ip_forward=1

持久化(重点)

如果只是执行上面那条命令,重启就没了。写进 `/etc/sysctl.conf` 理论上可以,但实际上在树莓派这类设备上,dhcpcd 或 systemd-networkd 经常会覆盖这个值,导致重启后 `ip_forward` 又变回 0——这是我们踩的第一个坑。

最稳妥的方式是用 systemd 服务来兜底:

cat > /etc/systemd/system/ip-forward.service << 'EOF'

[Unit]

Description=Enable IP Forwarding

After=network.target

[Service]

Type=oneshot

ExecStart=/usr/sbin/sysctl -w net.ipv4.ip_forward=1

RemainAfterExit=yes

[Install]

WantedBy=multi-user.target

EOF

systemctl daemon-reload

systemctl enable --now ip-forward.service

验证:

sysctl net.ipv4.ip_forward 必须 = 1

第二步:宣告子网路由

现在让 Tailscale 知道"这台机器背后还有一个子网"。

在树莓派(宣告 10.0.1.0/24):

tailscale up --accept-routes --advertise-routes=10.0.1.0/24

在Linux VM(宣告 10.0.0.0/24):

tailscale up --accept-routes --advertise-routes=10.0.0.0/24

常见坑:tailscale up 参数拒绝

如果你之前配置过出口节点(exit node)或其他路由,`tailscale up` 会报错:

Error: changing settings via 'tailscale up' requires mentioning all non-default flags.

这是因为 `tailscale up` 不是增量式的——每次执行都相当于重新设置,必须带上所有已有的非默认参数。先查看当前状态:

tailscale debug prefs | grep -i AdvertiseRoutes -A5

假设输出显示之前还有 `--advertise-exit-node` 和旧路由 `192.168.1.0/24`,那么命令就需要写成:

tailscale up --advertise-exit-node --accept-routes --advertise-routes=10.0.1.0/24,192.168.1.0/24

第三步:后台批准路由

这一步不做,前面全白干。

宣告只是告诉 Tailscale "我想帮你转发这些子网",但需要管理员在后台批准后才真正生效。

1. 打开 Tailscale Admin Console

2. 找到两台设备,点击右侧 `...` → Edit route settings

3. 勾选对应的子网路由,保存

批准之前,Tailscale 的 WireGuard 层根本不知道哪个 peer 负责哪个子网,所有跨子网流量会被静默丢弃——没有任何报错,就是不通。

第四步:配置 iptables NAT 规则

路由批准后,Tailscale 节点之间已经能转发数据包了。但还有一个问题:子网内的普通设备(比如连在光猫下的手机)并不知道 Tailscale 的存在,它只知道"要访问 10.0.0.x,找网关 10.0.1.1"。

所以我们需要在 Tailscale 节点上做 NAT,让经过它的流量看起来像是它自己发出的。

数据包路径:

子网设备发出的包 → 到达树莓派 LAN 口 → SNAT(改源 IP 为树莓派 Tailscale IP)→ tailscale0 → WireGuard 隧道 → tailscale0 → 到达 Linux VM → MASQUERADE(伪装成 VM 的 LAN IP)→ VM 的 LAN 口 → 目标设备

树莓派上的规则

MASQUERADE:把从对端过来的流量伪装成树莓派自己

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -o eth0 -j MASQUERADE

SNAT:发往机房子网的流量,改源 IP 为树莓派的 Tailscale IP

iptables -t nat -A POSTROUTING -d 10.0.0.0/24 -o tailscale0 -j SNAT --to-source 100.102.82.44

Linux VM 上的规则

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -o ens192 -j MASQUERADE

iptables -t nat -A POSTROUTING -d 10.0.1.0/24 -o tailscale0 -j SNAT --to-source 100.123.165.97

⚠️ 这里有一个最容易踩的坑

注意 MASQUERADE 规则里匹配的是 `-s 100.64.0.0/10`,不是 `-s 10.0.0.0/24`。

直觉上应该匹配"对端子网"的 IP 段,但实际上 iptables 的 POSTROUTING 链是按顺序执行的——SNAT 规则排在前面,已经把数据包的源 IP 改成了 Tailscale IP(100.x.y.z),等到 MASQUERADE 规则匹配时,源 IP 早就不在 10.0.0.0/24 里了。

如果你写 `-s 10.0.0.0/24`,这条规则的计数器永远是 0,流量到了这里直接跳过,然后被丢弃。

验证 NAT 规则是否生效:

iptables -t nat -L POSTROUTING -n -v

SNAT 和 MASQUERADE 的计数器都应该在递增。如果 MASQUERADE 是 0,检查匹配的源 IP 段。

第五步:解决 ping 通但 TCP 不通的问题

如果你配置完发现 ping 跨子网没问题,但 SSH 连不上、HTTP 打不开——恭喜,你遇到了 WireGuard 的 MTU 问题。

原因:

- 以太网默认 MTU:1500 字节

- WireGuard 隧道 MTU:1280 字节

- TCP 三次握手 SYN 包携带的 MSS:1460 字节

SYN 包的 MSS=1460 加上 IP 头和 TCP 头,实际数据包超过 1500 字节。虽然还没超过以太网 MTU,但一旦进入 WireGuard 隧道(MTU=1280),就太大了。WireGuard 不会产生 ICMP fragmentation-needed 消息,所以这些大包被静默丢弃。ICMP ping 只有 84 字节,不受影响,所以看起来 ping 通但 TCP 不通。

修复:TCP MSS 钳制,在两台设备上都执行,强制降低经过 tailscale0 的 TCP SYN 包中的 MSS 值:

出方向:

iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o tailscale0 -j TCPMSS --clamp-mss-to-pmtu

入方向:

iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -i tailscale0 -j TCPMSS --clamp-mss-to-pmtu

加上之后,TCPMSS 会自动把 MSS 钳制到 PMTU(Path MTU),TCP 连接就能正常建立了。

第六步:持久化

iptables 规则存在内存中,重启就没了。务必保存:

apt-get install -y iptables-persistent

netfilter-persistent save

可选:手动路由兜底

Tailscale 批准路由后会自动注入路由表,但加一条手动路由当保险也没什么坏处:

树莓派上:ip route add 10.0.0.0/24 dev tailscale0

Linux VM 上:ip route add 10.0.1.0/24 dev tailscale0

注意 `tailscale0` 是点对点接口,路由语法是 `dev tailscale0`,不能写 `via 100.x.y.z`——写了会报 `Nexthop has invalid gateway`。

验证连通性

按层级验证,从底层到上层:

第一层:Tailscale 原生连通性

tailscale ping 100.123.165.97 树莓派 → VM

tailscale ping 100.102.82.44 VM → 树莓派

`tailscale ping` 走 WireGuard 原生协议,不受 ICMP 防火墙限制,比普通 ping 可靠得多。

第二层:子网级 ICMP

ping -c 3 10.0.0.1 ping 机房网关

ping -c 3 10.0.1.1 ping 家里网关

第三层:TCP 连接

ssh user@10.0.0.110 SSH 到对端设备

curl http://10.0.1.4:8080 HTTP 测试

抓包终极诊断:如果某层不通,在接收端抓包定位:

tcpdump -i tailscale0 -n icmp

然后从发送端 ping,观察

排查清单

遇到问题按这个顺序查,不要跳:

1. `tailscale ping <对端TS-IP>` → 不通说明 Tailscale 本身有问题

2. `tailscale status` → 对端在线吗?

3. `tailscale debug prefs` → 路由宣告了吗?

4. Admin Console → 路由批准了吗?

5. `sysctl net.ipv4.ip_forward` → 是 1 吗?

6. `ip route show | grep <对端子网>` → 路由表里有吗?

7. `iptables -t nat -L POSTROUTING -n -v` → 计数器在涨吗?

8. `iptables -t mangle -L FORWARD -n -v` → MSS 规则生效了吗?

踩坑记录

以下每条都是真实踩过的,花了不少时间排查。

坑1:MASQUERADE 匹配了错误的源 IP

写了 `-s 10.0.0.0/24`,计数器永远是 0。因为 SNAT 先执行,源 IP 已经变成了 Tailscale 的 100.x.y.z。改成 `-s 100.64.0.0/10` 解决。

坑2:ping 通但 TCP 不通

这是 WireGuard MTU 1280 和以太网 MTU 1500 不匹配导致的。加 TCPMSS 钳制解决。

坑3:重启后 ip_forward 变回 0

`/etc/sysctl.conf` 写了没用,被 dhcpcd 覆盖。用 systemd oneshot 服务兜底。

坑4:tailscale up 报参数错误

`tailscale up` 不是增量式的,每次必须带上所有非默认参数。先用 `tailscale debug prefs` 查看。

坑5:路由语法错误

`ip route add 10.0.0.0/24 via 100.123.165.97 dev tailscale0` 报 `Nexthop has invalid gateway`。tailscale0 是点对点接口,去掉 `via`。

坑6:路由未批准,静默丢包

所有配置看起来都对,但跨子网不通。tcpdump 显示包发出去了,对端收不到。去 Admin Console 一看,路由还没批。

坑7:iptables 规则重启后丢失

忘了 `netfilter-persistent save`,重启后规则全没,一切不通。

Hello, Here is Artolina web! 2026-07-07
近期摄影作品 2026-07-11

评论区