Skip to content

feat: ESP32-less Linux bridge for uConsole CM4 — WiFi/BLE scanning, handshake capture, deauth#7

Open
FusedStamen wants to merge 6 commits into
LOCOSP:mainfrom
FusedStamen:bridge-stability-pr
Open

feat: ESP32-less Linux bridge for uConsole CM4 — WiFi/BLE scanning, handshake capture, deauth#7
FusedStamen wants to merge 6 commits into
LOCOSP:mainfrom
FusedStamen:bridge-stability-pr

Conversation

@FusedStamen
Copy link
Copy Markdown
Contributor

Overview

This PR adds a complete ESP32-less bridge for running WatchDogsGo on a ClockworkPi uConsole CM4 with HackerGadgets AIO v1, using the host's WiFi and Bluetooth hardware instead of a physical ESP32.

Tested on: uConsole CM4 + AC1200 (MT7921u, wlan1) + AWUS036ACM (wlan2) + CM4 internal BT (hci0)

Handshake capture and deauth require an external monitor-capable adapter on wlan2
(tested with AWUS036ACM / MT7612U). WiFi scanning and BLE work with built-in hardware only.

Files

wdg_wifi_bridge.py (updated)

Addresses all code review feedback from the previous round:

  • TOCTOU fix — PTY symlink creation uses mkdtemp + os.rename (atomic)
  • SIGTERM handler - signal.signal(SIGTERM, ...) calls stop() cleanly on systemctl stop; no more dangling symlink or monitor mode on shutdown
  • --iface validation - checks ip route get 8.8.8.8 before allowing monitor mode flip; prevents accidental default route loss on typo
  • pip advice - log message now points to .venv/bin/pip install bleak, not --break-system-packages
  • Scan rate limiter - 5s cooldown between live iw scan calls; returns cached results on rapid requests, preventing mt7921u timeout
  • Cache fallback - on iw scan timeout, serves last known results instead of empty
  • version command - returns firmware version string the game expects (was silently ignored)
  • Popen-based dispatch - start_handshake / stop_handshake and start_sniffer / stop_sniffer use Popen with proper start/stop toggle support
  • HS stderr logging - hs_capture.py stderr is forwarded to journalctl via _hs_stderr_loop
  • Deauth support - start_deauth dispatches aireplay-ng -0 5; checks monitor mode before setting to avoid race with hs_capture

hs_capture.py (new)

Standalone handshake capture script dispatched by the bridge:

  • airodump-ng + hcxpcapngtool polling pipeline on wlan2
  • Handles both WPA* and 22000* hash formats
  • Writes per-BSSID loot files matching loot_manager.py naming convention: _.txt / .22000 / .pcap (tshark-filtered)
  • _find_active_session() writes to the current game session directory
  • Skips monitor mode set if wlan2 is already in monitor (e.g. left by deauth)
  • SIGTERM/SIGINT handlers for clean teardown; restores wlan2 to managed mode on exit
  • Poll loop uses 0.5s increments so SIGTERM wakes it quickly

docs/uconsole-setup.md (new)

Full setup guide covering:

  • Hardware role table (wlan0/wlan1/wlan2/hci0/hci1/SDR/GPS)
  • System dependencies (airodump-ng, hcxpcapngtool, tshark - iw and dump1090 handled by setup.sh per 0.9.4)
  • Bluetooth adapter pinning via udev (fixes non-deterministic hci enumeration on reboot)
  • NetworkManager unmanaged config for wlan1
  • wlan1-up.service for boot persistence
  • Bridge systemd service config
  • GPS PTY service config
  • Upstream compatibility notes (0.9.4 / 0.9.6 / 0.9.7)
  • Troubleshooting section

Known limitations

  • Privilege drop (feat: offline map download via Stadia Maps (replaces disabled CartoDB) #5 from review) - os.setresuid() after PTY/monitor setup is not implemented. On a single-user embedded device the risk/complexity tradeoff doesn't justify it, but acknowledged.
  • Deauth + immediate capture timing - on the uConsole the game's CPU load makes rapid deauth→capture sequences difficult. Works correctly, just requires patience.
  • Whitelist scan empty for large loot databases - _wl_build_scan_list reads from self.wifi_networks which is empty when all BSSIDs are pre-loaded into _known_wifi from loot history. Filed separately as a bug.
  • pkt_sniff.py - dispatch stub exists in bridge but script not yet written. Separate PR.
  • wlan2 required for HS/deauth - hs_capture.py and deauth both require a monitor-capable
    adapter on wlan2. The bridge handles WiFi scanning and BLE without it. If wlan2 is absent,
    hs/deauth commands return a friendly error and do nothing.

Testing

  • WiFi scanning: stable across 10+ rapid scans, zero timeouts (rate limiter confirmed in journal)
  • BLE scanning: 60-75 devices per scan, consistent 8.2s duration, zero adapter drops
  • Handshake capture: per-BSSID .txt / .22000 / .pcap files confirmed in session directory
  • Deauth: aireplay-ng -0 5 confirmed working, monitor mode handoff to hs_capture clean
  • Boot persistence: wlan1 up, NM unmanaged, bridge running across multiple reboots
  • BT stability: hci0/hci1 pinned via udev, no swaps across 5+ reboots

…d, dispatch stubs

- Add SCAN_COOLDOWN (5s) rate limiter: returns cached results if scan requested
  too quickly, prevents mt7921u timeout under rapid game scan requests
- On iw scan timeout, serve last known cache instead of empty results
- Add version command handler — game expects response, was silently ignored
- Dispatch handshake/sniffer to external scripts (hs_capture.py, pkt_sniff.py)
  rather than running inline — bridge stays thin and stable
- Remove inline airodump-ng/tcpdump code from bridge entirely
- Remove stale wlan1 restore from _stop_hs_capture (hcxdumptool leftover)
- Default bt-iface changed to hci1 (CM4 internal UART BT, separate bus from WiFi)
- Tested on uConsole CM4 + AC1200 (wlan1) + hci1, zero timeouts under stress
hs_capture.py (new):
- Standalone handshake capture dispatched by bridge on start_handshake
- airodump-ng + hcxpcapngtool polling, handles WPA* and 22000* formats
- Per-BSSID loot files: .txt / .22000 / .pcap (tshark-filtered)
- _find_active_session() writes to current game session directory
- SIGTERM/SIGINT clean teardown, restores wlan2 managed mode

wdg_wifi_bridge.py:
- SIGTERM handler for clean systemctl stop
- Atomic PTY symlink via mkdtemp + rename
- --iface validation prevents monitor mode on default route interface
- Popen-based HS/sniffer dispatch with proper start/stop toggle
- HS stderr logged to journalctl via _hs_stderr_loop
- Scan rate limiter + cache fallback
- version command handler
- pip advice points to venv pip

docs/uconsole-setup.md:
- Full uConsole CM4 + AIO v1 setup guide
- BT udev pinning, NM unmanaged, wlan1-up service, GPS PTY
- start_deauth <bssid> <channel> command dispatches aireplay-ng -0 5
- Checks monitor mode before setting — avoids fighting with hs_capture
- Skips channel set if hs_capture is active
- Restores managed mode only if hs_capture is not running
- hs_capture skips monitor mode set if wlan2 already in monitor (left by deauth)
- HS stderr now logged to journalctl via _hs_stderr_loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant