捡垃圾捡到宝:把淘汰的 ZTE MF253S 4G 模块塞进 Linux 当原生网卡

前言です

事情的起因非常简单:作者在某鱼上花了一杯奶茶钱,淘到一块从旧 CPE(运营商送的 4G 家用路由)里拆出来的 mSATA 模块——ZTE MF253S,方案 ZX297510。

想法也很朴素:插进电脑的 mSATA 槽,当一张原生 4G 网卡用。能用就用,不能用就扔。

しかし……嘻嘻嘻(千早爱音唐笑),这东西不仅不能用,还会让你怀疑人生:插上去,开机 21 秒,它准时重启一次,像极了到点下班的本社畜;ModemManager 一碰它,它直接连人带机跑进 BootROM,连尸体都懒得留。

为什么呢?因为这块模块的固件是给运营商 CPE 定制的,压根没打算让你当普通网卡用。作者翻遍了恩山、读完了 ModemManager 的插件源码、把它家 CPE 的日志都掏出来看了一遍——最后给它写了个内核驱动。现在它已经能在装 stock ModemManager / NetworkManager 的机器上即插即用:任务栏直接弹出「移动数据」,跟正经的 EC20 一个待遇。

这篇文章就把整个折腾过程从原理到代码讲清楚。


硬件参数

项目 详情
模块型号 ZTE MF253S / ME3760V2
基带方案 ZTE Sanechips ZX297510(WF7510)
接口 mini-PCIe / mSATA(USB 2.0 信号)
USB ID 19d2:0199(正常模式)/ 19d2:0256(BootROM)
网络制式 TD-LTE(移动定制,硬件上只校准了 GSM / TD-SCDMA / LTE,没有 WCDMA)
USB 接口布局 if0 = USB-AT,if1 = USB-Rndis(ECM 数据),if2 = USB-Modem,if3 = USB-log,if4 = USB-adb

划重点:if1 是 ECM 数据口,但它不是标准 CDC ECM,主机侧需要自己写驱动绑定


适用人群

会使用 Linux,能自己编译内核模块。此教程并不适合小白——如果你只是想插卡上网,建议直接买正经的 EC20 / RM500U。

准备材料

  • ZTE MF253S / ME3760V2 模块一块
  • 带 mSATA 槽的电脑一台(别用 mSATA 转 USB 的板子,容易供电不足)
  • 极强的动手 / 思考 / 搜索能力
  • AI 一位(这次用的是 DeepSeek V4.1 Flash,能啃几万行 C 代码,比作者自己靠谱多了)
  • 良好的网络环境(你知道我在说什么 👁)

为什么这么难搞(原理)

先说结论:这块模块的固件,从设计上就没考虑过「标准主机栈」这件事。作者踩过的坑依次如下:

  1. QMI 是死路。qmicli --dms-get-manufacturer 能通,但任何 UIM / NAS 类命令都会把它搞崩——直接掉进 BootROM 模式重启。ModemManager 如果按 QMI 去探它,就是无限的 21 秒重启循环。
  2. PPP 拨号被拒绝。LTE 的默认承载在 attach 时自动激活,此时再发 ATD*99***1#,模块直接回 +CME ERROR: 3。想用 PPP?只有刚做完 CFUN 循环的那几秒能用,等于没用。
  3. 没有 DHCP。ECM 网卡口不跑 DHCP 服务端。IP 地址、网关、DNS 只在私有 URC +ZGIPDNS 里出现,长这样:
    +ZGIPDNS: 1,1,"IP","10.x.x.x","0.0.0.0","36.x.x.x","36.x.x.x"
    其中网关字段恒为 0.0.0.0,需要自己推导成「IP+1」。
  4. 数据通道要靠私有命令激活。光发 AT+CGACT=1,1(标准 PDP 激活)是不够的——ECM 数据口必须再收到 AT+ZGACT=1,1 才会真正通数据。这个命令只有它家 CPE 会用。
  5. 21 秒自复位。模块要求主机定时轮询(CSQ / CEREG 之类的保活),不轮询就自杀重启。初始化和心跳序列是作者把它家 CPE 的 RAtlog 日志逆出来的:AT+ZEACT=2AT+CGDCONT=1,IP,CMNET,,0,0AT+ZGAAT=0AT+CFUN=1AT+ZSET=EXCEPT_RESET,1AT+ZEMCI=0
  6. 还有一堆标准命令它不认识。ATZAT+GCAPAT+WS46=? 统统回 +CME ERROR: 6003——而 ModemManager 初始化第一步就发 ATZ,直接卡死在「启用模块」阶段。

方案演进:从守护进程到内核驱动

阶段一:用户态守护进程(能跑,但丑)

最初作者写了个 Python 守护进程:开机 → 打开 AT 口 → 发初始化序列 → 拨号 → 解析 +ZGIPDNS → 自己配 IP / 路由。能上网,问题是一堆:

  • 要在 systemd 里装服务、装 udev 规则、装 modprobe 黑名单——换个发行版就全废,完全没有即插即用;
  • ModemManager 只能看不能用(PPP 拨号被拒),任务栏里的「移动数据」永远连不上;
  • 守护进程和 MM 抢 AT 口,得靠 udev 规则让 MM 装瞎。

能用,但作者很不爽:这明明应该是驱动的活。

阶段二:全部下沉进内核(最终方案)

最终方案是两个内核模块,用户态零辅助进程

模块 职责
zte_ecm.ko 基于 usbnet / usbnet_generic_cdc_bind,绑定 if1(ECM 数据口),生成标准网卡 enpXsYfZuVi1
zte_atfix.ko AT 命令修复 + 伪装 + 真实拨号翻译 + 内核态初始化 / 保活 + +ZGIPDNS 缓存 + %IPDPADDR 应答

驱动是怎么把 ModemManager 骗过去的

让 MM 直接管理这块模块的核心思路:把它伪装成 MM 已经完美支持的另一类模块。作者选了 ZTE 的 Icera 系列(比如 MF821)——因为 MM 的 ZTE 插件对「非 Icera」模块会直接忽略网卡口,而对 Icera 模块则会走「NET 口 + 静态 IP」的 bearer 路径,正好契合这台 ECM-only 的机器。

MM 发出的命令 驱动的处理
ATZ 重写为 ATE0 再转发
AT+GCAP 伪造 +GCAP: +CGSM,+CLTE(让 MM 知道这是 LTE 模块,才会开 EPS 跟踪)
AT+WS46=? / AT+WS46=<n> 伪造模式列表 / 直接回 OK
AT%IPSYS? 伪造 %IPSYS: 0,1,0——关键中的关键,MM 的端口探测只要在响应里看到 %IPSYS: 就把模块归类为 Icera
AT%IPDPACT=<cid>,1 不转发(模块不认识),驱动在私有口上真正执行 AT+CGACT=1,1 + AT+ZGACT=1,1,等数据通道起来后再回 OK 和一条 %IPDPACT: <cid>,1,0 URC
AT%IPDPADDR=<cid> 用嗅探到的 +ZGIPDNS 缓存构造标准响应:IP / 网关(自动 IP+1)/ DNS,让 MM 拿到一份「静态 IPv4 配置」

另外几个细节:

  • 私有口隐身术。驱动接管了 if2(原本给守护进程用的口),做法是把 ModemManager 探口时发的裸 AT 探测直接吞掉——MM 探不到响应,就把这个口标记为「非 AT 口」自动无视。驱动自己的命令走 bulk 端点,根本不过 tty 层。
  • 内核心跳。probe 完成后自动跑 CPE 逆向出来的初始化序列,然后每 2 秒轮询一次 AT+CSQ / AT+CEREG? / AT+COPS?——21 秒自复位从此绝迹。
  • autosuspend 也在驱动里关掉,不再需要 udev 规则。
  • APN 自动识别。驱动读 IMSI 前五位,46000/46002/46004/46007/46008 → CMNET,46001/46006/46009 → 3gnet,46003/46005/46011 → ctnet。

实测效果

装上之后,ModemManager 的视角:

Status | state: registered
       | signal quality: 100%
       | operator name: CHINA MOBILE
       | packet service state: attached
       | ports: enp0s21f0u5i1 (net), ttyUSB0 (at)
       | ignored ports: ttyUSB1 (unknown), ttyUSB2 (unknown)

NetworkManager 里直接出现「移动数据」,点一下就连上,网卡自动配好 IP / 默认路由 / DNS:

$ nmcli connection up "China Mobile"
连接已成功激活

$ ip addr show enp0s21f0u5i1
    inet 10.x.x.x/32 scope global noprefixroute enp0s21f0u5i1

$ ping -I enp0s21f0u5i1 -c 3 223.5.5.5
3 packets transmitted, 3 received, 0% packet loss
rtt min/avg/max/mdev = 51.5/56.4/61.8/4.2 ms

$ curl --interface enp0s21f0u5i1 -o /dev/null -w '%{http_code}' http://www.baidu.com
200

重启电脑?模块自动加载,MM 自动认卡,NM 自动连接。全程无守护进程、无 udev 规则、无手工命令——这才是即插即用该有的样子


⚠️ Tips

  • mSATA 不能热插拔!一定要关机 → 插拔 → 开机。别问,问就是作者重启了三次。
  • 别折腾 QMI。这模块的 QMI 是残废的,一探就崩。内核里建议把 qmi_wwan 拉黑。
  • IPv6 别指望。固件虽然声称支持 IPV6 PDP(实测能激活、能拿到链路本地地址和 DNS),但 ECM 通道没有任何 RA/NDP 转发能力,拿不到全局地址,实际不可用。
  • 网络制式看硬件。TD-LTE 版本只在硬件上校准了 GSM / TD-SCDMA / LTE,插联通的卡是收不到信号的——这不是锁,是物理上就没有 WCDMA 校准数据,别去折腾什么解锁码和全网通固件。
  • 不同批次的模块固件可能略有差异,本文基于 ZTE_MF253SV1.0.0B01

感谢

感谢恩山上那些拆机、抓日志、逆向固件的前辈们——没有你们掏出来的 RAtlog,就没有这篇博客;感谢 ModemManager 的源码(虽然全程都在研究怎么「骗」它,但读懂了是真的强);感谢 DeepSeek V4.1 Flash 帮作者啃完了 ModemManager 的插件逻辑和几万行 C 代码,还顺手把内核模块写完了。

折腾的意义就在于此:一块被运营商淘汰的电子垃圾,在 Linux 手里重新变成了好用的 4G 网卡。

—— DeepSeek V4.1 Flash 爱吃白饭的蓝色大肥鱼

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇