小米路由器自动刷 OpenWrt 系统
概述
把小米路由器从原厂 MiWiFi 固件换成 OpenWrt,并让它真正可用且可长期维护(能进后台、能上网、WiFi 能连、界面中文、出问题查得清)。
核心思路:不拆机、不用串口、不用降级、不依赖 xmir-patcher —— 利用小米固件 api/xqsystem/set_macfilter_rules 接口的命令注入拿到 root shell,
经 SSH 做全量分区备份,再用 NAND 双系统方式把 OpenWrt 写进备用槽,原厂固件原地保留,随时可切回去。
> 本技能以小米 BE7000(RC06,NAND 版)实测跑通为主干;取 root 的方法与双系统切槽原理对其它 RC 系列机型同样适用,
> 差异点集中在「分区号 / 门禁值 / 端口映射」,见 references/01-原理与适用性.md。
>
> 2.0.1 版新增:Wi-Fi 7 双 5GHz 与 MLO 聚合、智能家居兼容网络、LuCI 主题与流量卸载、
> 崩溃取证(KXLG 持久化日志)、日志缓冲与界面显示修复。完整推导见
> references/06-无线进阶-MLO与兼容网络.md 与 references/07-崩溃取证与日志持久化.md。
⛔ 三条硬红线(先读,不可跳过)
- 只对用户自己拥有的设备操作。 先让用户明确确认设备归属。这是给用户刷自己的路由器,不是攻击工具。
- 没做完全量备份,绝不写任何分区。 备份不完整或校验不过,直接停止整个流程。
- 刷机是有变砖风险的不可逆操作。 开始前必须把下列信息告知用户并取得明确同意:
- 会清除原厂设置;保修可能失效
- 会覆盖目标槽内原有的 A/B 备份固件(所以必须先备份 mtd24,很多人漏这一步)
- 全程不要断电,第一次启动要等 1–3 分钟
- 回滚路径(必须提前讲清楚,见「阶段 6」)
还有一条前置检查(同样不可跳过)
> 电脑要有两条互相独立的网络通路:网线直连路由器 LAN 口 + WiFi/手机热点接外网。
>
> 刷机时路由器会重启,网线通路必然中断。若电脑的外网也来自这台路由器,
> AI 助手会话会直接掉线,刷到一半没人管。 详见阶段 0 第 3 条。
若用户只是想「改个 WiFi 名 / 看设备型号」,不要走本流程。
阶段总览
阶段 0 环境准备与机型确认(含「双网络通路」检查 —— 必做)
阶段 1 免拆机取 root(拿到 SSH)
阶段 2 全量分区备份 ← 门禁:不完整就不许往下走
阶段 3 刷入 OpenWrt(干跑 → 确认 → 执行)
阶段 4 首次启动验证
阶段 5 刷后可用化:LAN → WAN → WiFi → 中文化 → 聚合 → MLO → 智能家居 → 主题 → 卸载
阶段 6 回滚原厂 / 交付验证
阶段 7 长期运维与故障取证(崩溃日志、日志缓冲、重启规范)
每个阶段结束都要采集实测证据再进入下一阶段,不要凭「看起来成功了」推进。
阶段 0:环境准备与机型确认
-
确认依赖:
bash <python> -m pip install paramiko cryptography
后续所有 Python 脚本都用这个解释器跑(paramiko5.x 对老 dropbear 有坑,scripts/rsh.py已内置修补,见阶段 1)。 -
确定工作目录与三个环境变量(凭据只走环境变量,绝不写进文件):
bash export ROUTER_WORKDIR="$(pwd)/xiaomi-openwrt-work" # 其下自动用 firmware/ 与 backups/ export ROUTER_HOST="192.168.31.1" # 原厂默认 LAN;OpenWrt 首次启动后是 192.168.1.1 export ROUTER_PASSWORD="<路由器后台/SSH 密码>" -
⚠️ 电脑必须同时具备两条网络通路(最容易翻车的一点,务必先确认):
> 一根网线直连小米路由器的 LAN 口,同时用 WiFi / 手机热点连到另一个能上网的网络。
| 通路 | 用途 | 刷机期间 |
|---|---|---|
| 网线 → 路由器 LAN | 访问路由器(SSH / ubus):取 root、备份、刷机 | 必然中断 1–3 分钟(路由器在重启) |
| WiFi / 手机热点 → 外网 | 下载固件、装 Python 依赖、以及让 AI 助手本身保持在线 | 必须全程可用 |
为什么必须这样:
- 刷机时脚本要上传固件并在路由器上执行
ubiformat,随后路由器重启 —— 网线这条通路必然断。
如果电脑的外网也来自这台正在刷的路由器,那电脑会同时失去外网:
下载中断还是小事,更糟的是 AI 助手会话会直接掉线,刷到一半没人管。 - 因此固件下载、
pip install这类联网动作全部放在阶段 0–2 做完,不要等刷机时才下。 - 刷机全程(尤其阶段 3)不要让电脑只有一个网络出口。
开工前先确认默认路由不走网线那侧(否则外网仍然依赖路由器):
bash
# Windows
route print -4 | findstr "0.0.0.0" # 看默认路由指向哪块网卡
Get-NetIPInterface | Sort-Object InterfaceMetric # 看各网卡跃点数
# Linux / macOS
ip route show default # 或 netstat -rn | grep default
若默认路由在「有线」这块网卡上:把无线网卡的接口跃点数调低
(Windows:网卡属性 → IPv4 → 高级 → 取消「自动跃点」,填 10),
或把有线网卡调高(填 100)。
判据只有一个 —— 路由器在重启时,电脑仍然能上网。
-
把电脑网线接到路由器的 LAN 口,确认能打开原厂后台。
- ⚠️ 用「页面标题」区分设备,不要只看能否 ping 通。 光猫也常是192.168.1.1,同一广播域里会 IP 冲突,
曾出现「ping 得通但页面是运营商光猫」的误判。若 LAN 与光猫同网段,先把路由器 LAN 改成别的网段(如192.168.2.1)。
- 注意:改完路由器 LAN 网段后,有线网卡会重新拿一个 IP,别误以为断线了。 -
记录设备指纹(后续门禁要用):
board_name、固件版本、启动槽标志、分区表。
可先跑只读诊断:
bash
<python> scripts/rsh.py --file scripts/diag_cmds.txt
参考 references/03-BE7000-设备档案.md 里 BE7000 的实测值。
阶段 1:免拆机取 root
运行 scripts/vendor/exploit_set_macfilter_rules.py(来自开源项目 perrywey/xiaomi-rp01-root,MIT,见 scripts/vendor/LICENSE.txt)。
# 只读探测:证明注入成立,不产生任何副作用(会在你电脑上收到 1 个 ICMP 包)
<python> scripts/vendor/exploit_set_macfilter_rules.py --probe --ip $ROUTER_HOST --target <你电脑的IP>
# 侧信道验证 + 拿命令输出(推荐,先在本机跑 python -m http.server 8000)
<python> scripts/vendor/exploit_set_macfilter_rules.py --ip $ROUTER_HOST --exfil <你电脑的IP>:8000 --cmd 'id'
# 确认后注入并拉起 dropbear(SSH 22)
<python> scripts/vendor/exploit_set_macfilter_rules.py --ip $ROUTER_HOST
要点与禁忌(踩过):
- 载荷里不能出现分号
;(控制器用它切列表),多命令用换行分隔。 {"code":0}响应体不能作为成功判据(os.execute返回值被丢弃)。唯一可信的判据是侧信道回连:
用--probe(本机收 ICMP)或--exfil/--callback(本机起 HTTP 服务,看访问日志)。- SN 获取:
GET /cgi-bin/luci/api/xqsystem/init_info的id字段。 - root 密码:
scripts/vendor/calc_root_pwd.py <SN>(=MD5(SN + 固定盐)前 8 位)。 - 部分机型固件里
telnetd起不来(busybox 被打过补丁)→ 改用 dropbear,监听 22。 - RC06(BE7000)没有
device_security模块,所以另一条链set_global_enable对 BE7000 无效,只能走set_macfilter_rules。 - 此 root 是临时的(
/etc在内存里,重启即丢),所以阶段 1 与阶段 2 之间不要重启设备。
验证:ssh root@$ROUTER_HOST "id" 返回 uid=0(root) 才算拿到。
SSH 连接统一走 scripts/rsh.py(已修补 paramiko 5.x 对老 dropbear 的三处不兼容)。
阶段 2:全量分区备份(硬门禁)
<python> scripts/backup.py # 默认关键分区集
<python> scripts/backup.py --all # 所有 mtdblock(更保险,慢)
scripts/backup.py 会 dd | base64 流式拉回本地,同时做两件事:本地重算 sha256、与设备端 md5sum 交叉校验,
输出 backups/<stamp>/manifest.json。
必须备份到的分区(BE7000 为例,其它机型见 references/01-原理与适用性.md):
| mtd | 名称 | 为什么必须备 |
|---|---|---|
| 17 | APPSBLENV | 启动槽标志,回滚靠它 |
| 23 | rootfs | 原厂固件 slot0 |
| 24 | rootfs_1 | 不是空白分区! 里面是原厂 slot1 的 A/B 备份固件,刷了就没 |
| 25 | bdata | SN / MAC / 密钥 |
| 20 | ART | 无线校准数据 |
> ⚠️ mtd24 是最容易漏的一个。 官方安装脚本的 prepare 阶段专门为它单独备份。
> 只有 manifest.json 里 complete=true 且覆盖了上述全部分区,才允许进入阶段 3。
阶段 3:刷入 OpenWrt
先备好固件。只有名字里带 NAND-installer 的包才能用于 NAND 机型,解包后取 install/factory.ubi:
mkdir -p "$ROUTER_WORKDIR/firmware" && cd "$ROUTER_WORKDIR/firmware"
# 下载 OpenWrt-<机型>-<版本>-NAND-installer.tar.gz 后:
tar tzf OpenWrt-*-NAND-installer.tar.gz # 先看清楚里面有什么
tar xzf OpenWrt-*-NAND-installer.tar.gz
# 与官方/仓库公示的 sha256 逐字节比对,不要跳过
务必先跑干跑,看清它会做什么:
<python> scripts/flash.py # DRY-RUN:只跑门禁 + 打印计划
<python> scripts/flash.py --confirm # 确认无误后再真切
scripts/flash.py 内置门禁,任一不过即中止且不改动设备任何数据:
| 门禁 | 内容 |
|---|---|
| G1 | 当前启动槽 flag_boot_rootfs=0(确实在原厂槽) |
| G2 | /proc/cmdline 含 ubi.mtd=rootfs(与 G1 交叉验证) |
| G3 | 目标分区名称与容量正确(BE7000:rootfs_1 / 41943040) |
| G4 | 目标分区未被 UBI 挂载(避免写到正在使用的卷) |
| G5 | 本地备份 complete=true 且覆盖 mtd23/17/25/20/24 |
| G6 | 固件小于分区容量,且上传后设备端 md5 与本地一致 |
核心动作(门禁全过后):
ubiformat /dev/mtd24 -f /tmp/openwrt.ubi -s 2048 -O 2048 -y
nvram set flag_boot_rootfs=1 && nvram set flag_last_success=1
nvram set flag_boot_success=0 && nvram set flag_ota_reboot=0
nvram set flag_try_sys2_failed=0 && nvram set config_name=<板级 config 名>
nvram commit && sync && reboot
> 不要自己手写 bootargs。 切槽是由 U-Boot 内置命令 bootmiwifi 依据 flag_boot_rootfs 动态改写 bootargs 的,
> 官方脚本也只改 flag。改 bootargs 反而会出问题。
阶段 4:首次启动验证
等 1–3 分钟,然后:
- OpenWrt 默认 LAN = 192.168.1.1,LuCI
root/ 空密码(或admin)。 - 默认配置里 4 个网口全在
br-lan,没有 wan 接口 —— 所以插哪个口都在同一 L2 域,
光猫若也是192.168.1.1会 IP 冲突。这就是「刷完连不上」的头号原因,不是刷机失败。
确认设备身份的可靠方法:
- LuCI 不是 HTTP Basic 认证,带表单 POST
/cgi-bin/luci/会 403。
正确方式是 ubus JSON-RPCPOST /ubus,session.login换ubus_rpc_session,见scripts/ubus.py。 - 用
system.board/ 页面标题确认是 OpenWrt 而不是光猫。
阶段 5:刷后可用化
按顺序做,每步都验证再进下一步。完整命令与解释见 references/04-刷后配置指南.md。
5.1 修 LAN(把 IP 与掩码写对)
<python> scripts/fix_lan.py --ip 192.168.2.1 --mask 255.255.255.0
> ⚠️ ipaddr 与 netmask 必须分成两个 option 写。
> 写成 192.168.2.1/24(CIDR)会被 netifd 拆成 ipaddr + 一个非法 gateway,
> 导致 br-lan 只剩 /32、IPv4 全断。
> 救急:IPv6 不受影响,可用 http://[<ULA>]/ubus 进去救回来。
5.2 配 WAN(PPPoE / DHCP)
<python> scripts/wan_cfg.py --proto pppoe -u '<宽带账号>' -p '<宽带密码>'
<python> scripts/wan_cfg.py --proto dhcp
<python> scripts/wan_cfg.py --rollback # 还原 rc.local 与 crontab
先把接光猫的口从 br-lan 摘出来当 WAN(BE7000 是 eth1)。
> ⚠️ qcom_ppe inband 坑:网口脱离网桥后会打印
> configuring for inband/10g-qxgmii link mode,此后 MAC carrier 可能永久为 0
> (而 PHY 仍报 link=1,两层矛盾)。ip link set down/up、加回网桥都无效 —— 只能重启复位。
5.3 启用 WiFi(基础)
uci set wireless.radio0.country='CN'
uci set wireless.radio1.country='CN'
uci set wireless.radio1.channel='36'
uci commit wireless && wifi reload
> ⚠️ 中国 5G 合法信道只有 ch36–48 / ch149–165;ch144 不合法。
> 且 country 必须显式设 CN,否则 hostapd 退回 world 域(country 00),
> 5G 高频段全是 PASSIVE-SCAN,表现就是 LuCI 里一直显示「已禁用」。
5.4 LuCI 中文化
apk update
apk add luci-i18n-base-zh-cn luci-i18n-firewall-zh-cn luci-i18n-dashboard-zh-cn \
luci-i18n-package-manager-zh-cn luci-i18n-commands-zh-cn
uci set luci.main.lang='zh_cn' && uci commit luci
/etc/init.d/rpcd restart; /etc/init.d/uhttpd restart
> 新版是 luci-i18n-package-manager-zh-cn,没有 luci-i18n-opkg-zh-cn。
> 包管理器是 apk(不是 opkg)。/overlay 常常只有 20 MB 左右,装包前先 df -h /overlay。
> ⚠️ 绝不用 apk del 清理 world 里装失败留下的条目 —— 会连带卸载所有失去引用的包。直接编辑 /etc/apk/world。
5.5 端口聚合(可选,按需)
用 netifd 的设备层 bonding(config device + option type 'bonding'),再把 bond 当网桥端口用,
实现 L2 聚合。模式选项叫 policy 而不是 mode。
sh scripts/bond_apply.sh # 先改脚本顶部的 BOND 名与成员口
> ⚠️ netifd 热重载有顺序 bug:把网口从 br-lan 摘出、同时又 enslave 进 bond 时会失败
> (Device is in use and cannot be enslaved)。冷启动正常 —— 改完配置重启一次即可。
5.6 Wi-Fi 7 双 5GHz 与 MLO 聚合(可选)
BE7000 的 5G 是一块 QCN9274(ath12k_wifi7_pci)承载两路射频。把两路合并成一个 MLD 接口,
就是 Wi-Fi 7 的 MLO —— 客户端可同时在两个 5G 频段收发,等效带宽翻倍。
开关不是界面按钮,而是驱动参数:
cat /etc/modules.d/ath12k # 需要是:ath12k be7000_dual=1
开启后,LuCI「无线」里的 MLO 向导会生成 wireless.be7000_mlo 段(option mlo '1' +
list device 'radio1' 'radio2'),运行时产生 ap-mld0 接口。
sh scripts/mlo_fix80.sh # ⚠️ 必跑:把 MLO 的 EHT160 改回 EHT80
> ⚠️ 两个必须记住的点:
>
> 1. MLO 向导默认给 EHT160,在中国是越界的(ch36 + 160 MHz 覆盖 5170–5330,超出 5150–5250),
> 还会触发 60 秒 DFS-CAC,表现为 5G 长时间不可用。必须改回 EHT80。
> 中国 5G 的两块频谱(5150–5250 / 5725–5850)各自最多 80 MHz,没有 160 MHz 连续空间 ——
> 想更快只能靠 MLO 合并两路,而不是加宽单路。
> 2. 改过 wireless.be7000_mlo 段(尤其 key)之后,绝不能 wifi reload。
> 实测 hostapd 会往已拆除的 ath12k copy-engine SRNG 环写数据 → 中断上下文 Oops → 内核 panic。
> 正确姿势是 uci commit wireless 后直接 reboot(或 wifi down → 5s → wifi up)。
> 只改纯 2.4G 的口令时 wifi reload 是安全的。
验证 MLO 是否真的工作:
# 1) 路由器侧:看有哪些 BSS,SSID 与频段对不对
iwinfo | grep -E 'ESSID|Channel|HT Mode'
# 2) 内核侧:两路射频都要在发 beacon
iw dev | grep -E 'Interface|ssid|channel'
# 3) ⭐ 决定性判据:客户端在 ap-mld0 上的 link 目录
ls -d /sys/kernel/debug/ieee80211/phy1/netdev:ap-mld0/stations/<客户端MLD地址>/link-*
# → 双链路正常时 link-0 与 link-1 都在;只剩 link-0 就是掉回单链路了
> 手机 Wi-Fi 设置里不再显示 Wi-Fi 7 / MLO 标识、但路由器侧一切正常时,
> 用上面第 3 条判据确认,别只信手机。恢复方法:重启路由器(wifi reload 不够),
> 强制所有客户端重新协商。完整分析见 references/06-无线进阶-MLO与兼容网络.md。
5.7 智能家居兼容网络(可选,强烈建议)
家里有老智能设备(插座、摄像头、扫地机)时,另开一张强兼容的 2.4G 网络给它们,
不要和主网络混用 —— 主网络的 WPA3 / PMF 会让老设备连不上,而它们的重连风暴会把日志刷爆。
核心配置(config wifi-iface 'smarthome',挂在 radio0)与逐项依据见
references/06-无线进阶-MLO与兼容网络.md。要点:
- 加密用
psk-mixed(本机 wifi-scripts 映射为wpa=3,同时广播 WPA1 IE + WPA2/RSN IE,不含 TKIP) ieee80211w '0'(关 PMF —— 老设备必需)max_inactivity '3600'、disassoc_low_ack '0'、skip_inactivity_poll '1'、dtim_period '3'、
multicast_to_unicast '0'(保组播发现)、isolate '0'、ieee80211k '0'- ⚠️ 这张网络的 SSID 绝不能和主网络同名(即使加密方式不同)——
同名不同密钥会让客户端在两个 BSSID 间横跳,表现成「密码明明对却提示密码错误」。 - ⚠️ 本机 wifi-scripts 里
short_preamble/hidden是无效选项
(真实名字是preamble/ignore_broadcast_ssid)。
查选项名要grep/usr/share/ucode/wifi/*.uc,别猜。
sh scripts/iot_apply.sh # 含同名 SSID 冲突预检 + 仅首次备份
5.8 LuCI 主题(可选)
apk add --allow-untrusted ./luci-theme-argon-<ver>.apk ./luci-app-argon-config-<ver>.apk \
./luci-i18n-argon-config-zh-cn-<ver>.apk # 主题/配置/语言包三包必须同格式
uci set luci.main.mediaurlbase='/luci-static/argon'
uci set luci.themes.Argon='/luci-static/argon'
uci commit luci
rm -f /tmp/luci-indexcache /tmp/luci-modulecache
/etc/init.d/rpcd restart; /etc/init.d/uhttpd restart
> ⚠️ 官方源没有 argon(apk search '*argon*' 为空),要从 GitHub Release 下 .apk(v3 格式,magic AD)。
> 三个包必须同一种格式,混用会装不上。回滚:mediaurlbase 改回 /luci-static/bootstrap。
5.9 流量卸载(可选)
uci set firewall.@defaults[0].flow_offloading='1'
uci commit firewall && /etc/init.d/firewall restart
> 判据要三条一起看:nft list ruleset | grep -c flowtable、
> /proc/net/nf_conntrack 里连接带 [OFFLOAD] 标志、以及实测吞吐提升。
> ⚠️ 开启后部分依赖 conntrack 的功能会受影响(QoS、部分统计),用不到就关掉。
阶段 6:回滚原厂 / 交付验证
必须在动手前就告知用户,并在交付文档里写清楚:
- 设备还能起来:把启动槽标志切回 slot0
- 原厂系统内:
nvram set flag_boot_rootfs=0; nvram commit; reboot - OpenWrt 内:没有
nvram命令 → 用备份还原 mtd17
nandwrite /dev/mtd17 <mtd17-APPSBLENV.bin>; reboot(需先传进设备) - 完全起不来:进恢复模式重刷(本技能不展开,优先用上一条)。
- 各阶段的本地回滚点:备份目录
backups/<stamp>/。
收尾时确认设备上没有留下临时脚本(/etc/rc.local、/etc/crontabs/root 已还原,
python scripts/runshell.py --restore 可一键还原),
然后基于 assets/交付文档模板.html 填一份实际配置 / 实测参数 / 回滚路径交给用户。
阶段 7:长期运维与故障取证
刷完只是开始。这一阶段解决「过几天出问题了,怎么查得清」。
7.1 先把日志缓冲调大(否则什么都查不到)
uci set system.@system[0].log_size='1024' # 出厂默认只有 128 KB
uci commit system
/etc/init.d/log restart # 无需重启设备
> ⚠️ log_size 出厂默认 128 KB,logread 只能回溯十几分钟,而且会被单台异常设备刷屏挤爆
> (实测 1290 行日志里 1237 行来自同一台设备)。
> ⚠️ 更要命的是:logread 是环形缓冲,设备一重启就全没了。
7.2 崩溃取证:小米 KXLG 持久化日志 ⭐
设备「莫名其妙重启」时,这里能告诉你原因。
# 读取上一次内核崩溃的完整现场
dd if=/dev/mtd26 bs=512K count=1 2>/dev/null | strings \
| grep -nE 'KXLG|Kernel panic|Unable to handle|Oops|BUG:|Comm:|Call trace' | head -50
# 一条命令版
sh scripts/reboot_reason.sh
| 分区 | 用途 |
|---|---|
/dev/mtd26 "crash"(512 KiB) |
持久化 printk 环形缓冲,以 KXLG 魔数开头,panic=10 时落盘 |
/dev/mtd27 "crash_syslog" |
send-to-syslog 导出区(本机实测为空) |
/sys/fs/pstore/ |
本机是空的 —— 小米用的是自己的 KXLG,不是标准 pstore |
判读四要素:Comm:<进程名>(谁崩的)、pc : / lr :(崩在哪个函数)、
Modules linked in、Kernel panic - not syncing: ...。
> ⚠️ KXLG 只保留到「下一次启动」为止。
> 实测:panic 重启后读到的仍是崩溃那一轮的完整记录(约 1300 行);
> 但只要再做一次正常重启,mtd26 就会被本次启动的日志覆盖(只剩 535 行、搜不到任何 panic 关键字)。
> 所以发现崩溃后第一件事就是导出留存。 现成脚本:
> bash
> sh scripts/dump_kxlg.sh # 把 mtd26/mtd27/logread/dmesg 一并写到 /www/diag/
> curl -s --noproxy '*' 'http://192.168.2.1/diag/kxlg_strings.txt' -o kxlg.txt
>
> (写进 /www 再走 HTTP 取回,比把大文件塞进投递脚本可靠得多;用完记得 rm -rf /www/diag。)
已知的一个真实崩溃案例(BE7000 + Wi-Fi 7 MLO,完整分析见 references/07-崩溃取证与日志持久化.md):
WARNING ×4:ath12k/mac.c:7759 ath12k_mac_op_sta_state [Comm: hostapd]
↳ hostapd 通过 nl80211 请求删除一个 MLO 站点
kernel BUG at mm/slub.c:634! → ath12k_dp_reo_cmd_free 重复释放(double free)
↳ Comm: swapper/3(中断上下文)
Kernel panic - not syncing: Oops - BUG: Fatal exception in interrupt → panic=10 自动重启
> 这是 ath12k 驱动在「删除 MLO 客户端」路径上的缺陷,改配置治不了根。
> 已知缓解:给 5G 加 max_inactivity '3600'(默认 300 秒就踢空闲客户端,而「踢」正是触发点),
> 脚本 fix_inactivity.sh。
> 彻底规避:be7000_mlo.disabled='1' 并启用两个独立的 5G 网络(仍是 EHT80,只是没有 MLO 聚合)。
7.3 重启的规范做法
python scripts/reboot_router.py # 触发 + 轮询等待重新上线(含 Wi-Fi 就绪)
python scripts/reboot_router.py --no-reboot # 只等待(设备已在重启)
- 单条命令触发:
ubus file.exec /sbin/reboot(file.exec白名单内)。 - ⚠️ 改过
wireless.be7000_mlo段后不要用wifi reload,直接reboot(见 5.6)。 - ⚠️ 重启后
/etc/rc.local与/etc/crontabs/root会恢复出厂内容 —— 每次诊断完都要
python scripts/runshell.py --restore确认一次,别把临时脚本留在用户设备上。
7.4 界面显示类 BUG(LuCI 前端)
有些「看起来坏了」是前端显示问题,不是设备故障。已确认的一例:
LuCI 里 5G(MLO / ap-mld0)的信号强度显示成 —/-109 dBm,而 2.4G 正常。
- 根因:ath12k + mac80211 对 MLO 站点不填瞬时 RSSI(只有
signal avg有值),
而 LuCI 读的正是瞬时值 → 渲染成—。 - 该值经过
iwinfo(C 模块)→luci-rpc.getWirelessDevices(C 模块)→
/www/luci-static/resources/network.js(可改的 JS)逐层传递,只有最后一层能改。 - 修法:给
getSignal()/getSignalPercent()加signal_avg回退 + 注入一个每 10 秒刷新
window._signalFix[ifname]的 bootstrap。完整因果链与可重放脚本见
references/07-崩溃取证与日志持久化.md第 3 节。 - ⚠️ 改完必须让用户
Ctrl+F5强刷 —— LuCI 每 5 秒轮询的只是数据,不会重新加载 JS。
补丁前就打开的页面,不刷新永远看不到变化(这点很容易被误判成「没修好」)。
故障速查
| 现象 | 最可能的原因 | 处理 |
|---|---|---|
| 刷完 ping 得通但打不开后台 / 页面是光猫 | LAN 与光猫同网段冲突 | 用页面标题区分;把 LAN 改到 192.168.2.1 |
| 改完 LAN IP 后 IPv4 全断(ARP 通、ICMP/TCP 挂) | ipaddr 写成了 CIDR |
ipaddr+netmask 分开写;IPv6 通道救急 |
ubus uci set 返回 0 但没生效 |
rpcd 的 uci set 静默失效 | 走 rc.local/crontab 通道用原生 /sbin/uci |
ubus uci get network 少了整个 wan 段 |
rpcd 读取不可靠 / 漏返回含密码的段 | 用 root 通道 cat /etc/config/network 核实 |
| WAN 口 carrier 永久为 0,PHY 却报 link=1 | qcom_ppe inband 模式 |
重启 |
| WiFi 5G 一直「已禁用」 | 信道非法 / 未设 country |
设 country='CN' + 换 ch36 |
| bond 建出来但没有成员口 | netifd 热重载顺序 bug | 重启;或手动 enslave |
| 装了包之后 LuCI 页面莫名消失 | 用 apk del 清理 world 伤了依赖树 |
apk add 把误删的包装回来 |
| MLO 一开,5G 长时间不可用 | 向导设了 EHT160,触发 60s DFS-CAC 且中国越界 |
改回 EHT80 |
| 手机不再显示 Wi-Fi 7 / MLO 标识 | 客户端侧协商回退(路由器侧正常) | 查客户端 link-* 目录确认;重启路由器恢复 |
| 某设备密码对却连不上,日志刷 PSK-MISMATCH | 同 radio 上有同名 SSID 但不同 key 的两个 BSS | iw dev \| grep -E 'Interface\|ssid' 查重名,删掉多余段后重启 |
| 老智能设备连不上 2.4G | WPA3 / PMF 不兼容 | 另开 psk-mixed + ieee80211w 0 的独立 SSID(切勿同名) |
| 路由器莫名重启 / 自己重启 | 内核 panic(多为驱动缺陷),panic=10 自动重启 |
读 /dev/mtd26(KXLG)取崩溃现场,务必先导出再重启 |
logread 什么都查不到 |
log_size 太小,且重启即清空 |
调到 1024;崩溃现场改看 /dev/mtd26 |
LuCI 里 5G 不显示信号强度(—) |
ath12k 对 MLO 站点不填瞬时 RSSI | 前端补丁(见 7.4);改完必须 Ctrl+F5 |
| 某个 LAN 口反复 up/down,速率乱跳 | 网线 / 水晶头 / 接口接触不良 | 换网线或换口(硬件问题,与配置无关) |
更多细节与完整踩坑记录见 references/05-踩坑全集.md。
资源清单
scripts/
| 文件 | 用途 |
|---|---|
_common.py |
公共配置(全部走环境变量,无硬编码凭据) |
rsh.py |
SSH 通道(含 paramiko 5.x 对老 dropbear 的修补),其他脚本都 import 它 |
backup.py |
全量分区备份 + sha256/md5 交叉校验 + manifest |
flash.py |
刷机:默认干跑,6 道门禁,--confirm 才执行 |
ubus.py |
ubus JSON-RPC 客户端(LuCI 认证用) |
runshell.py |
绕过 rpcd ACL 的「投递脚本 + root 执行 + 回显」通用通道(--restore 一键还原) |
wan_cfg.py |
WAN(PPPoE/DHCP)配置与回滚 |
fix_lan.py |
修复 LAN IP/掩码(含 IPv6 救急入口) |
bond_apply.sh |
端口聚合(802.3ad / LACP) |
mlo_fix80.sh |
把 MLO 的 EHT160 修正为 EHT80(合规 + 消除 60s CAC) |
unify_wifi_pw.sh |
统一 2.4G / 5G / MLO 口令(含备份) |
iot_apply.sh |
建智能家居兼容 BSS(含同名 SSID 冲突预检) |
fix_inactivity.sh |
给 5G/MLO 加 max_inactivity=3600(降低 MLO 崩溃触发概率) |
reboot_router.py |
触发重启并轮询等待重新上线(--no-reboot 只等待) |
dump_kxlg.sh |
导出崩溃现场到 /www/diag/(mtd26/mtd27/logread/dmesg) |
reboot_reason.sh |
一条命令查清「上次为什么重启」:启动时刻、KXLG、复位原因、日志 |
router_diag.sh |
只读诊断:机型 / 分区 / 网口 / WiFi / 软件源 |
diag_cmds.txt |
配合 rsh.py --file 使用的只读诊断命令清单 |
vendor/ |
第三方取 root 工具(MIT,© 2026 perrywey)+ LICENSE.txt |
references/
| 文件 | 内容 |
|---|---|
01-原理与适用性.md |
漏洞原理、双系统切槽机制、机型适用矩阵、固件获取 |
02-刷机详解与回滚.md |
门禁逐条说明、刷写动作、回滚路径 |
03-BE7000-设备档案.md |
BE7000 实测档案:分区表、端口映射、PHY、网口速率、无线硬件与最终配置 |
04-刷后配置指南.md |
LAN/WAN/WiFi/中文化/聚合/MLO/智能家居/主题/卸载 的完整配置与验证命令 |
05-踩坑全集.md |
本项目全部踩过的坑与修法(按阶段组织) |
06-无线进阶-MLO与兼容网络.md |
Wi-Fi 7 双 5GHz 与 MLO 原理、频谱算术、加密取舍、智能家居兼容网络逐项依据 |
07-崩溃取证与日志持久化.md |
KXLG 崩溃日志、日志缓冲、ath12k MLO 崩溃实例、LuCI 前端显示修复 |
assets/
| 文件 | 用途 |
|---|---|
交付文档模板.html |
阶段 6 用的交付文档模板,占位符替换后可打印/分享 |
CHANGELOG.md
| 文件 | 用途 |
|---|---|
CHANGELOG.md |
版本变更记录(Keep a Changelog 格式),含新增/变更/安全修复/兼容性/已知限制 |
变更记录
- 2.0.1:新增阶段 7(长期运维与故障取证);新增 Wi-Fi 7 双 5GHz/MLO、智能家居兼容网络、
LuCI 主题、流量卸载;新增06/07两篇参考文档;新增mlo_fix80.sh、iot_apply.sh、
unify_wifi_pw.sh、fix_inactivity.sh、reboot_router.py、dump_kxlg.sh、reboot_reason.sh;
故障速查表从 8 条扩到 16 条。 - 1.0:初版 —— 免拆机取 root、全量备份、NAND 双系统刷入、LAN/WAN/WiFi/中文化/端口聚合、回滚。