修复 OpenWrt PassWall2 访问控制(ACL)源接口识别异常与多接口分流失效问题
在 OpenWrt 环境中使用 PassWall2 进行多 VLAN 或多接口(Multi-Interface)网络管理时,许多折腾高级网络架构(如多出口分流、独立 VLAN 代理策略)的小伙伴可能遇到过一个非常隐蔽的 Bug:明明在 ACL(访问控制)中为不同的源接口配置了不同的代理节点,但实际生效的接口全乱了,甚至所有 ACL 规则都被错误匹配到了同一个接口上。
本文将结合具体的日志现象、底层原因分析以及最终的代码修复方案,为大家完整拆解并解决这个问题。
1. 问题现象与日志分析
在 PassWall2 的日志中,我们可以非常直观地看到修复前后的对比(见下方日志截图):

🔴 异常日志现象(修复前)
观察日志后半段(14:21:46 之后):
Plaintext
2026-07-19 14:21:46: - 【001】,源接口【LQ.10】,所有设备,使用与全局配置不同节点...
2026-07-19 14:21:47: - 【002】,源接口【LQ.10】,所有设备,使用与全局配置不同节点...
2026-07-19 14:21:47: - 【003】,源接口【LQ.10】,所有设备,使用与全局配置不同节点...
现象描述:我们在 ACL 策略中显式定义了三个不同的策略:规则
001绑定接口LQ.10、规则002绑定接口LQ.20、规则003绑定接口LQ.30。异常点:PassWall2 加载防火墙规则时,【001】、【002】、【003】三条规则的源接口全变成了【LQ.10】! 导致
LQ.20和LQ.30网卡下的设备策略完全失控,全部走到了第一个接口的配置节点上。
🟢 正常日志现象(修复后)
参考日志前半段(14:17:22 前后):
Plaintext
2026-07-19 14:17:22: - 【001】,源接口【LQ.10】,所有设备...
2026-07-19 14:17:23: - 【002】,源接口【LQ.20】,所有设备...
2026-07-19 14:17:24: - 【003】,源接口【LQ.30】,所有设备...
正常效果:各个 ACL 规则准确匹配了对应的底层源接口,
LQ.10、LQ.20、LQ.30各司其职,实现了真正的多接口/多 VLAN 差异化分流。
2. 深入排查:为什么源接口会识别错误?
出现这个问题的根源在于 PassWall2 防火墙脚本 /usr/share/passwall2/nftables.sh 中对 interface 变量的处理逻辑存在漏洞。
原有的接口解析代码片段如下:
Bash
local gateway device
network_get_gateway gateway "${interface}"
network_get_device device "${interface}"
# network_get_device returns empty for non-UP interfaces (e.g. auto='0').
# Try ubus directly, then check if the name is a kernel device.
[ -z "${device}" ] && device=$(ubus call "network.interface.${interface}" status 2>/dev/null | jsonfilter -e '@.device' 2>/dev/null)
[ -z "${device}" ] && [ -d "/sys/class/net/${interface}" ] && device="${interface}"
[ -z "${device}" ] && device="${interface}"
_ipt_source="iifname ${device} "
msg=$(i18n "Source iface [%s]," "${device}")
底层 Bug 剖析:
变量残留与覆盖:在遍历 ACL 列表的循环(
for sid in ...)结束时,由于缺乏变量清理逻辑,上一轮循环残留的接口变量影响了后续解析。状态依赖度高:原代码优先依赖
network_get_gateway和network_get_device。但在多 VLAN、虚拟网卡或处于非 UP 状态(如auto='0'或动态拨号接口)时,network_get_device会直接返回空。缺乏内核网卡优先校验:对于已经在
/sys/class/net/中存在的 Linux 内核网卡(如 VLAN 虚拟卡LQ.10),原代码依然先去走 UCI 逻辑查询,最终在查询失败时盲目赋默认值,导致device变量混淆。硬塞错误网卡名生成规则:不管获得的
device是否真正在内核中存在,原逻辑都会直接输出iifname ${device}。当生成的 nftables 规则匹配到了不存在或错误的网卡名称时,多接口分流策略便彻底失效。
3. 终极解决方案:代码修复
找到根源后,我们只需重构 /usr/share/passwall2/nftables.sh 中关于 Source iface 部分的逻辑:优先判断是否为内核设备,引入 ubus 动态解析,并建立严谨的边界兜底校验。
修复步骤
使用 SSH 登录 OpenWrt 路由器。
编辑
/usr/share/passwall2/nftables.sh文件:Bash
nano /usr/share/passwall2/nftables.sh找到
定位:/Source iface所在的代码段(在load_acl函数内部)。将
if [ -n "${interface}" ]; then和对应的else内部代码替换为以下优化后的逻辑:
Bash
local device=""
if [ -d "/sys/class/net/${interface}" ]; then
device="${interface}"
else
network_get_device device "${interface}"
[ -z "${device}" ] && device=$(ubus call "network.interface.${interface}" status 2>/dev/null | jsonfilter -e '@.device' 2>/dev/null)
fi
if [ -n "${device}" ] && [ -d "/sys/class/net/${device}" ]; then
_ipt_source="iifname ${device} "
msg=$(i18n "Source iface [%s]," "${device}")
else
_ipt_source=""
msg=$(i18n "Source iface [%s]," $(i18n "All"))
fi
关键优化逻辑解析:
第一层(内核网卡优先):优先检查
/sys/class/net/${interface}。如果传入的已经是实际的内核网卡名(如 VLAN 接口LQ.10、LQ.20),直接使用,不走冗余的 UCI 查询。第二层(逻辑接口动态解析):若非物理网卡,则调用
network_get_device及ubus命令,动态解析 OpenWrt 逻辑接口背后的实际设备(@.device)。第三层(有效性二次校验与降级):最终输出
iifname ${device}前,校验/sys/class/net/${device}是否真实存在。若设备无效,自动降级为全局All(不匹配特定网卡),防止插入无效的防火墙规则导致系统报错或网络阻断。
4. 验证与效果
修改完成后保存文件,在 PassWall2 界面中点击 保存并应用 或重启服务:
Bash
/etc/init.d/passwall2 restart
再次查看 PassWall2 的运行日志,可以看到:
【001】 绑定 LQ.10
【002】 绑定 LQ.20
【003】 绑定 LQ.30
多接口/多 VLAN 的源接口绑定恢复正常,各自对应的节点代理与分流策略正确加载,成功解决多出口流量串掉的问题!
总结
在 OpenWrt 的 Shell 脚本开发中,UCI 逻辑接口与 Linux 本地内核网卡名之间的转换是一个经典的坑点。PassWall2 在处理多 ACL 规则时,由于缺乏对内核网卡的优先判定与严谨的容错校验,才引发了这一连串的源接口识别异常。
如果你也遇到了 PassWall2 多接口/多 VLAN 分流失效、ACL 源接口显示不正确的情况,不妨按照本文的代码修复尝试一下!