旁路由 + Tailscale + 同网段节点,LAN 设备 ping 得通远程节点却 ping 不通同局域网的 Tailscale 节点?rp_filter 在作怪,SNAT 一行解决。
前置问题
本文是
Tailscale 子网路由劫持本地流量排查与修复
的后续。解决了路由劫持之后,又发现了一个新问题。
发生了什么
Windows 关闭 Tailscale 后(纯 LAN 设备),ping 其他 Tailscale 节点:
1
2
3
4
| 100.80.0.1 (iStoreOS) ✅ 通
100.80.0.3 (chengdu) ✅ 通
100.80.0.4 (jing) ✅ 通
100.80.0.2 (dbroot) ❌ 不通!卡住不动
|
tracert 显示: 第一跳到 192.168.1.3(iStoreOS),第二跳就超时——说明请求到了,回复丢了。
为什么偏偏是 dbroot?
因为 dbroot 和 Windows 在同一个物理局域网里。
1
2
3
4
5
6
7
8
9
10
11
| 远程节点 (chengdu/jing):
请求路径:Windows → iStoreOS → tailscale隧道 → 远程节点
回复路径:远程节点 → tailscale隧道 → iStoreOS → Windows
路径完全对称 ✅
本地节点 (dbroot):
请求路径:Windows → iStoreOS → tailscale0 → 100.80.0.2 (dbroot) ✅ 到达
回复路径:dbroot 收到后查路由表:
"192.168.1.10?这不就在我隔壁吗?走 LAN 直连!"
dbroot → LAN直连 → 192.168.1.10
路径不对称 ❌
|
回复从 tailscale0 进来,却从 LAN 口出去——Linux 内核的 rp_filter(反向路径过滤) 认为这不正常,直接丢包。
开启 Tailscale 后,Windows 的源 IP 从 192.168.1.10 变成了 100.80.0.6:
1
2
| 源 IP 100.80.0.6 → dbroot 收到 → 查路由表 → "100.80.0.6 走 tailscale0"
→ 回复从 tailscale0 出去 → 入=出=tailscale0 → 对称 ✅
|
源 IP 不同,回复路径不同,就这么简单。
为什么 ZeroTier 没这问题
ZeroTier 是二层虚拟交换机,所有设备共享一个虚拟网段。同网段通信直接走虚拟交换机,不经过网关,没有非对称路径。Tailscale 是三层的,每个节点独立路由,旁路由转发时就埋下了非对称的坑。
怎么修
方案一:在 iStoreOS 上做 SNAT(已实施)
思路: 让 LAN 发往 Tailscale 的流量在 iStoreOS 上转换源地址,对外伪装成 100.80.0.1。这样回复路径永远对称,rp_filter 不触发。
1
2
3
4
5
| 修复前:
源 IP: 192.168.1.10 → dbroot 收到 → 回复走 LAN → 非对称 ❌
修复后:
源 IP: 100.80.0.1 (iStoreOS SNAT) → dbroot 收到 → 回复走 tailscale0 → 对称 ✅
|
实施:
在 iStoreOS 上创建 nftables 规则文件 /usr/share/nftables.d/chain-post/srcnat/30-tailscale-masq.nft:
1
| ip saddr 192.168.1.0/24 oifname "tailscale0" masquerade comment "Tailscale MASQ for LAN"
|
重启防火墙生效:
验证:
1
2
| nft list chain inet fw4 srcnat | grep -i tailscale
# 应该看到那条 masquerade 规则
|
方案二:在 dbroot 上关闭 rp_filter
思路: 直接告诉内核不要检查 tailscale0 接口的反向路径。
1
2
3
4
5
6
| # 临时生效(重启失效)
echo 0 > /proc/sys/net/ipv4/conf/tailscale0/rp_filter
# 或者全关
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.tailscale0.rp_filter=0
|
持久化:
1
2
3
| # 写入 /etc/sysctl.d/99-tailscale.conf
echo "net.ipv4.conf.tailscale0.rp_filter = 0" >> /etc/sysctl.d/99-tailscale.conf
sysctl -p /etc/sysctl.d/99-tailscale.conf
|
两个方案对比
| 方案一:iStoreOS SNAT | 方案二:关 rp_filter |
|---|
| 配置位置 | 只在 iStoreOS 上一处 | 每台同局域网的 Tailscale 节点都要配 |
| 新增节点 | 自动生效 | 新节点需要手动配 |
| 安全性 | 不降低内核安全策略 | 关闭了反向路径检查,降低了安全性 |
| 源 IP 可见性 | 远程节点看到的是 100.80.0.1(iStoreOS) | 远程节点能看到真实源 IP |
| 适用场景 | 不需要区分 LAN 内具体设备 | 需要区分来源(比如日志审计) |
| 维护成本 | 低,一次配完 | 中,每台都要配 |
为什么选了方案一:
这个网络里 LAN 设备访问 Tailscale 节点时,不需要区分"请求来自哪台设备"。就算需要区分,LAN 设备也可以直接通过 Tailscale 客户端用 100.80.0.x 身份访问,那时的源 IP 就是真实的。方案一一次配置解决所有问题,更省心。
为什么不两全其美
两个方案本质上是冲突的——做了 SNAT 就不需要关 rp_filter,关了 rp_filter 就不需要 SNAT。如果两个都做,反而多了一层 NAT 的性能开销,且没有额外收益。
最终效果
修完之后,整个网络的访问逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
| LAN 设备(无 Tailscale)
→ 192.168.1.3 网关 → NAT → 100.80.0.1
→ tailscale → 远程节点
✅ 全通
Windows(有 Tailscale,接口跃点 1000)
→ 192.168.1.x 走物理网卡(跃点 276)
→ 100.80.0.x 走 Tailscale(跃点 1000)
✅ 两不抢
远程 Tailscale 节点
→ 子网路由 192.168.1.0/24 → iStoreOS → LAN
✅ 不变
|
知识点
- rp_filter: Linux 内核的安全机制,检查包的入接口和路由表决定的出接口是否一致,不一致就丢
- 非对称路由: 请求和回复走不同路径,在网络拓扑复杂(旁路由 + VPN + 同网段)时容易触发
- SNAT/MASQUERADE: 不仅能解决"外网访问内网"的问题,也能解决"非对称路由"的问题——本质上都是让回复路径和请求路径一致