问题背景
公司总部到分支机房有一条专线,出口由主路由承担 PPPoE / 策略路由,一台 x86 小主机刷 OpenWrt 做旁路由:只负责科学上网、内网 DNS 分流、部分业务 VLAN 的策略转发,不直接做 NAT 出口。日常几乎零故障,于是变更窗口一拖再拖,固件一直停在 22.03。
最近为了修几个已知 CVE、顺带把 packages 跟上源,运维在周末凌晨把这台旁路由原地升级到 23.05。LuCI 能开、ping 网关正常、无线客户端上网也没异样。结果周一早高峰一到,分支机房访问总部 ERP、视频会议、打印机映射全部超时;总部侧看起来“线路绿着”,专线运营商检测也说丢包率正常——典型的看起来通、业务死。
故障现象
现象切得很整齐,几乎是“按路由路径分流”的:
- 同网段本机互访正常:总部办公区访问总部服务器、分支访问分支本地 NAS 都不受影响。
- 经旁路由策略转发的跨站业务全挂:源地址被 PBR(Policy-Based Routing)导入旁路由的流量,目标是对端机房内网网段时,客户端报超时 / 半开连接;抓包能看到 SYN 出去,几乎等不到 SYN-ACK。
- 旁路由自身上网正常:
ping 1.1.1.1、访问 LuCI、opkg update都成功,容易让人误判“路由没问题,一定是专线/应用层”。 - 主路由策略路由计数在涨:对端回程报文其实到了主路由,说明专线物理层与运营商侧无中断。
- 升级窗口前一周一切正常;变更 diff 表面只有固件大版本,业务配置“原样保留”。
最坑的一点是:升级脚本宣称 config migrated,UCI 里 firewall 段看起来还在,没有明显被清空的规则——所以第一反应几乎都不会去怀疑防火墙默认策略。
排查过程
1. 先定边界:是专线死,还是“某条路径”死
在分支机房挑一台能复现的 PC,同时做三组对照:
- 直连主路由默认网关 → 访问总部指定网段:通
- 强制走旁路由(临时把网关指到旁路由 LAN,或在主路由开一条只匹配这台 PC 的 PBR)→ 不通
- 旁路由上
ping对端网关 /traceroute对端服务器 → 旁路由自己能通
结论立刻收窄:专线与主路由正常,问题出在“经过旁路由转发”的那条路径。
2. 看转发面,不先看应用
旁路由上确认转发已开:
|
|
PBR 表项还在,路由条目也指到正确 next-hop。再在旁路由开 tcpdump:
|
|
入方向到了、出方向或回程丢——很像 filter 的 FORWARD 链在丢包,而不是路由表错。
3. 从 iptables 惯性切到 nftables 现实
旧印象里 OpenWrt 22.03 及更早多是 firewall3 + iptables/fw3。升级后随手敲:
|
|
ruleset 赫然是 firewall4(fw4)基于 nftables 的表。再看 zone 与默认策略:
|
|
关键发现(不同版本措辞略有差异,语义一致):
firewall.@defaults[0].forward在迁移后变成了REJECT/ 等价于 DROP 路径- 旧机依赖的那条“lan → wan / lan → vpn 隐式允许转发”在 fw3 时代靠 zone forward=
ACCEPT顶着 - 升级迁移脚本把 defaults.forward 收紧,同时部分自定义
firewall.forwarding/firewall.rule因选项名变更或匿名 section 顺序变化没有完整落地 - 结果:旁路由作为“纯转发节点”时,本机 INPUT 仍 ACCEPT(所以 LuCI/ping 自己都好),但 FORWARD 默认拒,跨接口业务包被 nft 静默丢弃
再核一条最直接的证据:
|
|
把测试机的一条流固定五元组后,对着 counter 刷一次访问,数字精确跳——根因锁定。
4. 对照升级前备份
幸好升级前有 sysupgrade -b 备份。解开对比:
|
|
旁路由场景下我们从来不只依赖 “wan 口出去的 masq”,还有 lan→专线接口 zone、lan→隧道 zone 的 forwarding。迁移后默认变严 + forwarding 悬空,等于把旁路由从“透明转发器”变成了“只许本机说话的终端”。
解决方案
止血(分钟级)
目标:先让早高峰业务恢复,再谈优雅配置。
- 临时放行旁路由转发(UCI 方式,避免手改 nft 重启丢失):
|
|
- 用业务五元组再打一枪,确认
nftforward 链 counter 不再对应该流增长,SYN-ACK 回来。 - 若仍有个别网段不通,检查 zone 成员网卡是否还在(升级后网卡名/DSA 口名变化会导致 interface 掉出 zone,表现同样是“能 ping 自己不能转”)。
根治
- 按最小权限重建 forwarding 矩阵,不要长期
defaults.forward=ACCEPT:- 只允许
lan → s2s、lan → vpn、必要的s2s → lan回程 - 明确
masq只开在真正做 NAT 的 zone(旁路由很多场景甚至不应开 masq)
- 只允许
- 为自定义 zone 写死名称与 device,避免依赖匿名 section 顺序;升级后跑一次:
|
|
- 变更清单里增加 firewall4 专项:
firewall.@defaults[0].{input,output,forward}- 所有
config forwarding/config rule/config redirect option device/option network是否仍解析得到
- 旁路由升级改为:先在同型号备机刷 23.05 → 导入配置 → 用 iperf/业务探针打转发路径 → 再窗口切换,禁止生产机“原地碰运气”。
根因分析
根因不是“OpenWrt 坏了”,而是 防火墙后端换代带来的安全默认值与配置模型漂移:
| 层级 | 升级前 (fw3/iptables) | 升级后 (fw4/nftables) |
|---|---|---|
| 后端 | iptables + fw3 | nftables + fw4 |
| 默认转发姿态 | 现场长期 ACCEPT / 宽松 zone | 迁移后偏 REJECT/DROP |
| 配置兼容 | 旧 UCI 基本原义 | 部分 option/匿名段迁移不完整 |
| 旁路由特性 | 依赖 FORWARD 放行 | FORWARD 一收紧业务全死、本机仍“看起来健康” |
旁路由的故障面具有迷惑性:管理面(INPUT)健康 ≠ 数据面(FORWARD)健康。主路由、专线、应用三方都可以“证明自己没问题”,于是锅会在多方之间空转,直到有人在旁路由上直接读 nft 的 forward policy 与 counter。
预防措施
- 软路由/旁路由纳入变更分级:大版本跨
firewall3 → firewall4、DSA 改造、内核大版本,一律按“网络割接”做,而不是“打个补丁”。 - 升级验收探针必须覆盖转发路径:
- 至少一条:客户端 → 旁路由 → 对端内网 TCP 业务端口
- 不要只用“能打开 LuCI / 能 ping 外网”当验收
- 配置即代码:
/etc/config/firewall、network、PBR 相关配置进 Git;升级前后自动 diff,forwarding 段数量不得无故减少。 - 监控:对旁路由 snmp/prometheus 采
nftcounter 或转发流量;业务超时告警与“旁路由 forward drop 速率”关联,避免只盯专线运营商链路灯。 - 文档化 zone 矩阵:一张表写清 src zone / dest zone / 是否 masq / 是否依赖自定义 rule,新人半夜不敢“顺手修”。
- 保留 15 分钟回退包:
sysupgrade前离线保存旧固件 + 完整 backup,确认 SERIAL/电源可进 failsafe。
总结
这次故障的完整链条是:
OpenWrt 大版本升级 → 防火墙从 fw3 切到 fw4 → 默认 FORWARD 收紧 + 部分 forwarding 迁移丢失 → 旁路由本机仍通但跨站转发静默丢包 → 专线与主路由“背锅”。
排查口诀可以压成一句:旁路由疑难,先看 FORWARD policy 与 counter,再看专线。
处理上止血可以短暂放开 defaults.forward,根治必须按 zone 矩阵最小放行,并把 firewall4 纳入升级验收清单。家里或公司的 x86 软路由如果还在 “能上网就当健康”,下次跨大版本前务必先在备机验证一条真实业务的转发路径——否则周一早上等你的,一定是“专线没问题,就是业务全死”的灵魂拷问。