目录

修复 OpenWrt PassWall2 访问控制(ACL)源接口识别异常与多接口分流失效问题

6

在 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.20LQ.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.10LQ.20LQ.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 剖析:

  1. 变量残留与覆盖:在遍历 ACL 列表的循环(for sid in ...)结束时,由于缺乏变量清理逻辑,上一轮循环残留的接口变量影响了后续解析。

  2. 状态依赖度高:原代码优先依赖 network_get_gatewaynetwork_get_device。但在多 VLAN、虚拟网卡或处于非 UP 状态(如 auto='0' 或动态拨号接口)时,network_get_device 会直接返回空。

  3. 缺乏内核网卡优先校验:对于已经在 /sys/class/net/ 中存在的 Linux 内核网卡(如 VLAN 虚拟卡 LQ.10),原代码依然先去走 UCI 逻辑查询,最终在查询失败时盲目赋默认值,导致 device 变量混淆。

  4. 硬塞错误网卡名生成规则:不管获得的 device 是否真正在内核中存在,原逻辑都会直接输出 iifname ${device}。当生成的 nftables 规则匹配到了不存在或错误的网卡名称时,多接口分流策略便彻底失效。

3. 终极解决方案:代码修复

找到根源后,我们只需重构 /usr/share/passwall2/nftables.sh 中关于 Source iface 部分的逻辑:优先判断是否为内核设备,引入 ubus 动态解析,并建立严谨的边界兜底校验。

修复步骤

  1. 使用 SSH 登录 OpenWrt 路由器。

  2. 编辑 /usr/share/passwall2/nftables.sh 文件:

    Bash

    nano /usr/share/passwall2/nftables.sh
    
  3. 找到 定位:/Source iface 所在的代码段(在 load_acl 函数内部)。

  4. 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.10LQ.20),直接使用,不走冗余的 UCI 查询。

  • 第二层(逻辑接口动态解析):若非物理网卡,则调用 network_get_deviceubus 命令,动态解析 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 源接口显示不正确的情况,不妨按照本文的代码修复尝试一下!