The embedded BroadMobi BM806C modem (Qualcomm MDM9225, firmware
M1.2.0_E1.0.1_A1.1.8) in the D-Link DWR-921 C1 has two independent
firmware bugs that together break the QMI data plane:
1. Modem accepts 802.3 framing but its 802.3 path is buggy — downlink
frames never reach the host kernel. raw-ip framing works.
2. qmish calls uqmi --start-network --apn <foo>, which triggers
FS#1363: the modem establishes a phantom bearer that gets IP
addresses but has no working data path. Using --start-network
--profile <N> (referencing a pre-configured NVRAM profile with
the same APN) works correctly.
Fixes applied:
- qmish patches (3x community.openwrt.lineinfile):
* Replace --set-data-format 802.3 with raw-ip
* Replace --wda-set-data-format 802.3 with raw-ip
* Bracket raw_ip sysfs write with ip link down/up (kernel rejects
write with -EBUSY when wwan0 is already up)
- Modem NVRAM: create/modify profile 2 (internetipv6, ipv6) for the
IPv6 APN — profile 1 is already managed by qmish's --modify-profile
- UCI wwan: add profile=1 and v6profile=2 so qmish uses --start-network
--profile instead of --apn on both the v4 and v6 legs
- Firewall: add wwan zone (input REJECT, output ACCEPT, forward REJECT)
and Allow-ICMPv6-wwan rule
- main.yml: reorder — packages (including usb-modeswitch) now run
before wwan setup, so the modem is out of EDL mode when wwan.yml
queries it for profile creation
See docs/wwan-bm806c-qmi-workaround.md for the full diagnosis
(what we ruled out, how we confirmed, manual setup steps, component
versions, future upstreaming).
Ansible
Idempotent configuration management for the home-lab network devices.
Devices
| Host | Group | IP | Playbook |
|---|---|---|---|
| crs418 (MikroTik CRS418) | mikrotik |
192.168.255.10 | playbooks/routeros.yml |
| dlink (OpenWrt AP) | openwrt |
192.168.255.11 | playbooks/openwrt.yml |
Both devices are reachable on the MGMT network (192.168.255.0/24) once fully set up.
Dependencies
ansible-galaxy collection install -r requirements.yml
pip install librouteros hvac
Collections used:
community.routeros >= 3.16.0— MikroTik API modulescommunity.hashi_vault >= 7.1.0— OpenBao/Vault secret lookupcommunity.openwrt >= 1.0.0— OpenWrt UCI and shell modules
MikroTik (routeros)
Secrets are fetched at runtime from OpenBao. No credentials are stored in files.
export VAULT_TOKEN=... # or OPENBAO_TOKEN
ansible-playbook playbooks/routeros.yml
Secret layout expected in OpenBao (KVv2, mount secret):
| Path | Fields |
|---|---|
routeros_api |
username, password |
wan_pppoe |
username, password |
router_tailscale |
container_password |
OpenWrt dlink AP
The dlink needs a one-time initialisation before it can be managed through MikroTik. There are two playbooks:
Step 1 — dlink-init.yml (once, PC directly connected)
Run this while your PC is plugged into one of the dlink LAN ports with the device still on its factory IP (192.168.1.1) and your SSH key has been added in web ui. MikroTik must not be in the picture yet.
What it does:
- Reconfigures switch0 so the WAN port becomes a VLAN trunk:
- untagged → VLAN 1 (MGMT, 192.168.255.0/24)
- tagged → VLAN 2 (LAN, 192.168.0.0/24)
- Adds
mgmtinterface: static 192.168.255.11/24, gateway 192.168.255.10 - Reconfigures
lanto a bridge on eth0.2 with no IP (AP mode) - Removes routed
wan/wan6interfaces - Commits and reloads network in the background
After the reload the device is no longer reachable at 192.168.1.1.
ansible-playbook playbooks/dlink-init.yml
Step 2 — connect dlink WAN port to MikroTik ether3
Plug the dlink WAN port into MikroTik ether3.
If the MikroTik config hasn't been applied yet, do it now:
export VAULT_TOKEN=...
ansible-playbook playbooks/routeros.yml
MikroTik ether3 is configured to send MGMT traffic untagged and VLAN 2 (LAN) tagged, which matches what dlink expects on its WAN port.
Step 3 — openwrt.yml (ongoing, via MikroTik)
All subsequent runs connect to 192.168.255.11 through MikroTik:
ansible-playbook playbooks/openwrt.yml
This is the idempotent main playbook. Run it any time to converge configuration.