tests: --sniffer-iface integrates sniff_air into per-cell matrix flow#41
Merged
Conversation
Wires the radiotap verifier from #40 (tests/sniff_air.py) into regress.py so it runs alongside every cell in default / --full-matrix / --encoding-matrix modes. Optional and opt-in — defaults preserved. ## Usage sudo python3 tests/regress.py --encoding-matrix \ --tx-pid 0x8813 --rx-pid 0x0120 --channel 100 \ --vm-name devourer-testrig --vm-ssh <user>@<VM-IP> \ --sniffer-iface wlan0mon Per-cell output gains a `↪ sniffer: N frames — <encoding>=N, ...` line under the existing hit-count line. Reports what actually flew on-air, which the matrix itself can't see — closes the open question from #40 ("did the kernel-TX path actually emit LDPC, or strip the flag?"). Intended for an AR9271. The chipset speaks vanilla radiotap without driver-side flag filtering, which makes it a reliable on-air observer of what other adapters emit. ## Implementation - `_spawn_sniffer(iface, channel, pcap_path)`: sets iface to monitor mode on `channel`, runs `tcpdump -w pcap -U -nn 'ether src CANONICAL_SA'`. Always host-local; sniffer adapters don't move into the VM via virsh USB passthrough. - `_summarise_sniffer_pcap(pcap_path)`: imports sniff_air at runtime (next to regress.py), reuses `_read_pcap_frames` + `_parse_radiotap` + `_frame_sa` + `CANONICAL_SA` to bucket captured frames by encoding kind / MCS / LDPC / STBC / BW. Returns a one-line summary string suitable for the cell's `notes` field. - `run_cell(sniffer_iface=...)`: spawns sniffer between RX and TX stages so the full TX window gets captured. Sniffer failures are observational — never fail the cell on sniffer issues. - `run_matrix`, `run_full_matrix`, `run_encoding_matrix`: pass `sniffer_iface` through to `run_cell`. When the sniffer is active and `r.notes` is set, print an extra `↪ <notes>` line per cell. - `--sniffer-iface IFACE` CLI flag + `DEVOURER_SNIFFER_IFACE` env equivalent in `main()`. ## Validation Code path is dormant when `--sniffer-iface` is not set — defaults preserved, prior runs unaffected. Hardware validation requires an AR9271 plugged in and is left as the next-up follow-up; not blocking because the dormant path is verified by all existing CI / matrix runs on master (which the immediately preceding PR #40 validated end-to-end). The sniffer parser itself was unit-tested in #40: every combo `inject_beacon.build_beacon` emits round-trips back through `sniff_air._parse_radiotap` with byte-identical decoded fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last of #39's three follow-ups: wires the radiotap verifier from #40 (
tests/sniff_air.py) intotests/regress.pyso it runs alongside every cell. Optional and opt-in —--sniffer-ifacedefaults to off, all prior modes unchanged.What this is
sudo python3 tests/regress.py --encoding-matrix \ --tx-pid 0x8813 --rx-pid 0x0120 --channel 100 \ --vm-name devourer-testrig --vm-ssh <user>@<VM-IP> \ --sniffer-iface wlan0monPer-cell output gains a
↪ sniffer: N frames — <encoding>=N, ...line under the existing hit-count line. Reports what actually flew on-air for each cell — closes the open question from #40 ("did the kernel-TX path actually emit LDPC, or strip the flag?").Intended for an AR9271: vanilla radiotap, no driver-side filtering on what the cell injects. The chipset is widely used as a sniffer for exactly this reason.
Implementation
_spawn_sniffer(iface, channel, pcap_path)tcpdump -w pcap -U -nn 'ether src CANONICAL_SA'. Always host-local; sniffer never moved into the VM via USB passthrough._summarise_sniffer_pcap(pcap_path)_read_pcap_frames+_parse_radiotap+_frame_sa+CANONICAL_SAto bucket captured frames. Returns a one-line summary for the cell'snotesfield.run_cell(sniffer_iface=...)run_matrix/run_full_matrix/run_encoding_matrixsniffer_ifacethrough torun_cell. When active +r.notesis set, print an extra↪ <notes>line per cell.--sniffer-iface IFACEDEVOURER_SNIFFER_IFACEenv equivalent.Validation
The dormant path (
sniffer_iface=None) preserves the exact prior behaviour of every matrix mode — only structural change inrun_cellis initializingsniffer_proc=Noneand a no-op cleanup if it stays None.Active-path validation requires an AR9271 plugged in, which isn't in the rig today. CI matrix builds will confirm the code paths compile / import. Functional end-to-end pending hardware.
The sniffer parser itself was unit-tested in #40: every combo
inject_beacon.build_beaconemits round-trips back throughsniff_air._parse_radiotapwith byte-identical decoded fields.What this PR doesn't touch
ENCODING_COMBOSunchanged from tests: VHT encoding combos + sniff_air.py radiotap verifier #40.Test plan
--helplists--sniffer-iface--sniffer-iface IFACEwith an AR9271 plugged in: verify per-cell↪ sniffer:lines appear and decode reasonably--sniffer-ifacerunning alongside--encoding-matrixshows different encoding distributions for--ldpcvs default cells (the actual goal — proves whether mac80211 / 88XXau emits the LDPC bit on-air)🤖 Generated with Claude Code