From 457e1a64bdb084cdd06af8a8225d54c06a1b50f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Wed, 16 Sep 2026 00:21:19 +0200 Subject: [PATCH] ieee80211: fix: honor requested scan BSSID and SSID filters Filter MLME-SCAN.confirm while preserving discovery order and the cached AP list. Cover wildcard and exact filters, empty matches, and cache preservation through the production confirmation builder. Change: src.ieee80211.scanning | behavior.change.fix | test | wifi-audit --- .../ieee80211/mgmt/Ieee80211MgmtSta.cc | 17 +++- tests/unit/Ieee80211ScanConfirmFilters_1.test | 77 +++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 tests/unit/Ieee80211ScanConfirmFilters_1.test diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.cc b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.cc index 859a5c108ea..3a7e3d10fb4 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.cc +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.cc @@ -5,6 +5,8 @@ // +#include + #include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.h" #include "inet/linklayer/ieee80211/mgmt/Ieee80211HtMgmtElements.h" @@ -495,14 +497,20 @@ void Ieee80211MgmtSta::sendScanConfirm() { EV << "Scanning complete, found " << apList.size() << " APs, sending confirmation to agent\n"; - // copy apList contents into a ScanConfirm primitive and send it back - int n = apList.size(); + // copy matching apList contents into a ScanConfirm primitive and send it back + // IEEE Std 802.11-2024, 6.5.3.2.2: BSSID and SSID identify specific or wildcard scan targets. + auto matchesScanRequest = [this](const ApInfo& ap) { + return (scanning.bssid == MacAddress::BROADCAST_ADDRESS || ap.address == scanning.bssid) + && (scanning.ssid.empty() || ap.ssid == scanning.ssid); + }; + int n = std::count_if(apList.begin(), apList.end(), matchesScanRequest); Ieee80211Prim_ScanConfirm *confirm = new Ieee80211Prim_ScanConfirm(); confirm->setBssListArraySize(n); auto it = apList.begin(); - // TODO filter for req'd bssid and ssid - for (int i = 0; i < n; i++, it++) { + for (int i = 0; it != apList.end(); it++) { ApInfo *ap = &(*it); + if (!matchesScanRequest(*ap)) + continue; Ieee80211Prim_BssDescription& bss = confirm->getBssListForUpdate(i); bss.setChannelNumber(ap->channel); bss.setBSSID(ap->address); @@ -512,6 +520,7 @@ void Ieee80211MgmtSta::sendScanConfirm() bss.setExtendedSupportedRates(ap->extendedSupportedRates); bss.setBeaconInterval(ap->beaconInterval); bss.setRxPower(ap->rxPower); + i++; } sendConfirm(confirm, PRC_SUCCESS); } diff --git a/tests/unit/Ieee80211ScanConfirmFilters_1.test b/tests/unit/Ieee80211ScanConfirmFilters_1.test new file mode 100644 index 00000000000..41eb899e321 --- /dev/null +++ b/tests/unit/Ieee80211ScanConfirmFilters_1.test @@ -0,0 +1,77 @@ +%description: +Exercise the production scan-confirm construction with discovered BSSs that +share an SSID, unrelated beacons, exact filters and wildcards. Filtering must +not destroy the discovery cache used by subsequent management operations. + +%includes: +#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.h" + +using namespace inet; +using namespace inet::ieee80211; + +%global: +class ScanConfirmStation : public Ieee80211MgmtSta +{ + public: + std::vector results; + int confirmations = 0; + + void addBss(const char *address, const char *ssid) + { + apList.emplace_back(); + auto& ap = apList.back(); + ap.address = MacAddress(address); + ap.ssid = ssid; + ap.channel = 1; + ap.supportedRates.numRates = 0; + ap.extendedSupportedRates.numRates = 0; + } + + void confirm(const MacAddress& bssid, const char *ssid, bool active) + { + scanning = ScanningInfo(); + scanning.bssid = bssid; + scanning.ssid = ssid; + scanning.activeScan = active; + results.clear(); + sendScanConfirm(); + ASSERT(apList.size() == 3); + } + + protected: + virtual void sendConfirm(Ieee80211PrimConfirm *message, Ieee80211PrimResultCode code) override + { + ASSERT(code == PRC_SUCCESS); + auto confirm = check_and_cast(message); + confirmations++; + for (size_t i = 0; i < confirm->getBssListArraySize(); i++) + results.push_back(confirm->getBssList(i).getBSSID()); + delete confirm; + } +}; + +%activity: +ScanConfirmStation station; +const MacAddress a("02:00:00:00:00:01"), b("02:00:00:00:00:02"), c("02:00:00:00:00:03"); +station.addBss("02:00:00:00:00:01", "requested"); +station.addBss("02:00:00:00:00:02", "requested"); +station.addBss("02:00:00:00:00:03", "unrelated"); +for (bool active : {false, true}) { + station.confirm(MacAddress::BROADCAST_ADDRESS, "", active); + ASSERT(station.results == std::vector({a, b, c})); + station.confirm(MacAddress::BROADCAST_ADDRESS, "requested", active); + ASSERT(station.results == std::vector({a, b})); + station.confirm(b, "", active); + ASSERT(station.results == std::vector({b})); + station.confirm(b, "requested", active); + ASSERT(station.results == std::vector({b})); + station.confirm(b, "unrelated", active); + ASSERT(station.results.empty()); + station.confirm(MacAddress::BROADCAST_ADDRESS, "absent", active); + ASSERT(station.results.empty()); +} +ASSERT(station.confirmations == 12); +EV << "Scan confirms honor both filters and preserve discovery cache.\n"; + +%contains: stdout +Scan confirms honor both filters and preserve discovery cache.