用于个人记录,方便以后排查。

我的环境是 ImmortalWrt 24.10.5 x86_64 firewall4 ,遇到的问题是:

1
/cgi-bin/luci/admin/services/nlbw

页面能打开,但是一直没有任何统计数据。

问题现象

一开始怀疑是 LuCI 页面问题,或者是编译固件时包没带全,但实际上:

  • nlbwmon 服务是正常运行的
  • luci-app-nlbwmon 也已经安装
  • /proc/net/nf_conntrack 里能看到大量连接
  • nlbw -c show 和页面里都没有任何数据

检查版本时,设备信息如下:

1
2
3
4
ImmortalWrt 24.10.5 r33805-7c4e882aaf6f
Linux 6.6.122 x86_64
nlbwmon 2024.02.21~c7616bcf-r1
luci-app-nlbwmon 25.306.51720~a833d72

初步排查

先确认服务本身是不是正常:

1
2
3
service nlbwmon status
ps w | grep nlbwmon
uci show nlbwmon

这一步看下来没有明显异常,nlbwmon 的确在运行。

再看统计结果:

1
2
nlbw -c show
nlbw -c json list

返回是空的:

1
{"columns":["family","proto","port","mac","ip","conns","rx_bytes","rx_pkts","tx_bytes","tx_pkts","layer7"],"data":[]}

但与此同时,系统连接跟踪表不是空的:

1
2
wc -l /proc/net/nf_conntrack
head -n 20 /proc/net/nf_conntrack

这说明不是没有流量,也不是内核没记录连接,而是 nlbwmon 没有把这些连接转成自己的统计数据。

先处理两个常见干扰项

先检查防火墙里是否开启了流量分载:

1
uci show firewall | grep flow_offloading

如果有下面这种配置:

1
2
firewall.@defaults[0].flow_offloading='1'
firewall.@defaults[0].flow_offloading_hw='1'

建议先关掉,因为这类按主机统计的工具很容易受快速转发影响。

1
2
3
4
uci set firewall.@defaults[0].flow_offloading='0'
uci set firewall.@defaults[0].flow_offloading_hw='0'
uci commit firewall
service firewall restart

另外我这台机器里,nlbwmonlocal_network 默认配置也有问题。

原配置包含:

1
2
3
4
5
6
7
8
192.168.0.0/16
172.16.0.0/12
10.0.0.0/8
lan
wan
wan_6
wan6
zerotier

而实际 WAN 地址是 10.250.4.238 ,属于运营商 CGNAT 的 10.x.x.x 网段。
这样一来,10.0.0.0/8 会把 WAN 地址也误判成本地地址,导致部分连接判断出错。

所以我把 local_network 收窄成只保留真正要统计的接口:

1
2
3
4
5
uci -q delete nlbwmon.@nlbwmon[0].local_network
uci add_list nlbwmon.@nlbwmon[0].local_network='lan'
uci add_list nlbwmon.@nlbwmon[0].local_network='zerotier'
uci commit nlbwmon
service nlbwmon restart

这两步是必要修正,但还不是最终根因。

最终定位

后来参考了社区 issue,尝试执行下面这组命令:

1
2
3
4
5
6
service nlbwmon stop
rmmod nf_conntrack_netlink
modprobe nf_conntrack_netlink
service nlbwmon start
sleep 60
nlbw -c show

结果这一次统计立刻出来了,直接能看到内网设备的流量:

1
2
3
4
IPv4     10.10.10.104       HTTPS
IPv4 10.10.10.101 HTTPS
IPv4 10.10.10.103 HTTPS
IPv4 10.10.10.117 HTTPS

到这里就可以基本确认:

  • 不是 LuCI 页面坏了
  • 不是 nlbwmon 没启动
  • 问题核心在 nf_conntrack_netlink

为什么会有这个问题

nlbwmon 依赖的是 conntrack 事件,而不是简单扫一次页面或者扫一次 IP 列表。

也就是说,nlbwmon 要想正常建立和更新统计数据,需要从内核的 nf_conntrack_netlink 收到新连接、连接销毁、连接统计变化之类的事件。

这台机器上的实际情况是:

  • /proc/net/nf_conntrack 有数据
  • nf_conntrack_acct=1
  • nf_conntrack_events=1
  • nlbwmon 进程本身也在运行

nlbwmon 启动后,拿不到正常可用的 nf_conntrack_netlink 事件,所以最终表现就是:

  • 数据库长期是空的
  • 新设备不会建立统计记录
  • LuCI 页面一直没有数据

结合这次实机验证,以及 ImmortalWrt 相关 issue 的讨论,比较合理的结论是:

ImmortalWrt 某些版本下,nf_conntrack_netlinkfirewall4 环境中的初始化状态异常,导致 nlbwmon 启动后无法正常接收或处理连接跟踪事件。

所以这不是单纯配置写错,而是一类已经有人复现过的兼容性回归问题。

解决方法

我的处理方式分三步:

第一步,关闭 offloading:

1
2
3
4
uci set firewall.@defaults[0].flow_offloading='0'
uci set firewall.@defaults[0].flow_offloading_hw='0'
uci commit firewall
service firewall restart

第二步,修正 local_network

1
2
3
4
uci -q delete nlbwmon.@nlbwmon[0].local_network
uci add_list nlbwmon.@nlbwmon[0].local_network='lan'
uci add_list nlbwmon.@nlbwmon[0].local_network='zerotier'
uci commit nlbwmon

第三步,重载 nf_conntrack_netlink

1
2
3
4
service nlbwmon stop
rmmod nf_conntrack_netlink
modprobe nf_conntrack_netlink
service nlbwmon start

执行后等待 30 到 60 秒,再刷新:

1
/cgi-bin/luci/admin/services/nlbw

一般就可以看到数据出来了。

持久化修复

如果只是手工执行一次上面的命令,当前这次开机是正常的,但以后升级固件时,之前直接修改的包文件很可能会被覆盖。

比如直接修改下面这个文件:

1
/etc/init.d/nlbwmon

这种方式虽然短期有效,但它属于包自带脚本,系统升级后通常会恢复成新固件里的默认版本,不适合作为长期持久化方案。

更稳妥的做法是:

  • 不再直接修改 /etc/init.d/nlbwmon
  • 新建一个自己的启动脚本
  • 再把这个脚本加入 sysupgrade 保留列表

我实际采用的是下面这个方案。

先新建:

1
/etc/init.d/nlbwmon-fix

内容如下:

1
2
3
4
5
6
7
8
9
10
#!/bin/sh /etc/rc.common

START=99
USE_PROCD=0

start() {
rmmod nf_conntrack_netlink 2>/dev/null || true
modprobe nf_conntrack_netlink 2>/dev/null || true
/etc/init.d/nlbwmon restart >/dev/null 2>&1 || true
}

然后赋予执行权限并启用:

1
2
3
chmod +x /etc/init.d/nlbwmon-fix
/etc/init.d/nlbwmon-fix enable
/etc/init.d/nlbwmon-fix start

接着把它加入:

1
/etc/sysupgrade.conf

追加这一行:

1
/etc/init.d/nlbwmon-fix

也可以直接用命令完成:

1
2
3
if ! grep -qx '/etc/init.d/nlbwmon-fix' /etc/sysupgrade.conf 2>/dev/null; then
printf '%s\n' '/etc/init.d/nlbwmon-fix' >> /etc/sysupgrade.conf
fi

这样做的好处是:

  • 重启时会自动执行修复逻辑
  • 升级固件时,只要保留配置,这个脚本也会一起保留下来
  • 不依赖包内部实现,后续 nlbwmon 包更新时也不容易被冲掉

相比直接修改 /etc/init.d/nlbwmon,这个方式更适合长期使用。

一键修复命令

如果只是想快速修复,可以直接执行下面这一组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
uci set firewall.@defaults[0].flow_offloading='0'
uci set firewall.@defaults[0].flow_offloading_hw='0'
uci commit firewall

uci -q delete nlbwmon.@nlbwmon[0].local_network
uci add_list nlbwmon.@nlbwmon[0].local_network='lan'
uci add_list nlbwmon.@nlbwmon[0].local_network='zerotier'
uci commit nlbwmon

service nlbwmon stop
rmmod nf_conntrack_netlink
modprobe nf_conntrack_netlink
service firewall restart
service nlbwmon restart

如果想长期稳定使用,建议继续按上面的方式创建 /etc/init.d/nlbwmon-fix 并加入 /etc/sysupgrade.conf

注意事项

  • flow offloadinghardware flow offloading 可能会影响这类按主机统计的插件,建议关闭。
  • 如果 WAN 走的是 CGNAT,比如 10.x.x.x,不要直接把 10.0.0.0/8 这种大私网段留在 local_network 里。
  • 这个问题不是你一个人遇到,ImmortalWrt 的多个版本、多个设备上都有人复现。
  • 如果系统升级后再次失效,通常重新执行下面这组命令就能恢复:
1
2
3
rmmod nf_conntrack_netlink
modprobe nf_conntrack_netlink
service nlbwmon restart
  • 根据 issue 里的反馈,即使临时恢复,IPv6 新地址统计在某些场景下仍然可能不完全稳定。
  • 如果想让修复在固件升级后依旧保留,建议把自定义脚本加入 /etc/sysupgrade.conf,不要只手改包自带的 /etc/init.d/nlbwmon

参考链接

  1. ImmortalWrt packages issue #1273
  2. ImmortalWrt packages issue #1276
  3. jow-/nlbwmon issue #62 comment