基于 v46 正式版(mt76)只恢复 UI 层: - luci-theme-argon 2.4.3: local-background-wins 登录页 + bg1.jpg fallback - luci-app-argon-config: 去 ui.changes.apply, ACL mutator 移 write - luci-app-wgtunnel: rpcd ucode 后端(get/status/prepare/apply/rollback/reconnect), JSONMap 前端, 60s 一次性 token, 快照回滚, 无全局 network ACL - luci-app-tr3000-status: 只读 rpcd ucode + 5s 轮询仪表盘 - tools/: audit_ui_packages.py + install_preview.sh + rollback_watchdog.sh - docs/: 开发经历与翻车记录 + 固件哈希记录 固件本体(含烤入 WG 私钥/PSK)不入 git, 仅 K 盘保存. kernel 成员与 v46 byte-identical; 尚未刷机.
12 KiB
v46 / v46.1 换固件开发经历与翻车记录
2026-08-17 → 2026-08-19,Cudy TR3000 (MT7981B, ImmortalWrt 24.10, mt76, Linux 6.6.151)。 本文记录从 mt_wifi 私有驱动迁到 mt76 开源驱动、最终落地 v46 正式版 + v46.1 UI-only 恢复的完整经历,以及每一处翻车的根因。
一、为什么换固件(动机)
老固件(Gzxhwq mt_wifi 系)在 2026-07 的整个 DBDC/HE160 战役后,数据面仍不稳定:
- mt_wifi 私有驱动:
wifi reload卡死、重启后数据面时好时坏(assoc 正常但 0 数据包)、计数器说谎(Tx=0 但流量在跑)、UCI 状态与物理广播不一致。 - DBDC merge 字段 swap:UCI 必须反向写才能广播正确,LuCI 显示天然错位,每次开机 ra0/rax0 ↔ dat 绑定还随机。
- 无法 LuCI 完全接管:改无线要么不生效、要么重启盒子,v25 更是直接变砖(见下)。
用户评估后拍板:「评估开源的吧」「用官方树重编(干净)」。
二、v25 教训(先把它钉死,别让历史重演)
用户特别要求把 v25 的教训记进来。它来自 2026-07-20 的 mt_wifi 时代,但教训完全适用 mt76。
现象:sysupgrade-v25.bin 刷入后无限重启,必须 U-Boot 回刷 v11 恢复。
根因:files/etc/hotplug.d/net/99-wifi-band-sync 这个脚本,在 ra*/rax* netdev up 事件里 uci commit → netifd reload wifi → 新的 netdev up 事件 → hotplug 再触发 → commit→reload→event 死循环。
三条铁律(对一切新固件生效):
- 任何会触发 wifi reload 的逻辑,绝对不允许放进
hotplug.d/net。netdev 事件是 reload 的直接结果,一放进去就是自激回路。 - uCI commit 本身不触发 netifd,结构上不可能循环——只有在 hotplug 事件里主动调 reload 才会。要让状态稳定,要么 commit-only,要么 log-only,绝不 reload。
- 检测类逻辑要「连续两次读数一致才算稳定」(v24 教训:在驱动 init 中途 30s 抓到瞬态 2.4G 读数把 UCI 改反)。v27 的 commit-only verifier 仍会在 reload 间反复翻 UCI,最终 v28 改 log-only + 显示层 patch 才根治。
三、mt76 迁移评估(2026-08-17)
先做研究(project_mt76_migration_research.md):
- mt76 就是 TR3000 的官方驱动(
DEVICE_PACKAGES=kmod-mt7915e),Cudy 官方即 mt76。 - 下载腰斩 bug(openwrt/mt76#980):5G 侧已被 commit
0d6eaa4(2025-09-18,precal-data offset)修复,构建树的 mt76PKG_SOURCE_DATE=2025-11-06已包含。2.4G 可能略慢于原厂。 - WED bug(openwrt/openwrt#20473):WED 开启时可能掉到 ~140Mbps,需要关注。
- 关键决策:不用「嫁接」——在 mt_wifi 树上恢复 mt76+mac80211-scripts+wifi-scripts 会撞无底洞 patch 冲突(mt_wifi 树用 backports 6.12.6,官方 24.10 用 6.12.96)。全新克隆官方
openwrt-24.10树immortalwrt-mt76,mt76 原生无冲突。
四、v45.1 六连翻车(2026-08-17 一天六个版本,五个假说全证伪)
v45.1 — mt76 CLEAN 官方树(md5 202e4a15...)
干净 mt76 基线 + 移植 WG/VXLAN/隧道。发现 mt76 macaddr bug:驱动读到 multicast 位 MAC(d1:fd)→ hostapd 拒 UP。解法:wifi-iface 设合法单播 MAC(fc:a0),wifi-device 层无效。已固化。
v45.2 — ip-full 修 VXLAN 空壳(md5 4dd520ac...)
症状:刷 v45.1 后「办公室服务完全访问不了」。真根因:v45 用了 ip-tiny,ip link add x type vxlan id 10 直接报错,netifd vxlan proto 有 bug 建出空壳 vxlan0(vni/remote/dstport 全空,帧封装不进 VXLAN)。老 mt_wifi 固件有 ip-full 所以 20-vxlan 脚本能跑。修复:CONFIG_PACKAGE_ip-full=y + 关 ip-tiny + ip-bridge=y。
v45.3 — vxlan BUILT-IN 内核(md5 2b4980ba...,假说一)
症状:仍 RX=0。假说:CONFIG_NET_UDP_TUNNEL/CONFIG_NET_IP_TUNNEL/CONFIG_VXLAN 是 =m(module),built-in 保证 udp_tunnel encap 基础设施先就绪。修复尝试:强制 =y built-in,去 kmod-vxlan。另烤入两 iface uapsd=0(手机 WiFi 51→2ms,这个保留了下来)。
v45.6 — 关 JUMP_LABEL(md5 330c923a...,假说二)
症状:仍 RX=0。kprobe/ftrace 铁证:VXLAN 回程帧到 wg0 后 udp_queue_rcv_one_skb 被调 23×,udp_encap_enable() 执行(static_branch_inc++),但 vxlan_rcv 从不执行 0× → ARM64 6.6.151 的 jump-label patch 没生效(老 6.6.95 没这 bug)。修复尝试:关 CONFIG_JUMP_LABEL 让 static_branch_unlikely 退化成普通运行时读 key。仍失败。
假说三(v45.4/997 VXLAN GRO patch、v45.5-dbg/ftrace+kprobe、v45.6/998 WG GRO bypass)全是内核方向,现场抓取证,最终全部证伪,这些实验 patch 在 v46 全清。
最终真根因(2026-08-18,v46 前夕)—— 不是内核!
vxlan0 RX=0 的真根因是:/etc/config/firewall 只把 lan 放 LAN zone、wan wan6 放 WAN zone,wg0 不属于任何 zone。办公室回程的 VXLAN 包从 wg0 进 INPUT,fw4 只接受 br-lan/eth0,10.99.0.2:4789 落入 handle_reject。所以 WG handshake/transfer 正常、wg0 抓得到 ARP Reply,但 vxlan0 RX 恒 0。
修复(授权后现场):uci add_list firewall.@zone[0].network='wg0' + 重载 fw4。立即 ping 192.168.1.1 8/8、RX +54、HTTP/HTTPS/DNS 正常、OpenClash fake-IP 198.18.25.135。
血泪总结:v45.1→v45.6 六个版本,两个是环境工具(ip-tiny、macaddr),三个是内核假说(built-in、JUMP_LABEL、GRO/GRO-bypass)全部证伪,真凶是防火墙 zone 归属——/etc/config/firewall 里少写一个 wg0 的最普通的一行配置。先查配置,再怀疑内核。
讽刺的是:为查这个「最简单的地方」,我们把调试下探到了最深——改内核内存。v45.3 把 NET_UDP_TUNNEL/VXLAN 强制 built-in;v45.6 关 CONFIG_JUMP_LABEL 让 jump-label 不再 patch 内核内存里的代码(ARM64 6.6.151 的 static_branch 代码补丁疑似失效);v45.5-dbg 用 kprobe/ftrace 在内核内存里插桩取证据(udp_queue_rcv_one_skb 被调 23×、vxlan_rcv 0×)。也就是说,从 uci add_list 这一行就能修好的问题,我们一路干到动内核内存——越简单的根因越容易藏在最深的地方,排查顺序应当反过来:先配置、后路由、再 nft、最后才内核。
五、v46 正式版落地(2026-08-18)
全量 make dirclean 构建(kernel 6.6.151,fa661850 主树 + 350d41da LuCI feed):
- LAN
br-lan+eth1,管理 IP192.168.1.2/24;WANeth0DHCP 仅承载自身 + WG underlay - WG
10.99.0.2/32↔ 办公室10.99.0.1/32;VXLAN VNI 10/UDP 4789/nolearning入 br-lan wg0与lan同属 LAN zone(上面真根因的固化)- 零 forwarding、WAN 无 masq、flow offload 关、
kmod-br-netfilter+ MSS clamp 1330 - 小路由 DHCP/RA/DHCPv6/DNS redirect 全关,客户端由办公室 192.168.1.1 供
- 两路 mt76 AP 保留合法单播 MAC +
uapsd=0 /etc/config/{network,firewall,dhcp,wireless}打包权限0600
产物:sysupgrade-v46-formal-mt76.bin(14,388,023 bytes, SHA-256 261bcb92...)。已刷机、已实机验收(WG/VXLAN、防泄漏、MSS 1330、mt76 HE160、1GbE 全达标)。
六、v46.1 UI-only 恢复(2026-08-19)—— 第二轮翻车
v46 是纯 Bootstrap 基线。用户要恢复 Argon + Bing 壁纸 + WG 隧道插件 + 只读 Link Health 仪表盘。约束:不恢复 turboacc/HNAT/WARP/BBR/mt_wifi/wrtbwmon 或任何可写加速开关。
翻车 1:=m 只编译不进镜像(本轮最坑)
首次 make → rootfs 与 v46 完全同尺寸 (10000384 bytes),新增文件 0!
.config 里 5 个 UI 包都是 =m(module)。=m 只是「编译成 .ipk 但不装入镜像」,进镜像的包必须是 =y。改成 =y + make defconfig 重编 → rootfs +408KB,新增 70 文件全 UI。
教训:make 成功 ≠ 包进镜像。增量构建后用 diff -rq 对比新旧 rootfs 清单,0 新增就该立刻警觉。
翻车 2:theme-argon 的 6 张图片被源树「删掉」
feeds/luci 工作区里 argon.svg logo、bg1.jpg 壁纸 fallback(159KB)、blank.png、volume×2 全部消失——这就是「登录页背景不显示」的真根因,不是 upstream 没 ship。git checkout 恢复。Bing 失败后的本地 bg1.jpg fallback 由此生效。
翻车 3:前端 ReferenceError: push is not defined
Link Health 页面「健康检测全没数据」。根因:后端 ucode 用全局 push(),前端 JS 我沿用了 push(rows, ...),但浏览器 JS 里 push 不是全局函数,必须 rows.push(...)。全量扫了三个前端 JS,只此一处。
翻车 4(预览期,已修):两个状态解析 bug
- 路由 present=false:真实路由
10.99.0.1 dev wg0 scope link是正常 host 路由,但ip -j输出dst:"10.99.0.1"(省略/32),后端只比对10.99.0.1/32漏判。改为同时接受裸地址与/32。 - vxlan destination_port=null:真实
dstport 4789,但 iproute2 在 JSON 里省略默认端口字段。缺省即等于 4789,缺省按 REQUIRED_VXLAN_PORT 处理。
v46.1 产物与验收(全过)
sysupgrade-v46.1-ui-mt76.bin(14,797,623 bytes, SHA-256629cc6a3...),K 盘双份 hash 复核一致- kernel 成员与 v46 byte-identical(
366f1c78...,4,375,268 bytes)——增量构建复用 v46 kernel object,绝不 dirclean - rootfs +408KB:新增 70 文件全 UI,0 删除,共享文件内容变化 0
- denylist 零变化:
/etc/config/{network,firewall,dhcp,wireless}(0600)、rc.local、20-vxlan、30-mss-clamp、mss nft 全 hash 一致 - manifest 无禁项:无 turboacc/wrtbwmon/mtkhnat/WARP/mt_wifi/BBR;
iw6.9-r1 进镜像(Link Health 依赖) - 烤入的均为硬化版:
rows.push、which_bin后端、handleSaveApply:null禁用、sysauth local-wins、无ui.changes.apply
状态:增量构建 + 静态验收通过,尚未刷机/实机验收。
七、方法论沉淀
- 先查配置,再怀疑内核。v45 的三个内核假说全错,真凶是防火墙 zone 里少写一行
wg0——最简单的地方。为了修这一行,我们下探到改内核内存(built-in、JUMP_LABEL、kprobe 插桩)全白费。排查顺序永远从浅到深:/etc/config/*→ 路由表 → nft 规则 → 内核。越简单的根因越藏得深。 =y才进镜像,=m只是 .ipk。构建完必须解包对比 rootfs 清单,0 增删=没烤进去。- 前端 JS 别抄 ucode 语法。
push()/match()/length()在浏览器 JS 里语义完全不同,跨层复用代码前先想清楚运行环境。 - netdev/hotplug 里绝不 reload wifi(v25 铁律)。commit-only / log-only 才安全。
- 内核验证靠对比,不靠直觉。kernel 成员 byte-identical 是「没动内核」的唯一可靠证据。
- 预览期就在 live overlay 上验证前端,bug 修复后立即烤进固件,避免「预览 OK / 固件又坏」的二次翻车。
八、附:v46.1 提交内容(git)
仓库:K:\SynologyDrive\学习\Openwrt\.v46.1-ui-src\,commit ae721f8:
luci-theme-argon(local-wins 登录背景 + bg1.jpg fallback)luci-app-argon-config(去ui.changes.apply、ACL mutator 移 write)luci-app-wgtunnel(rpcd ucode 后端 get/status/prepare/apply/rollback/reconnect + JSONMap 前端 + 60s 一次性 token + 快照回滚,无全局 network ACL)luci-app-tr3000-status(只读 rpcd ucode + 5s 轮询仪表盘 + which_bin + expect-permissive + rows.push)audit_ui_packages.py+install_preview.sh+rollback_watchdog.sh