把一台 Handsome OpenStick(UFI001C / MSM8916,Debian 11 arm64,382 MiB RAM) 从「随身 4G 上网棒」改造成「靠 USB 供网的小服务器」的完整经验沉淀:硬件边界、网络架构、Windows ICS 共享、单页服务台、代理与节点治理、以及大量失败尝试的最终结论。
本仓库只记录方法、架构与结论,不含任何订阅地址、节点信息、密钥、口令、二维码或真实公网地址。文中所有账号、密码、控制器 secret 均为占位符;私有网段(192.168.x.x)、端口、服务名、GPIO 编号和设备内路径属于通用技术信息,予以保留。
板载贴片卡不是 eUICC(写不了 eSIM),板载 modem 射频硬件级故障(永久 offline)。这两条是硬件边界,软件无法绕过 ⇒ 上网只能靠 USB gadget 由对端供网(电脑 ICS / 手机以太网共享)或 WiFi 上行。确认边界之后,设备可以长期稳定地跑:2.4G 热点 + 统一域名门户 + 阅读服务 + 代理网关。
| 文档 | 内容 |
|---|---|
| 01-hardware.md | 硬件结论、USB gadget 角色、SIM 切换 GPIO、树外内核模块、adb 与救援通道 |
| 02-network.md | 热点 / USB 上行 / NAT / mDNS / DNS 链路、上行模式切换、透明代理、验收基线 |
| 03-windows-ics.md | Windows ICS 共享的三个大坑、COM/WMI 修复顺序、开机与周期守护 |
| 04-portal.md | 单文件 iOS 风格服务台、后端 API、文件管理、节点清理、每日免费节点、整备基线 |
| 05-troubleshooting.md | 排障实录:15 个主题的「现象 → 原因 → 结论」,含已验证不可行清单 |
| scripts/ | 脱敏后的可复用脚本(ICS 守护、订阅规范化、节点测活/抓取、验收等) |
手机 / 笔记本
│ WiFi(自建 2.4G 热点,192.168.12.0/24)
▼
┌────────────────────── OpenStick ──────────────────────┐
│ hostapd + dnsmasq wlan0 192.168.12.1 │
│ nginx → / 单页服务台 + /api/ 反代 │
│ mihomo mixed 7890 / redir 7892 / │
│ tproxy 7893 / DNS 1053 / 控制器 │
│ reader :5888 · moread :8080 · ttyd :7681 │
│ avahi (mDNS) → http://openstick.local/ │
│ │
│ usb0 192.168.137.254/24 ── 默认路由 via 192.168.137.1 │
└──────────────────────────┬───────────────────────────┘
│ USB gadget(NCM)
▼
电脑「UsbNcm」网卡 192.168.137.1
Windows ICS:NAT + IP Forwarding
│
▼
电脑物理网卡 → 公网
关键设计:设备不依赖板载 modem,把 USB 口伪装成网卡向电脑要网;电脑侧用 ICS 做一次 NAT;设备再把自己的热点共享出去,于是手机连热点也能上网。所有管理入口收敛到 http://openstick.local/,USB 侧和热点侧解析到同一地址。
- 先确认硬件边界,再写代码。 前期大量时间耗在「给贴片卡写 eSIM」「救活 4G」上,最终结论都是硬件不支持。判据见 01 / 05。
- USB 口是 gadget(Device),不是 Host。 强制切 host 会让 adb 永久消失、必须断电恢复;设备也没有受控 VBUS。「让设备当主机接手机共享」这条路已实测堵死。
- ICS 不是「勾上共享」就完事。 幽灵网卡标志残留会让所有
EnableSharing报0x80040201;只写 WMI 不启动 NAT;SharedAccess重启会把 IP Forwarding 关掉。顺序必须是「清标志 → COM 双激活 → 重启服务 → 最后开转发」。 - 守护脚本要健康秒退。 每 10 分钟跑一次,状态正常就写心跳退出,绝不无脑重启服务;心跳写在
C:\ProgramData\OpenStick\而不是 SYSTEM 的%TEMP%。 - PowerShell 5.1 脚本必须纯 ASCII,网卡名运行时动态取,否则中文网卡名会 GBK 乱码导致参数验证失败。
- mDNS 的 AAAA 会坏事。 avahi 设
use-ipv6=no,别让openstick.local广播到不存在的地址;曾经把设备搞成「电脑能 ping IP、打不开域名」。 - HTTP provider 的节点清理不能删缓存。 订阅一刷新节点全回来。正解是「持久黑名单 + feed 生成时过滤 +
PUT /providers/proxies/<name>热重载」,并按来源隔离测活记录。 - 判空正则要认 inline map。
^\s*-\s*name:不认- {name: ...},会把订阅缓存清成proxies: [];「文件非空就原样服务」更稳。 - 所有自动清理都要安全闸。 整源全失效时跳过、过滤后为空时保底返回原文,否则空 provider 会让 mihomo 返回 503;一键删文件要服务端复验类别、
rmdir只删空目录。 - 382 MiB 内存下,裁剪比优化有效。 最后只留「看书 + 代理 + 门户」,把监控 / 下载 / 媒体 / 面板 / 通知全部卸载,服务从二十多个降到 9 个,
systemctl --failed为 0。
- 板载卡写 eSIM profile、板载 4G 恢复注册网络;
- USB 切 Host 模式接手机 USB 网络共享;
- 网络 adb(
adb tcpip 5555):Debian 版 adbd 只听 127.0.0.1:5037; - 用
nmcli c reapply(本机不支持,要nmcli d reapply usb0); - 靠
192.168.100.1之类的第二固定入口做兼容(会与 avahi / ICS 互相干扰); - 在设备上跑 Docker / 重型面板 / 多套下载器(内存与 I/O 都不划算)。
详见 05-troubleshooting.md 附录 A。
电脑上的 TUN 模式代理(会创建虚拟网卡并接管 0.0.0.0/1 + 128.0.0.0/1)会把 ICS 转发出去的包再代理一次;设备自身已开透明代理时就会变成双层代理,表现为延迟升高、国内站点绕远。只用系统代理模式则不影响 ICS 的内核 NAT 转发。判断方法:看默认路由有没有被拆分、对比设备与电脑的出口 IP 是否一致。建议把 192.168.137.0/24、192.168.12.0/24、openstick.local 加入代理绕过列表。
- 仓库里的脚本是脱敏后的参考实现:secret、口令、热点凭据改为环境变量或占位符,部署路径为通用路径,请按自己环境修改后再用。
- 服务台前端与后端 API 的完整线上代码未收录,04 文档描述其结构与关键机制。
- 任何涉及
iptables/nftables/ ICS / systemd 的操作都可能中断网络,操作前务必保留热点 Web 终端这条带外救援通道。 - 本仓库为个人经验记录,不构成对任何硬件或网络配置的保证。