blob: 47b87254f1e76983deb78687e11868281dba5ae7 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/hardware/wlan/info/c/banjo.h>
#include <fuchsia/hardware/wlanif/c/banjo.h>
#include <zircon/errors.h>
#include <functional>
#include <list>
#include <memory>
#include <optional>
#include <gtest/gtest.h>
#include "src/connectivity/wlan/drivers/testing/lib/sim-device/device.h"
#include "src/connectivity/wlan/drivers/testing/lib/sim-env/sim-env.h"
#include "src/connectivity/wlan/drivers/testing/lib/sim-fake-ap/sim-fake-ap.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/test/sim_test.h"
#include "src/connectivity/wlan/lib/common/cpp/include/wlan/common/macaddr.h"
namespace wlan::brcmfmac {
namespace {
constexpr zx::duration kScanStartTime = zx::sec(1);
constexpr zx::duration kSimulatedClockDuration = zx::sec(10);
} // namespace
struct ApInfo {
explicit ApInfo(simulation::Environment* env, const common::MacAddr& bssid,
const wlan_ssid_t& ssid, const wlan_channel_t& chan)
: ap_(env, bssid, ssid, chan) {}
simulation::FakeAp ap_;
bool probe_resp_seen_ = false;
};
struct ClientIfc : public SimInterface {
void OnScanResult(const wlanif_scan_result_t* result) override;
void OnScanEnd(const wlanif_scan_end_t* end) override;
// Txn ID for the current scan
uint64_t scan_txn_id_ = 0;
// Result of the previous scan
wlan_scan_result_t scan_result_code_ = ZX_OK;
std::list<wlanif_scan_result> scan_results_;
// BSS's IEs are raw pointers. Store the IEs here so we don't have dangling pointers
std::vector<std::vector<uint8_t>> seen_ies_;
};
class ActiveScanTest : public SimTest {
public:
static constexpr zx::duration kBeaconInterval = zx::msec(100);
static constexpr uint32_t kDwellTimeMs = 120;
ActiveScanTest() = default;
void Init();
void StartFakeAp(const common::MacAddr& bssid, const wlan_ssid_t& ssid,
const wlan_channel_t& chan, zx::duration beacon_interval = kBeaconInterval);
void StartScan(const wlanif_scan_req_t* req);
void VerifyScanResults();
void EndSimulation();
bool all_aps_seen_ = false;
void GetFirmwarePfnMac();
uint32_t GetNumProbeReqsSeen() { return num_probe_reqs_seen; };
protected:
// This is the interface we will use for our single client interface
ClientIfc client_ifc_;
// The default active scan request
wlanif_scan_req_t default_scan_req_;
private:
// StationIfc methods
void Rx(std::shared_ptr<const simulation::SimFrame> frame,
std::shared_ptr<const simulation::WlanRxInfo> info) override;
// All simulated APs
std::list<std::unique_ptr<ApInfo>> aps_;
// Mac address of sim_fw
common::MacAddr sim_fw_mac_;
common::MacAddr last_pfn_mac_ = common::kZeroMac;
std::optional<common::MacAddr> sim_fw_pfn_mac_;
uint32_t num_probe_reqs_seen = 0;
};
void ClientIfc::OnScanResult(const wlanif_scan_result_t* result) {
ASSERT_NE(result, nullptr);
EXPECT_EQ(scan_txn_id_, result->txn_id);
wlanif_scan_result_t copy = *result;
// Copy the IES data over since the original location may change data by the time we verify.
std::vector<uint8_t> ies(copy.bss.ies_bytes_list,
copy.bss.ies_bytes_list + copy.bss.ies_bytes_count);
seen_ies_.push_back(ies);
copy.bss.ies_bytes_list = seen_ies_.at(seen_ies_.size() - 1).data();
scan_results_.emplace_back(copy);
}
void ClientIfc::OnScanEnd(const wlanif_scan_end_t* end) { scan_result_code_ = end->code; }
void ActiveScanTest::Init() {
ASSERT_EQ(SimTest::Init(), ZX_OK);
ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_), ZX_OK);
// Get the interface MAC address
client_ifc_.GetMacAddr(&sim_fw_mac_);
default_scan_req_ = {
.bss_type = WLAN_BSS_TYPE_INFRASTRUCTURE,
.scan_type = WLAN_SCAN_TYPE_ACTIVE,
.num_channels = 5,
.channel_list = {1, 2, 3, 4, 5},
.min_channel_time = kDwellTimeMs,
.max_channel_time = kDwellTimeMs,
.num_ssids = 0,
};
}
void ActiveScanTest::StartFakeAp(const common::MacAddr& bssid, const wlan_ssid_t& ssid,
const wlan_channel_t& chan, zx::duration beacon_interval) {
auto ap_info = std::make_unique<ApInfo>(env_.get(), bssid, ssid, chan);
// Beacon is also enabled here to make sure this is not disturbing the correct result.
ap_info->ap_.EnableBeacon(beacon_interval);
aps_.push_back(std::move(ap_info));
}
// Tell the DUT to run a scan
void ActiveScanTest::StartScan(const wlanif_scan_req_t* req) {
client_ifc_.if_impl_ops_->start_scan(client_ifc_.if_impl_ctx_, req);
}
// Called when simulation time has run out. Takes down all fake APs and the simulated DUT.
void ActiveScanTest::EndSimulation() {
for (auto& ap_info : aps_) {
ap_info->ap_.DisableBeacon();
}
}
void ActiveScanTest::GetFirmwarePfnMac() {
brcmf_simdev* sim = device_->GetSim();
if (!sim_fw_pfn_mac_) {
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status =
brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", sim_fw_pfn_mac_->byte, ETH_ALEN, nullptr);
EXPECT_EQ(status, ZX_OK);
}
}
void ActiveScanTest::VerifyScanResults() {
for (auto result : client_ifc_.scan_results_) {
int matches_seen = 0;
for (auto& ap_info : aps_) {
common::MacAddr mac_addr = ap_info->ap_.GetBssid();
ASSERT_EQ(sizeof(result.bss.bssid), sizeof(mac_addr.byte));
if (!std::memcmp(result.bss.bssid, mac_addr.byte, sizeof(mac_addr.byte))) {
ap_info->probe_resp_seen_ = true;
matches_seen++;
// Verify SSID
wlan_ssid_t ssid_info = ap_info->ap_.GetSsid();
auto ssid = brcmf_find_ssid_in_ies(result.bss.ies_bytes_list, result.bss.ies_bytes_count);
EXPECT_EQ(ssid.size(), ssid_info.len);
ASSERT_LE(ssid_info.len, sizeof(ssid_info.ssid));
EXPECT_EQ(memcmp(ssid.data(), ssid_info.ssid, ssid_info.len), 0);
// Verify channel
wlan_channel_t channel = ap_info->ap_.GetChannel();
EXPECT_EQ(result.bss.chan.primary, channel.primary);
EXPECT_EQ(result.bss.chan.cbw, channel.cbw);
EXPECT_EQ(result.bss.chan.secondary80, channel.secondary80);
// Verify has RSSI value
ASSERT_LT(result.bss.rssi_dbm, 0);
}
}
// There should be exactly one AP per result.
EXPECT_EQ(matches_seen, 1);
}
for (auto& ap_info : aps_) {
if (ap_info->probe_resp_seen_ == false) {
// Failure
return;
}
}
// pfn mac should be different from the one from last scan.
EXPECT_NE(last_pfn_mac_, *sim_fw_pfn_mac_);
last_pfn_mac_ = *sim_fw_pfn_mac_;
// pfn mac will be set back to firmware mac after active scan.
GetFirmwarePfnMac();
EXPECT_EQ(sim_fw_mac_, *sim_fw_pfn_mac_);
sim_fw_pfn_mac_.reset();
// The probe response from all APs were seen
all_aps_seen_ = true;
}
void ActiveScanTest::Rx(std::shared_ptr<const simulation::SimFrame> frame,
std::shared_ptr<const simulation::WlanRxInfo> info) {
GetFirmwarePfnMac();
ASSERT_EQ(frame->FrameType(), simulation::SimFrame::FRAME_TYPE_MGMT);
auto mgmt_frame = std::static_pointer_cast<const simulation::SimManagementFrame>(frame);
if (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_PROBE_REQ) {
// When a probe request is sent out, the src mac address should not be real mac address.
auto probe_req = std::static_pointer_cast<const simulation::SimProbeReqFrame>(mgmt_frame);
EXPECT_NE(probe_req->src_addr_, sim_fw_mac_);
EXPECT_EQ(probe_req->src_addr_, *sim_fw_pfn_mac_);
num_probe_reqs_seen++;
}
if (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_PROBE_RESP) {
auto probe_resp = std::static_pointer_cast<const simulation::SimProbeRespFrame>(mgmt_frame);
EXPECT_NE(probe_resp->dst_addr_, sim_fw_mac_);
EXPECT_EQ(probe_resp->dst_addr_, *sim_fw_pfn_mac_);
}
}
// AP 1&2 on channel 2.
constexpr wlan_channel_t kDefaultChannel1 = {
.primary = 2, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0};
constexpr wlan_ssid_t kAp1Ssid = {.len = 16, .ssid = "Fuchsia Fake AP1"};
const common::MacAddr kAp1Bssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
constexpr wlan_ssid_t kAp2Ssid = {.len = 16, .ssid = "Fuchsia Fake AP2"};
const common::MacAddr kAp2Bssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbd});
// AP 3 on channel 4.
constexpr wlan_channel_t kDefaultChannel2 = {
.primary = 4, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0};
constexpr wlan_ssid_t kAp3Ssid = {.len = 16, .ssid = "Fuchsia Fake AP3"};
const common::MacAddr kAp3Bssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbe});
// This test case might fail in a very low possibility because it's random.
TEST_F(ActiveScanTest, RandomMacThreeAps) {
// Create simulated device
Init();
// Start the first AP
StartFakeAp(kAp1Bssid, kAp1Ssid, kDefaultChannel1);
StartFakeAp(kAp2Bssid, kAp2Ssid, kDefaultChannel1);
StartFakeAp(kAp3Bssid, kAp3Ssid, kDefaultChannel2);
default_scan_req_.txn_id = ++client_ifc_.scan_txn_id_;
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &default_scan_req_),
kScanStartTime);
// Schedule scan end in environment
env_->ScheduleNotification(std::bind(&ActiveScanTest::EndSimulation, this),
kSimulatedClockDuration);
env_->Run(kSimulatedClockDuration);
VerifyScanResults();
EXPECT_EQ(all_aps_seen_, true);
EXPECT_EQ(client_ifc_.scan_result_code_, ZX_OK);
}
TEST_F(ActiveScanTest, ScanTwice) {
Init();
default_scan_req_.txn_id = ++client_ifc_.scan_txn_id_;
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &default_scan_req_),
kScanStartTime);
env_->Run(kSimulatedClockDuration);
VerifyScanResults();
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &default_scan_req_),
kScanStartTime);
env_->ScheduleNotification(std::bind(&ActiveScanTest::EndSimulation, this),
kSimulatedClockDuration);
env_->Run(kSimulatedClockDuration);
VerifyScanResults();
EXPECT_EQ(client_ifc_.scan_result_code_, ZX_OK);
}
// Ensure that the FW sends out the max # probe requests set by the
// driver (as there are no APs in the environment).
TEST_F(ActiveScanTest, CheckNumProbeReqsSent) {
Init();
default_scan_req_.txn_id = ++client_ifc_.scan_txn_id_;
default_scan_req_.num_channels = 1;
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &default_scan_req_),
kScanStartTime);
env_->ScheduleNotification(std::bind(&ActiveScanTest::EndSimulation, this),
kSimulatedClockDuration);
env_->Run(kSimulatedClockDuration);
EXPECT_EQ(GetNumProbeReqsSeen(), (uint32_t)BRCMF_ACTIVE_SCAN_NUM_PROBES);
EXPECT_EQ(client_ifc_.scan_result_code_, ZX_OK);
}
// This test is to verify brcmfmac driver will return an error when an invalid ssid list
// is indicated in active scan test request.
TEST_F(ActiveScanTest, OverSizeSsid) {
constexpr zx::duration kFirstScanStartTime = zx::sec(1);
constexpr zx::duration kSecondScanStartTime = zx::sec(2);
Init();
StartFakeAp(kAp1Bssid, kAp1Ssid, kDefaultChannel1);
wlanif_ssid_t invalid_scan_ssid = {
.len = 33,
.data = "1234567890",
};
wlanif_ssid_t valid_scan_ssid = {
.len = 16,
.data = "Fuchsia Fake AP1",
};
// Case contains over-size ssid in ssid field of request.
wlanif_scan_req_t req_break_ssid = {
.txn_id = ++client_ifc_.scan_txn_id_,
.bss_type = WLAN_BSS_TYPE_INFRASTRUCTURE,
.ssid = invalid_scan_ssid,
.scan_type = WLAN_SCAN_TYPE_ACTIVE,
.num_channels = 5,
.channel_list = {1, 2, 3, 4, 5},
.min_channel_time = kDwellTimeMs,
.max_channel_time = kDwellTimeMs,
.num_ssids = 0,
};
// Case contains over-size ssid in ssid_list in request.
wlanif_scan_req_t req_break_ssid_list = {.txn_id = ++client_ifc_.scan_txn_id_,
.bss_type = WLAN_BSS_TYPE_INFRASTRUCTURE,
.scan_type = WLAN_SCAN_TYPE_ACTIVE,
.num_channels = 5,
.channel_list = {1, 2, 3, 4, 5},
.min_channel_time = kDwellTimeMs,
.max_channel_time = kDwellTimeMs,
.num_ssids = 2,
.ssid_list = {valid_scan_ssid, invalid_scan_ssid}};
// Two active scans are scheduled,
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &req_break_ssid),
kFirstScanStartTime);
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &req_break_ssid_list),
kSecondScanStartTime);
env_->ScheduleNotification(std::bind(&ActiveScanTest::EndSimulation, this),
kSimulatedClockDuration);
env_->Run(kSimulatedClockDuration);
VerifyScanResults();
EXPECT_EQ(client_ifc_.scan_result_code_, WLAN_SCAN_RESULT_INTERNAL_ERROR);
}
// This test case verifies that the driver returns SHOULD_WAIT as the scan result code when firmware
// is busy.
TEST_F(ActiveScanTest, ScanWhenFirmwareBusy) {
Init();
// Start the first AP
StartFakeAp(kAp1Bssid, kAp1Ssid, kDefaultChannel1);
// Set up our injector
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("escan", ZX_OK, BCME_BUSY);
env_->ScheduleNotification(std::bind(&ActiveScanTest::StartScan, this, &default_scan_req_),
kScanStartTime);
env_->ScheduleNotification(std::bind(&ActiveScanTest::EndSimulation, this),
kSimulatedClockDuration);
env_->Run(kSimulatedClockDuration);
// Verify that there is no scan result and the scan result code is WLAN_SCAN_RESULT_SHOULD_WAIT.
EXPECT_EQ(client_ifc_.scan_results_.size(), 0U);
EXPECT_EQ(client_ifc_.scan_result_code_, WLAN_SCAN_RESULT_SHOULD_WAIT);
}
} // namespace wlan::brcmfmac