家里和机房的设备想互相访问?
Tailscale 的子网路由功能可以做到。本文从零开始,带你一步步配置,并附上实战中踩过的每一个坑。
背景
家里的网络是光猫拨号,下面挂了一台树莓派,子网是 '10.0.1.0/24'。机房那边是 iKuai 软路由 + PVE 虚拟化,跑着一台 Linux VM,子网是 '10.0.0.0/24'。
两台机器通过 Tailscale 已经可以互访了,但问题在于:子网内的其他设备不行。比如家里的手机连不上机房的 NAS,机房的 Windows 虚拟机也访问不了家里的打印机。
Tailscale 恰好提供了「子网路由」(Subnet Router)功能来解决这个问题。思路很简单:让树莓派和 Linux VM 分别充当各自子网的网关,把对端子网的流量通过 Tailscale 隧道转发过去。
网络拓扑

两台 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`,重启后规则全没,一切不通。