博主头像

人間になりたい!!!!!


皖ICP备2025096275号

当 Easytier 遇上 mwan3: 一次 VPN 回程路由被劫持的修复实录

前言

博主使用了 Easytier 在家, 学校, 境内外公网服务器之间通过 WireGuard 协议建立了一个三层 VPN 网络. 平日里在学校或出门在外都会通过这个 VPN 网络连接家里的设备, 访问家里搭建的业务. 在博主今天尝试在学校连接家里服务器上的 SMB 时, 发现全部连接失败, 故着手研究原因.

网络拓扑架构

Topology
Topology

  • 在 HomeLab 中, 有三台设备代理了 192.168.1.0/24 网段, 即其他设备可以走这三个节点访问该网段. 且对于该网段的访问没有专门的 ACL 限制, 所有设备均可访问
  • 在校园节点中, 网关代理了 192.168.20.0/24 网段, 同样可以被所有设备访问

链路状态

  • 相关节点状态: HomeLab (在线), Router-School (在线)
  • 链路通信状态

    • 校园节点在网关上与 192.168.1.0/24 段的设备通信正常, 但下游设备与该网段的通信时不正常
    • HomeLab 中所有节点的网络通信状态与子网代理状态都是正常的
  • 疑点: 下游设备与 192.168.1.14/32 通信时正常, 但与该网段中其他所有设备通信都不正常. 表现为 ICMP Ping 无回包, 业务数据访问不通

问题分析

造成该故障最常见的原因可能有以下几点

  • 回程路由缺失
  • 反向路径过滤 (rp_filter) 导致回包被丢弃
  • IP 转发未开启
  • Easytier 的出口策略 / 策略路由冲突

我们这边逐个来排查

1. 回程路由缺失

  • 请求: 192.168.20.0/24 → 网关 → tun0 → VPN → 192.168.1.0/24
  • 回包: 192.168.1.0/24 → VPN → 网关 tun0 → ??? → 192.168.20.0/24

网关自身访问时, 源地址通常是 tun0 的 IP (10.0.64.253), 这个地址天然在 VPN 路由域内, 对端可以直接回包.
而下游设备访问时, 源地址保留为 192.168.20.x, 如果对端节点没有去往 192.168.20.0/24 的路由, 回包就会丢失或走默认路由被丢弃.

这里检查校园网关的路由表

root@X-wrt-School:~# ip rou
default via 10.131.0.1 dev pppoe-wan proto static metric 40 
default via 10.151.0.1 dev pppoe-xwan01 proto static metric 41 
default via 10.141.0.1 dev pppoe-xwan02 proto static metric 42 
default via 10.141.0.1 dev pppoe-xwan03 proto static metric 43 
default via 10.151.0.1 dev pppoe-xwan04 proto static metric 44 
10.0.64.0/24 dev tun0 proto kernel scope link src 10.0.64.253 
10.0.64.0/24 dev tun0 proto static metric 65535 
10.10.64.0/24 dev tun0 proto static metric 65535 
10.131.0.1 dev pppoe-wan proto kernel scope link src 10.131.15.37 
10.141.0.1 dev pppoe-xwan02 proto kernel scope link src 10.141.102.228 
10.141.0.1 dev pppoe-xwan03 proto kernel scope link src 10.141.40.183 
10.151.0.1 dev pppoe-xwan01 proto kernel scope link src 10.151.10.205 
10.151.0.1 dev pppoe-xwan04 proto kernel scope link src 10.151.32.226 
169.254.0.0/16 dev br-lan proto kernel scope link src 169.254.254.254 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.1.0/24 dev tun0 proto static metric 65535 
192.168.3.0/24 dev tun0 proto static metric 65535 
192.168.20.0/24 dev br-lan proto kernel scope link src 192.168.20.1 
192.168.20.0/24 dev br-lan proto bird scope link metric 32 
192.168.100.0/24 dev tun0 proto static metric 65535

可以看到, 192.168.1.0/24 正常从 tun0 出口

然后检查 HomeLab 节点中设备的路由表

nanami114@NanamiWork-Server:~$ ip rou
default via 192.168.1.1 dev vmbr0 proto kernel onlink 
10.0.64.0/24 dev tun0 proto static metric 65535 
10.10.64.0/24 dev tun0 proto static metric 65535 
192.168.1.0/24 dev vmbr0 proto kernel scope link src 192.168.1.8 
192.168.3.0/24 dev tun0 proto static metric 65535 
192.168.20.0/24 dev tun0 proto static metric 65535 
192.168.100.0/24 dev tun0 proto static metric 65535

可以看到这里也是正常的, 因此该原因被排除

2. 反向路径过滤 (rp_filter) 导致回包被丢弃

这里直接检查校园网关的 rp_filter 策略

root@X-wrt-School:~# sysctl net.ipv4.conf.all.rp_filter
net.ipv4.conf.all.rp_filter = 0
root@X-wrt-School:~# sysctl net.ipv4.conf.tun0.rp_filter
net.ipv4.conf.tun0.rp_filter = 0
root@X-wrt-School:~# sysctl net.ipv4.conf.br-lan.rp_filter
net.ipv4.conf.br-lan.rp_filter = 0

可以看到, 所有相关网络接口的 rp_filter 均为 0, 证明这里没有问题, 故排除该原因

3. IP 转发未开启

首先网关本身使用的系统为 OpenWrt, 该系统为专业的软路由系统, 因此可以直接排除该原因. 但为了严谨, 这里还是检查一下

root@X-wrt-School:~# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

4. Easytier 的出口策略 / 策略路由冲突

在第一项检查中我们可以看到, 路由表中存在多个 pppoe-wan, 说明这是一个多拨的网络环境. 因此可能造成下游流量在转发时被路由策略规则劫持, 导致回包走到了错误的物理接口, 而非回到 tun0.

root@X-wrt-School:~# ip rule show
0:      from all lookup local
1001:   from all iif pppoe-wan lookup 1
1002:   from all iif pppoe-xwan01 lookup 2
1003:   from all iif pppoe-xwan02 lookup 3
1004:   from all iif pppoe-xwan03 lookup 4
1005:   from all iif pppoe-xwan04 lookup 5
2001:   from all fwmark 0x100/0x3f00 lookup 1
2002:   from all fwmark 0x200/0x3f00 lookup 2
2003:   from all fwmark 0x300/0x3f00 lookup 3
2004:   from all fwmark 0x400/0x3f00 lookup 4
2005:   from all fwmark 0x500/0x3f00 lookup 5
2061:   from all fwmark 0x3d00/0x3f00 blackhole
2062:   from all fwmark 0x3e00/0x3f00 unreachable
32766:  from all lookup main
32767:  from all lookup default

通过这条输出, 我们可以注意到:

  • 下游设备 192.168.20.x → 网关 → 匹配 main 路由表走 tun0 → VPN → 192.168.1.y (正常发出)
  • 回包到达网关 tun0 后, 内核会进行路由查找. 此时, 如果回包被打上了某个 fwmark (例如 0x100), 就会命中规则 2001: from all fwmark 0x100/0x3f00 lookup 1, 从而使用路由表 1 进行转发.

这里我们查看路由表 1 的具体路由信息

root@X-wrt-School:~# ip route show table 1
default via 10.131.0.1 dev pppoe-wan metric 40 
10.0.64.0/24 dev tun0 proto kernel scope link src 10.0.64.253 metric 256 
10.0.64.0/24 dev tun0 proto static scope link metric 65535 
10.10.64.0/24 dev tun0 proto static scope link metric 65535 
10.131.0.1 dev pppoe-wan proto kernel scope link src 10.131.15.37 
10.141.0.1 dev pppoe-xwan02 proto kernel scope link src 10.141.102.228 metric 3 
10.141.0.1 dev pppoe-xwan03 proto kernel scope link src 10.141.40.183 metric 4 
10.151.0.1 dev pppoe-xwan01 proto kernel scope link src 10.151.10.205 metric 2 
10.151.0.1 dev pppoe-xwan04 proto kernel scope link src 10.151.32.226 metric 5 
169.254.0.0/16 dev br-lan proto kernel scope link src 169.254.254.254 metric 256 
192.168.1.0/24 dev tun0 proto static scope link metric 65535 
192.168.3.0/24 dev tun0 proto static scope link metric 65535 
192.168.20.0/24 dev br-lan proto bird scope link metric 32 
192.168.20.0/24 dev br-lan proto kernel scope link src 192.168.20.1 metric 256 
192.168.100.0/24 dev tun0 proto static scope link metric 65535

从输出的路由表信息来看, 已经存在去往 192.168.20.0/24 的正确路由, 这说明单纯的回程缺失已经被排除. 但下游设备无法通信, 此时问题大概率出现在连接跟踪表记 (conntrack) 与防火墙过滤的联合作用上.

首先我们查看 OpenWrt 的防火墙配置

可以看到, Easytier 接口的出入配置都是正确且有效的, 那么接下来我们对问题进行深入分析与排查

深入分析

mwan3 的标记机制

mwan3 会在 mangle 表的 PREROUTINGOUTPUT 中挂载 mwan3_hook. 核心逻辑如下

  • CONNMARK restore: 恢复连接上保存的标记 (fwmark).
  • 如果恢复后标记为 0, 则根据入接口 (mwan3_ifaces_in) 或策略 (mwan3_rules) 打上新标记, 最后 CONNMARK save, 将标记保存到连接跟踪里.
  • 此后所有属于该连接的包都会自动被还原相同标记, 并通过 ip rulefwmark 匹配, 强制走对应的路由表 (如 table 1~5).

当前链路存在的问题

192.168.20.x 首次访问 192.168.1.x

  • 出站 (br-lan → 网关 → tun0):
    该包在 PREROUTINGOUTPUT 链被 mwan3 打上某个标记 (例如 0x100), 并 CONNMARK save 保存.
  • 回包 (tun0 → 网关 → 应回到 br-lan):
    回包进入 PREROUTINGmwan3_hookCONNMARK restore 直接将标记恢复为 0x100.
    由于标记非零, 后续 mwan3_ifaces_in 等链都被跳过 (它们要求 mark match 0x0/0x3f00).
    回包带着 0x100 进入路由查找, 命中 ip rule 中的 2001: from all fwmark 0x100/0x3f00 lookup 1, 使用 table 1 转发.

尽管 table 1 有去往 192.168.20.0/24 的直连路由, 理论上可以通, 但实际环境中常被以下两种隐形过滤阻断:

  1. filterFORWARD 链的状态跟踪丢弃
    大多数 OpenWrt 默认防火墙 FORWARD 链允许 ESTABLISHED ,RELATED 状态, 丢弃 INVALID.
    当回包带着被强制改变的路由标记走过路由后, 连接跟踪可能将其状态标记为 INVALID (因为出方向走的是 main 表或不同的路由表, 回包却走 table 1, 属于非对称路由, conntrack 可能视其为无效), 随后被 FORWARD 链丢弃.
  2. 反向路径校验 *(即使 rp_filter=0 仍有其他机制)
    某些内核版本在策略路由下仍会做额外的路径校验, 或者 secure_redirects 等参数造成丢弃. 但更常见的是第 1 点.

因此, 即使路由存在, 包也可能在送到 br-lan 之前就被 filter 规则或 conntrack 剔除.

故障修复

快速恢复

最快且最可靠的办法是让回包不再经过 FORWARD 转发路径, 而是直接进网关本机. 将下游设备的源地址伪装为网关 VPN 接口的地址即可.

iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o tun0 -j MASQUERADE

执行完后看效果:

C:\Users\Nanami114>ping 192.168.1.4

Pinging 192.168.1.4 with 32 bytes of data:
Reply from 192.168.1.4: bytes=32 time=11ms TTL=63
Reply from 192.168.1.4: bytes=32 time=11ms TTL=63
Reply from 192.168.1.4: bytes=32 time=11ms TTL=63
Reply from 192.168.1.4: bytes=32 time=12ms TTL=63

Ping statistics for 192.168.1.4:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 11ms, Maximum = 12ms, Average = 11ms

C:\Users\Nanami114>ping 192.168.1.8

Pinging 192.168.1.8 with 32 bytes of data:
Reply from 192.168.1.8: bytes=32 time=7ms TTL=63
Reply from 192.168.1.8: bytes=32 time=7ms TTL=63
Reply from 192.168.1.8: bytes=32 time=7ms TTL=63
Reply from 192.168.1.8: bytes=32 time=7ms TTL=63

Ping statistics for 192.168.1.8:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 7ms, Maximum = 7ms, Average = 7ms

可以看到, 此时下游设备已经可以正常收到对端设备 ICMP Ping 的回包. 接下来查看业务流量状态:

SMB
SMB

这里可以看到, 此时 SMB 流量已经可以正常通讯, 说明整个网络链路已经恢复, 下游设备恢复了与 192.168.1.0/24 段的正常通讯.

当然, 这个方法虽然恢复快速但也存在不妥:

  • 所有下游设备去往 192.168.1.0/24 的通讯, 都会被目标设备识别为 10.0.64.253 在与其通信, 也就是校园网关在与其通信. 为了解决这个问题, 下面我们进行深度修复.

根因修复: 调整 mangle 规则, 避免 VPN 回程路由被标记

  • 精准修复命令
    mwan3_hookCONNMARK save 之前插入一条规则: 如果包是从 tun0 进入且目标是 192.168.20.0/24, 则将其标记清零, 并让 conntrack 保存清零后的标记. 这样该连接后续流量将不再受多线策略影响.

首先查看 mwan3_hook 接口编号

root@X-wrt-School:~# iptables -t mangle -L mwan3_hook --line-numbers
Chain mwan3_hook (2 references)
num  target     prot opt source               destination         
1    CONNMARK   all  --  anywhere             anywhere             mark match 0x0/0x3f00 CONNMARK restore mask 0x3f00
2    mwan3_ifaces_in  all  --  anywhere             anywhere             mark match 0x0/0x3f00
3    mwan3_local  all  --  anywhere             anywhere             mark match 0x0/0x3f00
4    mwan3_rules  all  --  anywhere             anywhere             mark match 0x0/0x3f00
5    mwan3_connected  all  --  anywhere             anywhere            
6    CONNMARK   all  --  anywhere             anywhere             CONNMARK save mask 0x3f00

可以看到 mwan3_connected第五条, CONNMARK save第六条. 我们需要在这两条之间插入标记清零的规则

iptables -t mangle -I mwan3_hook 6 -i tun0 -d 192.168.20.0/24 -j MARK --set-mark 0


这里可以看到, 刚才插入的规则已经生效. 此时看下游设备效果.
这里使用下游设备连接对端物理设备的 SSH, 连接两次即可在终端内看到上一次连接的 IP 地址.

Linux NanamiWork-Server 7.0.0-3-pve #1 SMP PREEMPT_DYNAMIC PMX 7.0.0-3 (2026-04-21T22:56Z) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed May 27 16:19:00 2026 from 192.168.20.2
nanami114@NanamiWork-Server:~$

可以看到, 这里显示 Last login: Wed May 27 16:19:00 2026 from 192.168.20.2, 证明改动已经生效, 并已经恢复了与目标设备的连接. 至此, 整个 VPN 网络的修复就已完成.

后记

本人非专业网络运维, 如有疏漏或错误, 还请指出! 如果您觉得文章不错, 还请分享给有需要的

当 Easytier 遇上 mwan3: 一次 VPN 回程路由被劫持的修复实录
https://blog.nanami.tech/archives/310/
本文作者 Madobi Nanami
发布时间 2026-05-27
许可协议 CC BY-NC-SA 4.0
发表新评论