Skip to content

Add a WPA2/CCMP station-mode client (APFPV)#335

Open
GingerFluffyCat wants to merge 7 commits into
OpenIPC:masterfrom
GingerFluffyCat:apfpv-station-mode
Open

Add a WPA2/CCMP station-mode client (APFPV)#335
GingerFluffyCat wants to merge 7 commits into
OpenIPC:masterfrom
GingerFluffyCat:apfpv-station-mode

Conversation

@GingerFluffyCat

Copy link
Copy Markdown
Contributor

Summary

devourer currently has no way to be a WPA2 client — it injects/receives in
monitor mode, and the AP-mode test harnesses under tests/ play the AP role
against a real station. This adds the missing piece: a full station-mode
client that scans, authenticates, associates, runs the WPA2 4-way handshake,
gets an IP over DHCP, and streams data — usable as a real Wi-Fi uplink for an
RTL8812AU-family dongle, not just a monitor/injection tool.

New library surface (mirrors the existing devourer/chip-family target
pattern): two new CMake targets, apfpv (driver-independent: WPA2
supplicant, 802.11 frame builders, CCMP crypto, beacon/scan parsing) and
apfpv_station (the driver-dependent orchestrator + RX pipeline).

What's included

  • src/apfpv/Wpa2Supplicant (4-way handshake, client side),
    Dot11Frames (auth/assoc/probe request builders), Wpa2Crypto +
    crypto/{AesCcm,AesCmac,Sha1Hmac} (own CCMP/PBKDF2/PRF implementation, no
    external crypto dependency), ScanProbe (beacon parsing for AP
    discovery), StationMode (the arming sequence: MACID → MSR=STATION →
    BSSID → RCR filter, ported from the kernel's hw_var_set_opmode STATION
    path), StationTxDesc (the unicast TX descriptor — BMC=0 enables the
    ACK handshake, the fix that made auth/assoc frames actually get ACKed).
  • ApfpvStation — the top-level orchestrator: connect() runs the
    gated scan→arm→auth→assoc→4-way→DHCP→stream chain; a driver-level
    supervisor thread auto-reconnects on link loss (deauth or an RX/beacon
    timeout), re-arming on the known channel rather than a full rescan.
    Also exposes a general-IP bridge (arbitrary IPv4 over the CCMP link, not
    just an RTP/video assumption) and a minimal in-order TCP stack over it.
  • RxDeframe — the station RX pipeline: CCMP decrypt, A-MPDU RX
    reorder (kernel recv_indicatepkt_reorder equivalent — required once the
    AP aggregates; without it, out-of-order subframes are dropped), BlockAck
    request handling, and RTP-sequence reordering for the video use case this
    was built for.
  • ApfpvDhcp / LqFeedback — a DHCP client and a link-quality
    telemetry feed.
  • Driver hooks needed for the above (RtlAdapter): fillH2CCmd/
    sendH2CPacket (firmware mailbox), setSecCamKey/enableHwSec (security
    CAM programming for the PTK/GTK), sendStationFrameSync (concurrent
    IN/OUT unicast TX with an ACK-drain check, validated against the
    worst-case USB/IP transport), reset_device.
  • Two hardware-validated fixes (RadioManagementModule/
    PhydmWatchdog): SetStationRxFilter — the kernel-matched RCR/RXFLTMAP
    configuration station mode needs (the prior permissive filter dropped
    management frames the auth/assoc handshake needs); and a close-range
    DIG-saturation fix — once linked, track the receiver's gain floor/ceiling
    off the live beacon RSSI instead of the always-monitor-mode bounds, or a
    strong/close signal storms the false-alarm counter and the front-end goes
    deaf (no RX at all) even though the link should be trivially strong.

Caveats / what's not claimed

  • Single client only (no multi-station bookkeeping beyond what's needed for
    the one active connection).
  • ApfpvStation.cpp also contains AP-mode (SoftAP) scaffolding —
    beaconing, RSSI tracking, and a WPA2 4-way authenticator (the AP side) —
    wired onto this repo's own StartBeacon/StopBeacon. It's explicitly
    marked work-in-progress in its own comments: association completion and
    the data plane (ARP/ICMP/DHCP responding to an associated client) aren't
    there yet, unlike this repo's own tests/ap_wpa2.cpp/ap_responder.cpp
    harnesses, which do have a working data plane. Happy to split it out of
    this PR if you'd rather review the station-mode work on its own — say the
    word and I'll drop those commits/functions.
  • Bench-validated on RTL8812AU (Jaguar1) against a real OpenIPC AP; not
    tested against Jaguar2/3/Kestrel chips.

Testing

Built cleanly as a standalone CMake target (apfpv_station) against this
branch; also integrated and building end-to-end in a downstream Android app
(PixelPilot) alongside the existing WFB-ng monitor-mode path.

Confirmed by research: Wpa2Supplicant/Dot11Frames/Wpa2Crypto/crypto/* have
zero coupling to the driver layer (pure byte-buffer logic behind a SendFn
callback) -- ported byte-for-byte with zero code changes, only new build
wiring (apfpv CMake target) + the already-proven android/log.h host-build
compat shim reused from WiFiDriver.

ApfpvDhcp, LqFeedback, Wpa2Authenticator: same story, built clean.

ApfpvStation.cpp/h and RxDeframe.cpp/h are copied onto the tree but NOT yet
wired into the build -- these need real porting work against upstream's
restructured driver API (IRtlDevice/RtlJaguarDevice, new H2C/station
convenience wrappers that don't exist upstream yet) before they'll compile.
Next commit.
…nto RtlAdapter

Zero logic changes -- these only ever used rtw_read<T>/rtw_write<T> (same
template signatures upstream) and hardcoded/named H2C mailbox register
offsets (REG_HMETFR/REG_HMEBOX_0..3/REG_HMEBOX_EXT_0..3, confirmed present
in hal/hal_com_reg.h). reset_device() needed one small new addition:
upstream's transport layer had no USB-reset concept at all (no station
client flow existed there to need reconnect recovery) -- added a
virtual int reset() to IRtlTransport (default no-op, e.g. for a future PCIe
transport) with a real libusb_reset_device() override in UsbTransport.

Verified: devourer library builds clean (Jaguar1-only config).
Zero logic changes -- confirmed hw_var_rcr_config() (the actual register
write) and every RCR_*/REG_RXFLTMAP* constant this function needs already
exist upstream, used the same way in its own _InitWMACSetting_8812A(). This
is the kernel-exact station RCR fix: RCR_AAP (promiscuous) bypasses the HW
auto-Block-Ack engine for A-MPDU aggregates entirely (ADDBA/DELBA/single-
frame-fallback ~25Mbps ceiling); APM+CBSSID_DATA routes through the real
station RX path so the chip auto-BAs aggregates instead.

Verified: devourer library builds clean.
…not yet wired)

Mechanical port of the accessors + backing state (_lastRfCh, _scanMode) from
the pre-rebuild WiFiDriver. Honest caveat, flagged in the code: the actual
BEHAVIOR (reading RF 0x18 back into _lastRfCh after a tune, skipping IQK
when _scanMode is set) lives inside set_channel_bwmode and is NOT yet
wired up -- these always report "not wedged"/have no IQK-suppression
effect until that integration lands. Tracked as follow-up work.

Verified: builds clean.
- RF-verify-read (BB 0x834 + RF 0x18 readback -> _lastRfCh) added to
  PHY_HandleSwChnlAndSetBW8812, Android-gated, same placement/behavior as
  the pre-rebuild WiFiDriver -- confirmed phy_query_bb_reg/phy_query_rf_reg
  exist upstream with matching signatures (this code path is skipped on the
  host/non-Android build, so verified by symbol lookup rather than compile
  here; will compile-check for real on the next Android/Gradle build).
- _scanMode now actually suppresses the IQK trigger (fast unsettled retunes
  across a scan sweep wedge the RF synth if IQK runs mid-sweep).

rf_wedged()/setScanMode() are no longer inert -- both declared AND wired.

Verified: devourer library builds clean.
Kept the proven default behavior exactly (concurrent IN/OUT, ACK-driven
TXPKT_EMPTY drain check, opt-in clear_halt for the WSL/vhci wedge case,
_txFastPath skipping the drain diagnostic once streaming). Dropped the
legacy DEVOURER_TX_USE_PAUSE fallback: it paused/resumed a per-adapter
async-RX worker that doesn't exist in this form anymore (RX-loop ownership
moved to whoever calls bulk_read_async_loop, i.e. RtlJaguarDevice::
StartRxLoop/StopRxLoop) -- that branch was already documented as legacy/
non-default in the pre-rebuild code, so it's dropped rather than force-fit
onto a different architecture, not silently lost functionality.

Adapted bulk_out_eps[0]/libusb_clear_halt(_dev_handle,...) to this class's
own first_bulk_out_ep()/bulk_clear_halt() equivalents.

Verified: devourer library builds clean.
…tream base

- PhydmWatchdog: restore the close-range DIG-saturation fix (RSSI-tracked
  IGI floor/ceiling once linked) that upstream's monitor-only DigTick had
  no equivalent for.
- StationTxDesc: port the unicast TX descriptor (BMC=0 ACK-handshake fix).
- ApfpvStation: replace every StartMonitorAsyncRx/StopAsyncRx call site
  with upstream's blocking StartRxLoop/StopRxLoop run on a dedicated
  thread (RtlJaguarDevice::Init/StartRxLoop no longer return until
  stopped, unlike the old async URB-pool contract).
- AP mode: wire onto upstream's IRtlDevice::StartBeacon/StopBeacon (real
  HW TBTT beacon + ACK-engine arming) instead of the old manual register
  pokes + software beacon-poll loop; fix apSend() calling send_packet
  synchronously from the RX callback thread (returns BUSY per
  docs/ap-mode.md) by queuing to a separate TX-pump thread.
- RadioManagementModule: port prime_offset_40mhz (40 MHz primary-channel
  offset) and add no-op pauseAsyncRx/resumeAsyncRx to RtlAdapter (RX
  ownership moved out of the adapter, so there's no in-adapter pump left
  to pause).

apfpv_station now builds clean against upstream's rebuilt base.
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