在 OpenWrt 使用 firewall4 添加 SNAT 规则以支持 Tailscale Exit Node 正确回程
现状
- 主路由 192.168.2.1 关闭 DHCP
- OpenWrt 软路由 (192.168.2.2) 提供 DHCP 服务,DHCP 下发的网关为 192.168.2.2, 下发的 DNS 为 192.168.2.2
- OpenWrt 软路由 (192.168.2.2) 安装 Tailscale,已设置 Exit Node + Subnet Route 192.168.2.0/24
- 网络 -> 防火墙 -> 区域:已存在 tailscale -> lan 的 IP 动伪装(Masquerading)
- 网络 -> 接口:只有 lan 和 tailscale 两个接口
问题
- 从公网连上 Tailscale 并指定 OpenWrt 为 Exit Node 后,只能访问 192.168.2.2,192.168.2.0/24 网段下的其他主机和公网全部超时
解决
使用 tcpdump 抓包发现是回程的包没有正确返回,确定是 SNAT 的问题。
手动加入 iptables -t nat -I POSTROUTING -s 100.64.0.0/10 -o br-lan -j MASQUERADE
之后问题解决, LAN 和公网均可借助 OpenWrt 访问
但是我使用的 openwrt 比较新,已经不再推荐使用 iptables 管理防火墙,所以还要寻找一下替代方案。
在 o3 的帮助下,终于找到可行的做法,操作步骤如下
- 登录 LuCI → 「网络 ▸ 防火墙」
这里会看到 概览 / 端口转发 / 流量规则 / NAT 规则 四个标签。
“NAT 规则”正是专门用来写 SNAT/MASQUERADE 的页面。 - 切到 「NAT 规则」 标签页,点页面底部 「添加」。
会弹出一个带 3 个子标签的表单:常规设置 / 高级设置 / 时间限制。
这些字段的名字与含义可在官方手册中对应到 UCI 配置项,
其中“出口设备”“动作”等就是我们需要填的要素。 常规设置 里填写
字段 选择 / 填写值 说明 名称 CGNAT-masq
任意易辨识的名字 限制地址族 IPv4
只处理 IPv4 协议 任意
与 iptables 命令里的“全部协议”一致 出口区域 未指定
(或保持默认)MASQUERADE 不要求指定 zone 源地址 100.64.0.0/10
对应 -s
参数动作 MASQUERADE – 自动改写为出口接口 IP
与 iptables 目标一致 点击 高级设置 子标签,再设置
字段 选择值 出口设备 br-lan
对应 -o br-lan
;这个字段只在高级设置里出现其他保持默认即可。
(如果你的接口名字不同,请按实际桥接口名选取。)config nat option name 'CGNAT-masq' option family 'ipv4' option hook 'postrouting' list match 'ip saddr 100.64.0.0/10' list match 'oifname "br-lan"' option target 'MASQUERADE'
firewall4 随后会把它转译为 nft 规则
ip saddr 100.64.0.0/10 oifname "br-lan" masquerade
写入 table inet fw4 chain srcnat,效果与原 iptables 指令完全一致。
生效检查
SSH 到路由器执行:
nft list table inet fw4 | grep 100.64
应能看到:
ip saddr 100.64.0.0/10 oifname "br-lan" masquerade