日常排查:PVE 跨 VLAN Ping 不通(默认网关指错 + OpenWrt LAN 区误开 NAT)

这是一篇“日常排查记录”。只写可验证的事实、证据链和最小修复,不写玄学。

TL;DR

  • 现象:跨 VLAN ping 超时;目标机本地抓包却能看到 echo reply 已经发出。
  • 根因 1(决定性):PVE 刻意以 OpenWrt(192.168.22.13)作为默认网关做 QoS/流量处理,但 没有为其它内网 VLAN 配置显式路由,导致回程流量走默认路由“拐进” OpenWrt,主网关看不到 reply(典型非对称路径)。
  • 根因 2(放大器):OpenWrt 把 lan zone 开了 masq=1,把内网互访流量也 SNAT 改源成 192.168.22.13,让路径与会话变得不可预期。
  • 修复
    • 保留 PVE 默认网关为 OpenWrt(192.168.22.13),但为内网网段(如 192.168.11.0/24192.168.183.0/24)添加静态路由指向主网关(192.168.22.1
    • OpenWrt 关闭 lan zone 的 masq
  • 预防:内网只路由不 NAT;如果默认网关必须指向旁路(QoS/代理),就把“内网路由”和“公网默认路由”明确拆开;用 tcpdump + ip route get 走 SOP。

背景(拓扑与角色)

家庭网络是多 VLAN:

  • Cloud Gateway Fiber(主网关):负责三层路由与 VLAN 间转发
    • VLAN11:192.168.11.0/24(GW 192.168.11.1
    • VLAN22(PVE/实验):192.168.22.0/24(GW 192.168.22.1
    • VLAN183:192.168.183.0/24(GW 192.168.183.1
  • OpenWrt(旁路/二级网关)192.168.22.13/24br-lanfw3 + iptables,带旁路代理脚本)
  • PVE192.168.22.12/24vmbr0
  • 测试机
    • 192.168.183.235(VLAN183)
    • 192.168.11.29(VLAN11)

简化数据路径(正常设计)应该是:

  • VLAN183 ↔ VLAN22:都由 Fiber 做三层转发
  • OpenWrt 如果要做“上网/代理出口”,应该只在真正的 wan 出口做 NAT/代理,而不是改写内网互访流量

用 shell 画出拓扑与数据路径(ASCII)

下面这个脚本不会改任何配置,只是把拓扑和两条关键路径打印出来,方便读者“看见数据怎么走”:

bash
#!/usr/bin/env bash
set -euo pipefail
# Print the topology and the two paths (bad vs fixed).
cat <<'EOF'
+------------------------------+
| Cloud Gateway Fiber (L3) |
| VLAN11: 192.168.11.1 |
| VLAN22: 192.168.22.1 |
| VLAN183: 192.168.183.1 |
+---------------+--------------+
|
VLAN22
|
+----------------+----------------+
| |
+----------v-----------+ +----------v-----------+
| OpenWrt (side GW) | | PVE |
| 192.168.22.13 | | 192.168.22.12 |
| QoS / traffic mgmt | | vmbr0 |
+----------------------+ +----------------------+
Clients:
VLAN183 host: 192.168.183.235
VLAN11 host: 192.168.11.29
Problem #1 (before):
192.168.183.235 -> Fiber -> PVE
PVE reply -> (default gw) OpenWrt -> [NAT/proxy/unknown] -> ??? (Fiber never sees the reply)
Fix idea:
Keep PVE default gw = OpenWrt (for QoS),
but route internal subnets via Fiber.
Problem #2 (amplifier):
OpenWrt LAN masquerade (SNAT) rewrites east-west traffic,
making internal routing/session behavior unpredictable.
EOF

事件与影响

事件 1:VLAN183 ping 不通 PVE

  • 发起端:192.168.183.235 执行 ping 192.168.22.12 一直超时
  • PVE 本机抓包能看到:
    • ICMP echo request 到达
    • ICMP echo reply 发出
  • 但在主网关 Fiber(br22/br0)抓包只看到 request,看不到 reply

这意味着:PVE 的 reply 根本没走回 Fiber —— 问题不是“主网关丢包”,而是“回程路径压根没经过主网关”。

事件 2:修复后,VLAN11 仍 ping 不通 PVE

  • 192.168.11.29 ping 不通 192.168.22.12
  • 但可以 ping 通 192.168.22.13(OpenWrt)

这暴露出第二个问题:OpenWrt 的 NAT 行为在破坏内网互访的可预期性。


排查过程(证据链)

1)先证明主网关本身可用

在 Fiber 上验证:

  • Fiber 能 ping 通 192.168.183.235192.168.22.12
  • ip route 显示 192.168.22.0/24192.168.183.0/24 为直连

结论:Fiber 的基础三层与直连网段没问题。

2)逐跳抓包:定位“reply 消失在哪一跳”

抓包观察到:

  • PVE 本机:request/reply 都存在(说明 PVE 会回包)
  • Fiber 的 br22/br0:只有 request,没有 reply

结论:reply 在到达 Fiber 之前就“拐走了”。

3)用 ip route get 让路由决策自己说话

在 PVE 上:

  • ip route 显示默认路由:default via 192.168.22.13 dev vmbr0
  • ip route get 192.168.183.235 显示:via 192.168.22.13

关键结论(问题 1 根因):PVE 没有到其它内网 VLAN 的显式路由,因此对 192.168.183.0/24 的回包自然走默认路由交给 OpenWrt。只要 OpenWrt 这边出现 NAT/代理/错误路由中的任意一种,主网关就可能完全看不到 reply。

这就是典型的非对称路由:

  • 正向:183 → Fiber → 22 → PVE(正常)
  • 回程:PVE → OpenWrt → (NAT/代理/错误路由/黑盒)(不可控)

4)验证 OpenWrt 的防火墙实现与 NAT 配置

为了排除“背后还有一套规则系统”的不确定性,确认 OpenWrt 是 fw3 + iptables(无 nftables),然后检查到:

  • lan zone 开启了 masq='1'

关键结论(问题 2 根因):OpenWrt 把内网互访也做了 SNAT,源地址被改成 192.168.22.13,会让跨 VLAN 的会话与回程路径变得混乱(尤其叠加旁路代理/策略导流时)。


解决方法(最小改动)

修复 1:保留默认网关为 OpenWrt,但把“内网路由”显式指回主网关 Fiber

需求前提:PVE 默认网关 必须192.168.22.13(OpenWrt),因为你要在 OpenWrt 上做 QoS/流量处理;那就别动默认路由,改成“内网网段走 Fiber,其他流量仍走 OpenWrt”。最简单且最可控的做法是给 PVE 加静态路由。

临时验证(示例只覆盖本次出现问题的 VLAN11/VLAN183,可按你的实际 VLAN 增加):

bash
# Keep default via OpenWrt for QoS / traffic management
# default via 192.168.22.13
# Route internal VLANs via the primary L3 gateway (Fiber)
ip route replace 192.168.183.0/24 via 192.168.22.1 dev vmbr0
ip route replace 192.168.11.0/24 via 192.168.22.1 dev vmbr0

验证思路:

  • ip route get 192.168.183.235 应该显示 via 192.168.22.1
  • 在 Fiber 的 br22/br0 上应该能抓到 echo reply

持久化(Proxmox 常见在 /etc/network/interfacesvmbr0,用 post-up 写死最直接):

bash
post-up ip route replace 192.168.183.0/24 via 192.168.22.1 dev vmbr0
post-up ip route replace 192.168.11.0/24 via 192.168.22.1 dev vmbr0
post-down ip route del 192.168.183.0/24 via 192.168.22.1 dev vmbr0 || true
post-down ip route del 192.168.11.0/24 via 192.168.22.1 dev vmbr0 || true

验证:

  • Fiber 的 br22/br0 能抓到 echo reply
  • 192.168.183.235ping 192.168.22.12 恢复

修复 2:关闭 OpenWrt 的 lan zone NAT

bash
uci set firewall.@zone[0].masq='0'
uci commit firewall
/etc/init.d/firewall restart

验证:

  • 192.168.11.29ping 192.168.22.12 恢复
  • 内网互访源 IP 保持真实,后续排障和访问控制都更简单

预防措施(别再制造“网络玄学”)

  • 内网之间只路由,不做 NAT
    • lan / 内网 zones:masq=0
    • 只有真正的公网出口(wan)才 masq=1
  • 如果默认网关必须指向旁路(QoS/代理),就把路由拆清楚
    • 默认路由可以指向 OpenWrt,但 所有内网网段必须有显式静态路由 指回主网关(或用策略路由按目的网段分流)
    • 原则很简单:内网东西向流量走“可预期的三层”,不要让它掉进 NAT/代理黑盒
  • 固定排障 SOP(Standard Operating Procedure:标准排查流程)
    • 这句话的意思是:遇到类似“跨 VLAN ping 不通”的问题,不要靠猜,按同一套步骤用证据把问题钉死。
    • 第一步:先用抓包回答 3 个问题:request 到没到?目标回没回?reply 消失在哪一跳?
      • 在目标机上:tcpdump -ni vmbr0 icmp
      • 在主网关上(目标 VLAN 口 / 发起 VLAN 口):tcpdump -ni br22 icmptcpdump -ni br0 icmp
    • 第二步:用路由决策输出替代“想当然”
      • 在 PVE 上:ip route + ip route get 192.168.183.235
      • 在 OpenWrt/Fiber 上:ip route get 192.168.183.235(确认下一跳到底是谁)
    • 第三步:优先把数据路径变简单
      • 内网互访先恢复为“纯路由、真实源地址、可预期回程”,再去叠加 QoS/代理/策略导流
  • 旁路代理要“明确排除内网网段”
    • 内网网段不应被透明导入 VPN/隧道
    • 先保证纯路由可用,再谈代理与加速

一句话教训

当你觉得“ICMP 在搞幽灵”的时候,大概率不是协议栈坏了,而是你把默认网关/NAT 配成了黑盒。


Comments