Files
x1pro/docs/project_v46_v461_development_and_failures.md
zhangxiaokun 29cf67154a v46.1-ui-mt76: Argon theme + hardened WG tunnel + Link Health dashboard
基于 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; 尚未刷机.
2026-08-19 15:04:02 +08:00

12 KiB
Raw Permalink Blame History

v46 / v46.1 换固件开发经历与翻车记录

2026-08-17 → 2026-08-19Cudy 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 commitnetifd reload wifi → 新的 netdev up 事件 → hotplug 再触发 → commit→reload→event 死循环

三条铁律(对一切新固件生效)

  1. 任何会触发 wifi reload 的逻辑,绝对不允许放进 hotplug.d/net。netdev 事件是 reload 的直接结果,一放进去就是自激回路。
  2. uCI commit 本身不触发 netifd,结构上不可能循环——只有在 hotplug 事件里主动调 reload 才会。要让状态稳定,要么 commit-only,要么 log-only,绝不 reload。
  3. 检测类逻辑要「连续两次读数一致才算稳定」(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。
  • 下载腰斩 bugopenwrt/mt76#980):5G 侧已被 commit 0d6eaa42025-09-18precal-data offset)修复,构建树的 mt76 PKG_SOURCE_DATE=2025-11-06 已包含。2.4G 可能略慢于原厂。
  • WED bugopenwrt/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.10immortalwrt-mt76mt76 原生无冲突。

四、v45.1 六连翻车(2026-08-17 一天六个版本,五个假说全证伪)

v45.1 — mt76 CLEAN 官方树(md5 202e4a15...

干净 mt76 基线 + 移植 WG/VXLAN/隧道。发现 mt76 macaddr bug:驱动读到 multicast 位 MACd1:fd)→ hostapd 拒 UP。解法:wifi-iface 设合法单播 MACfc:a0),wifi-device 层无效。已固化。

v45.2 — ip-full 修 VXLAN 空壳(md5 4dd520ac...

症状:刷 v45.1 后「办公室服务完全访问不了」。真根因v45 用了 ip-tinyip link add x type vxlan id 10 直接报错,netifd vxlan proto 有 bug 建出空壳 vxlan0vni/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=mmodule),built-in 保证 udp_tunnel encap 基础设施先就绪。修复尝试:强制 =y built-in,去 kmod-vxlan。另烤入两 iface uapsd=0(手机 WiFi 51→2ms,这个保留了下来)。

v45.6 — 关 JUMP_LABELmd5 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 zonewg0 不属于任何 zone。办公室回程的 VXLAN 包从 wg0 进 INPUTfw4 只接受 br-lan/eth010.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-inv45.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.151fa661850 主树 + 350d41da LuCI feed):

  • LAN br-lan+eth1,管理 IP 192.168.1.2/24WAN eth0 DHCP 仅承载自身 + WG underlay
  • WG 10.99.0.2/32 ↔ 办公室 10.99.0.1/32VXLAN VNI 10/UDP 4789/nolearning 入 br-lan
  • wg0lan 同属 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.bin14,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 包都是 =mmodule)。=m 只是「编译成 .ipk 但不装入镜像」,进镜像的包必须是 =y。改成 =y + make defconfig 重编 → rootfs +408KB,新增 70 文件全 UI。

教训make 成功 ≠ 包进镜像。增量构建后用 diff -rq 对比新旧 rootfs 清单,0 新增就该立刻警觉。

翻车 2theme-argon 的 6 张图片被源树「删掉」

feeds/luci 工作区里 argon.svg logo、bg1.jpg 壁纸 fallback159KBblank.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.bin14,797,623 bytes, SHA-256 629cc6a3...),K 盘双份 hash 复核一致
  • kernel 成员与 v46 byte-identical366f1c78...4,375,268 bytes)——增量构建复用 v46 kernel object,绝不 dirclean
  • rootfs +408KB:新增 70 文件全 UI0 删除,共享文件内容变化 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/BBRiw 6.9-r1 进镜像(Link Health 依赖)
  • 烤入的均为硬化版:rows.pushwhich_bin 后端、handleSaveApply:null 禁用、sysauth local-wins、无 ui.changes.apply

状态:增量构建 + 静态验收通过,尚未刷机/实机验收


七、方法论沉淀

  1. 先查配置,再怀疑内核。v45 的三个内核假说全错,真凶是防火墙 zone 里少写一行 wg0——最简单的地方。为了修这一行,我们下探到改内核内存(built-in、JUMP_LABEL、kprobe 插桩)全白费。排查顺序永远从浅到深:/etc/config/* → 路由表 → nft 规则 → 内核。越简单的根因越藏得深。
  2. =y 才进镜像,=m 只是 .ipk。构建完必须解包对比 rootfs 清单,0 增删=没烤进去。
  3. 前端 JS 别抄 ucode 语法push()/match()/length() 在浏览器 JS 里语义完全不同,跨层复用代码前先想清楚运行环境。
  4. netdev/hotplug 里绝不 reload wifiv25 铁律)。commit-only / log-only 才安全。
  5. 内核验证靠对比,不靠直觉。kernel 成员 byte-identical 是「没动内核」的唯一可靠证据。
  6. 预览期就在 live overlay 上验证前端,bug 修复后立即烤进固件,避免「预览 OK / 固件又坏」的二次翻车。

八、附:v46.1 提交内容(git

仓库:K:\SynologyDrive\学习\Openwrt\.v46.1-ui-src\commit ae721f8

  • luci-theme-argonlocal-wins 登录背景 + bg1.jpg fallback
  • luci-app-argon-config(去 ui.changes.apply、ACL mutator 移 write
  • luci-app-wgtunnelrpcd 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