OpenWrt package that fixes bufferbloat and monitors connectivity on LTE/5G modems.
Modem Doctor auto-configures CAKE traffic shaping, disables hardware offloading that bypasses it, and runs a watchdog that detects high latency or connection drops — automatically recovering by reselecting a better cell tower. Includes a LuCI web interface for configuration and real-time status monitoring.
Status: Early release. Developed and tested on a single device (GL-E750 Mudi V2 with Quectel EM060K-GL, OpenWrt 22.03). The core fixes (SQM/CAKE, queue tuning, GRO, TCP) are standard Linux networking and should work on any OpenWrt router with a cellular modem. The AT command layer currently targets Quectel modems. Feedback and testing on other hardware is welcome.
- SQM/CAKE Traffic Shaping — Auto-configures CAKE qdisc to prevent bufferbloat, the main cause of latency spikes under load
- Hardware Offloading Management — Detects and disables Qualcomm SFE which silently bypasses CAKE, making traffic shaping ineffective
- Smart Watchdog — Monitors connectivity and latency with progressive recovery (interface restart -> airplane mode toggle -> hard modem reset) and exponential backoff
- Cell Reselection — Automatically forces the modem to find a better tower when latency exceeds the threshold
- Modem Tweaks — Reduces TX queue length, disables GRO, disables modem sleep, tunes TCP at boot
- LuCI Web Interface — Real-time modem status dashboard with signal quality, temperature, and configuration UI
- AT Command Abstraction — Automatically detects gl_modem, sms_tool, comgt, or direct serial
| Problem | Cause | Fix |
|---|---|---|
| Latency spikes under load (1-3s+) | Bufferbloat in modem queue | CAKE traffic shaping |
| CAKE not working on forwarded traffic | Hardware offloading (SFE) bypassing tc | Auto-disable SFE modules |
| Random latency spikes at idle | Modem sleep mode wake-up | AT+QSCLK=0 |
| Jitter from packet batching | GRO aggregating packets | Disable GRO on wwan |
| TCP performance drops after idle | Slow start after idle | sysctl tuning |
| Stuck on bad cell tower | No automatic reselection | Watchdog with airplane mode toggle |
| Connection drops | Modem/interface hangs | Progressive recovery escalation |
| Router | Modem | OpenWrt | Status |
|---|---|---|---|
| GL-E750 Mudi V2 | Quectel EM060K-GL (4G LTE Cat 6) | 22.03 | Working |
The AT command library handles both modern and legacy Quectel command formats. These modems use the same AT interface and should work, but have not been tested:
- Modern Quectel (5G): RM500Q-GL, RM520N-GL
- Legacy Quectel (4G): EC25, EG25-G, EP06-E/A, EM06, BG96
If you test on any of these, please open an issue with your results.
Most of modem-doctor's value comes from Linux kernel-level fixes (SQM/CAKE, TX queue tuning, GRO disable, TCP tuning) and 3GPP standard AT commands (AT+CFUN for recovery). These work with any cellular modem. The hardware offloading disable currently targets Qualcomm SFE as found on GL.iNet routers — other offloading engines (MediaTek HNAT, nftables flow offload) are not yet handled. Sleep mode disable (AT+QSCLK) and signal monitoring are Quectel-specific. Contributions for other chipsets and routers are welcome.
- OpenWrt 22.03 (tested; newer versions should work)
- A Quectel LTE/5G modem (see Compatibility)
- Dependencies installed automatically:
comgt,jsonfilter,ethtool,sqm-scripts,kmod-sched-cake
Download the latest .ipk files from Releases, then:
# -O flag required for OpenWrt's Dropbear SSH (no sftp-server)
scp -O modem-doctor_*.ipk luci-app-modem-doctor_*.ipk root@192.168.8.1:/tmp/
ssh root@192.168.8.1 'opkg install /tmp/modem-doctor_*.ipk /tmp/luci-app-modem-doctor_*.ipk'Add to your feeds.conf.default:
src-git modem_doctor https://github.com/francesco-carrella/openwrt-modem-doctor.git
Then:
./scripts/feeds update modem_doctor
./scripts/feeds install -a -p modem_doctor
make menuconfig # Select Network -> modem-doctor and LuCI -> luci-app-modem-doctor
make package/modem-doctor/compile
make package/luci-app-modem-doctor/compileVia LuCI: navigate to Services -> Modem Doctor -> Configuration and enable.
Or via CLI:
uci set modem-doctor.main.enabled=1
uci commit modem-doctor
/etc/init.d/modem-doctor startAt startup, Modem Doctor:
- Disables hardware offloading (SFE) — Qualcomm's Shortcut Forwarding Engine fast-paths packets around the Linux networking stack, completely bypassing CAKE. Modem Doctor unloads the SFE kernel modules so CAKE can shape all forwarded traffic.
- Configures CAKE — Creates an SQM queue on the modem interface with CAKE qdisc, applying download/upload bandwidth limits. CAKE manages the packet queue to keep latency low even when the link is saturated.
Set bandwidth to ~80% of your measured connection speed. If set too high, the modem's internal queue fills up and CAKE can't help.
Every 2 minutes (configurable):
- Connectivity check — Pings target (default 1.1.1.1). If all pings fail, progressive recovery:
- Step 1: Interface restart (ifdown/ifup)
- Step 2: Airplane mode toggle (AT+CFUN=4/1)
- Step 3: Hard modem reset (AT+CFUN=1,1)
- Exponential backoff prevents hammering during extended outages
- Latency check — Measures average ping latency. If above threshold (default 500ms):
- Forces cell reselection via airplane mode toggle
Applied once at service start:
- TX queue length set to 100 (stock is 1000 — prevents bufferbloat)
- GRO disabled on wwan device (prevents latency batching)
- Modem sleep disabled (AT+QSCLK=0 — prevents idle-to-active latency spikes)
- TCP slow start after idle disabled (sysctl)
All settings are individually toggleable via the LuCI configuration UI.
Automatically detects and uses the best available method:
gl_modem— GL.iNet routerssms_tool— 4IceG ecosystemcomgt— Standard OpenWrt- Direct serial — Fallback to /dev/ttyUSB2
modem-doctor/ # Backend package
├── Makefile
└── files/
├── etc/
│ ├── config/modem-doctor # Default UCI config
│ ├── init.d/modem-doctor # procd init script
│ └── uci-defaults/80-modem-doctor # First-boot setup
└── usr/
├── bin/
│ ├── modem-doctor.sh # Watchdog daemon
│ └── modem-doctor-setup.sh # Boot-time fixes + SQM config
└── lib/modem-doctor/
└── modem-doctor-lib.sh # AT command abstraction
luci-app-modem-doctor/ # LuCI web interface package
├── Makefile
├── htdocs/.../view/modem-doctor/
│ ├── status.js # Real-time status dashboard
│ └── config.js # Configuration form
└── root/
├── etc/uci-defaults/ # LuCI cache clear
└── usr/
├── libexec/rpcd/
│ └── luci.modem-doctor # rpcd JSON API
└── share/
├── luci/menu.d/ # Menu entries
├── rpcd/acl.d/ # Access control
└── ucitrack/ # UCI -> init linkage
See CONTRIBUTING.md for development setup and guidelines.
# Deploy to router for testing
./deploy.sh root@192.168.8.1
# Build .ipk packages locally (without SDK)
./create-ipk.sh