OpenWrt旁路由升级后内网专线业务为何全断?一次firewall4默认策略变更的排查实录

OpenWrt 从 firewall3 升到 firewall4 后,旁路由 FORWARD 默认策略变 DROP,专线回程与东向访问被静默丢弃。

问题背景

公司总部到分支机房有一条专线,出口由主路由承担 PPPoE / 策略路由,一台 x86 小主机刷 OpenWrt 做旁路由:只负责科学上网、内网 DNS 分流、部分业务 VLAN 的策略转发,不直接做 NAT 出口。日常几乎零故障,于是变更窗口一拖再拖,固件一直停在 22.03。

最近为了修几个已知 CVE、顺带把 packages 跟上源,运维在周末凌晨把这台旁路由原地升级到 23.05。LuCI 能开、ping 网关正常、无线客户端上网也没异样。结果周一早高峰一到,分支机房访问总部 ERP、视频会议、打印机映射全部超时;总部侧看起来“线路绿着”,专线运营商检测也说丢包率正常——典型的看起来通、业务死

故障现象

现象切得很整齐,几乎是“按路由路径分流”的:

  1. 同网段本机互访正常:总部办公区访问总部服务器、分支访问分支本地 NAS 都不受影响。
  2. 经旁路由策略转发的跨站业务全挂:源地址被 PBR(Policy-Based Routing)导入旁路由的流量,目标是对端机房内网网段时,客户端报超时 / 半开连接;抓包能看到 SYN 出去,几乎等不到 SYN-ACK。
  3. 旁路由自身上网正常ping 1.1.1.1、访问 LuCI、opkg update 都成功,容易让人误判“路由没问题,一定是专线/应用层”。
  4. 主路由策略路由计数在涨:对端回程报文其实到了主路由,说明专线物理层与运营商侧无中断。
  5. 升级窗口前一周一切正常;变更 diff 表面只有固件大版本,业务配置“原样保留”。

最坑的一点是:升级脚本宣称 config migrated,UCI 里 firewall 段看起来还在,没有明显被清空的规则——所以第一反应几乎都不会去怀疑防火墙默认策略。

排查过程

1. 先定边界:是专线死,还是“某条路径”死

在分支机房挑一台能复现的 PC,同时做三组对照:

  • 直连主路由默认网关 → 访问总部指定网段:
  • 强制走旁路由(临时把网关指到旁路由 LAN,或在主路由开一条只匹配这台 PC 的 PBR)→ 不通
  • 旁路由上 ping 对端网关 / traceroute 对端服务器 → 旁路由自己能通

结论立刻收窄:专线与主路由正常,问题出在“经过旁路由转发”的那条路径。

2. 看转发面,不先看应用

旁路由上确认转发已开:

1
2
3
sysctl net.ipv4.ip_forward
# = 1
ip rule; ip route show table all | head

PBR 表项还在,路由条目也指到正确 next-hop。再在旁路由开 tcpdump

1
2
3
4
tcpdump -ni eth0 host <分支客户端IP> and host <总部ERP>
# 能看到进方向 SYN
tcpdump -ni eth1 host <总部ERP>
# 出方向有时有、有时没有;回程 SYN-ACK 经常在 br-lan 侧消失

入方向到了、出方向或回程丢——很像 filter 的 FORWARD 链在丢包,而不是路由表错。

3. 从 iptables 惯性切到 nftables 现实

旧印象里 OpenWrt 22.03 及更早多是 firewall3 + iptables/fw3。升级后随手敲:

1
2
3
iptables -L -n -v
# 空的,或提示用 nft
nft list ruleset | less

ruleset 赫然是 firewall4(fw4)基于 nftables 的表。再看 zone 与默认策略:

1
2
3
uci show firewall | grep -E 'forward|input|output|name'
fw4 print | head -100
nft list chain inet fw4 forward

关键发现(不同版本措辞略有差异,语义一致):

  • 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 静默丢弃

再核一条最直接的证据:

1
2
3
nft list chain inet fw4 forward | grep -E 'policy|drop|reject'
# policy drop
# 计数器在 counter packets 上持续增加,与业务超时时间线吻合

把测试机的一条流固定五元组后,对着 counter 刷一次访问,数字精确跳——根因锁定。

4. 对照升级前备份

幸好升级前有 sysupgrade -b 备份。解开对比:

1
2
3
4
5
6
7
8
# 旧
firewall.@defaults[0].forward='ACCEPT'   # 或 zone lan forward=ACCEPT + 明确 forwarding
# 新
firewall.@defaults[0].forward='REJECT'
# 旧存在的 config forwarding
#   option src 'lan'
#   option dest 'offload' / 'vpn' / 自定义 zone
# 新配置中对应 section 丢失或 dest zone 名变更后悬空

旁路由场景下我们从来不只依赖 “wan 口出去的 masq”,还有 lan→专线接口 zone、lan→隧道 zone 的 forwarding。迁移后默认变严 + forwarding 悬空,等于把旁路由从“透明转发器”变成了“只许本机说话的终端”。

解决方案

止血(分钟级)

目标:先让早高峰业务恢复,再谈优雅配置。

  1. 临时放行旁路由转发(UCI 方式,避免手改 nft 重启丢失):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
uci set firewall.@defaults[0].forward='ACCEPT'
# 或更稳妥:只补齐业务需要的 zone forwarding,而不是永久放开 defaults
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].dest='wan'      # 按实际 zone 名
uci add firewall forwarding
uci set firewall.@forwarding[-1].src='lan'
uci set firewall.@forwarding[-1].dest='s2s'      # 专线/隧道 zone 名以现场为准
uci commit firewall
/etc/init.d/firewall restart
  1. 用业务五元组再打一枪,确认 nft forward 链 counter 不再对应该流增长,SYN-ACK 回来。
  2. 若仍有个别网段不通,检查 zone 成员网卡是否还在(升级后网卡名/DSA 口名变化会导致 interface 掉出 zone,表现同样是“能 ping 自己不能转”)。

根治

  1. 按最小权限重建 forwarding 矩阵,不要长期 defaults.forward=ACCEPT
    • 只允许 lan → s2slan → vpn、必要的 s2s → lan 回程
    • 明确 masq 只开在真正做 NAT 的 zone(旁路由很多场景甚至不应开 masq)
  2. 为自定义 zone 写死名称与 device,避免依赖匿名 section 顺序;升级后跑一次:
1
2
3
fw4 restart
nft list ruleset > /root/fw4-after-upgrade.nft
# 与升级前备份 diff
  1. 变更清单里增加 firewall4 专项
    • firewall.@defaults[0].{input,output,forward}
    • 所有 config forwarding / config rule / config redirect
    • option device / option network 是否仍解析得到
  2. 旁路由升级改为:先在同型号备机刷 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。

预防措施

  1. 软路由/旁路由纳入变更分级:大版本跨 firewall3 → firewall4、DSA 改造、内核大版本,一律按“网络割接”做,而不是“打个补丁”。
  2. 升级验收探针必须覆盖转发路径
    • 至少一条:客户端 → 旁路由 → 对端内网 TCP 业务端口
    • 不要只用“能打开 LuCI / 能 ping 外网”当验收
  3. 配置即代码/etc/config/firewallnetwork、PBR 相关配置进 Git;升级前后自动 diff,forwarding 段数量不得无故减少。
  4. 监控:对旁路由 snmp/prometheus 采 nft counter 或转发流量;业务超时告警与“旁路由 forward drop 速率”关联,避免只盯专线运营商链路灯。
  5. 文档化 zone 矩阵:一张表写清 src zone / dest zone / 是否 masq / 是否依赖自定义 rule,新人半夜不敢“顺手修”。
  6. 保留 15 分钟回退包sysupgrade 前离线保存旧固件 + 完整 backup,确认 SERIAL/电源可进 failsafe。

总结

这次故障的完整链条是:

OpenWrt 大版本升级 → 防火墙从 fw3 切到 fw4 → 默认 FORWARD 收紧 + 部分 forwarding 迁移丢失 → 旁路由本机仍通但跨站转发静默丢包 → 专线与主路由“背锅”。

排查口诀可以压成一句:旁路由疑难,先看 FORWARD policy 与 counter,再看专线。
处理上止血可以短暂放开 defaults.forward,根治必须按 zone 矩阵最小放行,并把 firewall4 纳入升级验收清单。家里或公司的 x86 软路由如果还在 “能上网就当健康”,下次跨大版本前务必先在备机验证一条真实业务的转发路径——否则周一早上等你的,一定是“专线没问题,就是业务全死”的灵魂拷问。

使用 Hugo 构建
主题 StackJimmy 设计