一次中兴光猫救砖与 DDNS-GO (在光猫内安装其他应用) 自启动方案迁移之旅
起因
手里一台 ZTE G7606V6 光猫,ZX279133 双核 A53、512MB 内存、256MB SPI NAND。
起初是为了改光猫 Tr069、远程管理、上报信息相关设置,发现新版固件自带的 DDNS 是灰色的使用不了,虽然可以通过 Telnet 命令直接更改,但是为了方便点想把网页修改成可编辑的,试了一下居然可以。
把原来 /home/httpd/app_ddns_conf_js.gch备份一下,然后把修改版的app_ddns_conf_js.gch放在了/userconfig/app_ddns_conf_js.gch。
看了一下光猫配置和系统,突发奇想能不能在光猫内运行其他应用,比如 DDNS-GO 或者 Lucky。
最初方案很直接:
Telnet 登录,把 DDNS-GO 传到 /opt/cu/apps/ddns-go/,
然后修改 /etc/init.d/rcS
加入
mount --bind /userconfig/app_ddns_conf_js.gch /home/httpd/app_ddns_conf_js.gch和
test -x /opt/cu/apps/ddns-go/ddns-go && SSL_CERT_FILE=/opt/cu/apps/ddns-go/cacert.pem setsid /opt/cu/apps/ddns-go/ddns-go -l 192.168.1.1:9876 -f 600 -c /opt/cu/apps/ddns-go/config.yaml >> /opt/cu/apps/ddns-go/ddns-go.log 2>&1 < /dev/null &替换光猫 DDNS 页面和 DDNS-GO 自启动。
操作时做了 shell 语法检查、端口验证、网页验证,一切正常——直到一次意外断电冷启动。
变砖
通电后光猫所有指示灯不亮,LAN 口物理链路都不起来。接 TTL 看启动日志,U-Boot 读两个槽位都报:
Failure while reading at offset ...
Read on kernel1/kernel2 failed with error -74-74 即 EBADMSG——NAND ECC 无法纠正。两个固件槽位(N60 和 N58)都在各自镜像末尾几页报错。
排查
逐页对比 NAND 数据与以前备份:
- 修改前的最后一页:与原固件逐字节一致
- 紧接着的新页:数据大量不同,U-Boot 报 ECC 失败
两个槽位都在修改前内容结束、后来写入内容开始的第一页发生变化并报 ECC -74:
- kernel2:0x237e000 与 N58 备份逐字节一致;从 0x237e800 起 1875/2048 字节不同并报错。
- kernel1:0x23a6800 与 N60 固件逐字节一致;从 0x23a7000 起 1886/2048 字节不同并报错。原因清楚了:修改 rcS 后,JFFS2 向 rootfs 所在的 NAND 区块写入了新页面,而这些新页的 ECC/OOB 不被当前 U-Boot 正确读取,导致 U-Boot 加载整幅镜像时在这些页失败,加载内核时读不下去,内核因此根本没启动,不是 rcS 执行时卡住,但确实是修改 rcS 所触发的写入导致无法冷启动。
救援
因为我有光猫备份的固件和新固件,所以不用找固件了。
把 CH340(UART TTL 串口转 USB)的电压跳线调成 3.3V 后连接光猫和电脑(如果需要驱动要先安装 CH340 驱动),这款光猫中兴 G7606V6 光猫 UART TTL 串口从上到下顺序是 VCC,TX,RX,GND,CH340和光猫 UART 口的连接方式是
| CH340 USB-TTL | 光猫主板 UART |
|---|---|
| GND | GND |
| TXD | RX(光猫的接收,接 CH340 的发送) |
| RXD | TX(光猫的发送,接 CH340 的接收) |
| VCC/3.3V/5V | 不接(悬空) |
连接好后使用xshell的SERIAL协议连接 COM 端口
在给光猫通电后狂按空格打断然后停在 U-Boot 命令行,此时光猫 LAN 口 2.5G 口连接电脑是不能识别的,需要插后面的千兆 LAN 口,电脑才能识别到本地链路。
通过 TFTP 把修改前的完整 N58 槽位备份加载到 RAM,CRC32 校验通过后,只擦写 kernel2/rootfs2 这一个分区。
写入后整区回读,CRC32 完全一致。重启,N58 回来了,网络正常、网页正常。
然后在 TTL 中打断 U-Boot,把 N60 的槽位也如法炮制:提取原厂 N60 升级包中前 0x23c0000 字节(精确槽位边界,后续的 preplugin.img 排除),TFTP 加载、校验、擦写 kernel1/rootfs1、回读验证。
N60 也恢复正常。最终把 others 分区中的 BootImageNum 从 1 改回 0,让默认启动回到 N60。
教训:不能再改 rcS
修改 rcS 是这次问题的根源,而且两个槽位都改了意味着没有退路。问题是:
rcS位于 JFFS2 rootfs,直接向 rootfs 写入会触发 NAND ECC 不一致;- 主系统 rootfs 是只读挂载的,强行写入本身就说明设计上不该这么用;
- 如果冷启动时 U-Boot 读不了修改后的页面,任何校验机制(Secure Boot、FIT hash)都轮不到执行。
所以需要彻底换一种自启动方案,不能再碰 rcS、不能再写 rootfs。
寻找新的启动方案
Telnet 进入系统探查:
mount 输出显示 /opt/cu/apps 挂载为:
ubi0_0 on /opt/cu/apps type ubifs (rw)这是一个独立的 UBIFS 分区(plugin_data,约 106MB),不属于 N60/N58 任何槽位。
ps w 看到有一堆 OpenWrt 风格的进程:procd、ubusd、rpcd、uappd……进一步看 /proc/2602/root/etc/,发现这其实是一套内嵌的 UFW/OpenWrt 运行环境:
DISTRIB_ID='UFW'
DISTRIB_REVISION='V02.01.0240_arm_zte_ZX279133K54_F'
OpenWrt 19.07.7 for zte_133_K54_hgu_fttrPS:UFW 是 Unified Framework,统一管理框架。
它的根文件系统采用 overlay 挂载:
lowerdir=/opt/cu/framework/ufw/rootfs
upperdir=/opt/cu/apps/apps也就是说,在 /opt/cu/apps/apps/ 下创建的任何文件,都会在 UFW 容器内对应路径下出现。所有系统自带服务都通过 /etc/init.d/ 脚本 + /etc/rc.d/S??* 链接由 procd 管理。
其中 /etc/rc.d/S95done 会执行 /etc/rc.local,而 /etc/rc.d/ 中的每个 S??* 链接在启动时都会被 procd 自动扫描并启动。
新方案实施
移动 DDNS-GO 到 UFW overlay 可见路径:
mv /opt/cu/apps/ddns-go /opt/cu/apps/apps/opt/apps/ddns-go主系统原路径就不保留了。
UFW 容器内通过
/opt/apps/ddns-go/访问。创建 procd 启动脚本(持久 overlay):
1. mkdir -p /opt/cu/apps/apps/etc/init.d /opt/cu/apps/apps/etc/rc.d 2. printf '%s\n' '#!/bin/sh /etc/rc.common' '' 'START=32' 'USE_PROCD=1' '' 'PROG=/opt/apps/ddns-go/ddns-go' '' 'start_service() {' ' procd_open_instance' ' procd_set_param command "$PROG" -c /opt/apps/ddns-go/config.yaml' ' procd_set_param stdout 1' ' procd_set_param stderr 1' ' procd_close_instance' '}' > /opt/cu/apps/apps/etc/init.d/ddns-go 3. chmod 755 /opt/cu/apps/apps/etc/init.d/ddns-go创建的内容为:
#!/bin/sh /etc/rc.common START=32 USE_PROCD=1 PROG=/opt/apps/ddns-go/ddns-go start_service() { procd_open_instance procd_set_param command "$PROG" -c /opt/apps/ddns-go/config.yaml procd_set_param stdout 1 procd_set_param stderr 1 procd_close_instance }内容为标准 OpenWrt procd 服务格式,
USE_PROCD=1。创建 rc.d 启动链接:
ln -sf ../init.d/ddns-go /opt/cu/apps/apps/etc/rc.d/S32ddns-go效果如下
/opt/cu/apps/apps/etc/rc.d/S32ddns-go → ../init.d/ddns-go- 冷启动验证:断电重启后,DDNS-GO 自动运行,PID 为 2779。
整个过程没有修改 N60/N58 rootfs,没有改 rcS,没有写 JFFS2,所有持久化数据都在独立的 UBIFS 分区上。
经验总结
| 总结 | 内容 |
|---|---|
| 不要改 rootfs 内的启动脚本 | 冷启动失败概率高且两个槽位同时损坏后无法回退 |
| 嵌入式设备的持久分区是首选 | 独立于固件槽位,切换 N58/N60 仍保留 |
| 光猫内部可能藏着一套 OpenWrt | procd + ubus + opkg,/opt/cu/framework/ufw/rootfs 是只读基础层 |
| UFW 容器的 overlay 上层是持久入口 | /opt/cu/apps/apps/ 在 plugin_data,不受槽位切换影响 |
| NAND ECC 坑 | 修改 JFFS2 rootfs 时,U-Boot 可能读不了新写的 ECC 页面,这不是校验问题,是硬件层解码失败 |
现在 DDNS-GO 已通过 UFW procd 安全自启,不再依赖 rcS 修改。
原厂 DDNS 页面恢复出厂版本,修改版副本保留在 /userconfig/ 待后续通过更安全的路线(如 httpd 别名)重新启用,但是没有继续测试了,只能使用花生壳的 DDNS 意义也不是很大。
还有内嵌的 LXC 容器内的 OpenWrt 不清楚能不能装 OpenWrt 的哪些插件以及有没有其他可玩性,有兴趣的可以自己试试。
相关推荐
- 暂无相关推荐,看看别的吧。
0 评论