| // 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/wlanif/c/banjo.h> |
| #include <fuchsia/wlan/ieee80211/cpp/fidl.h> |
| #include <fuchsia/wlan/stats/cpp/fidl.h> |
| #include <zircon/errors.h> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <wifi/wifi-config.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/common.h" |
| #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.h" |
| #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/sim.h" |
| #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/test/sim_test.h" |
| #include "src/connectivity/wlan/drivers/wlanif/convert.h" |
| #include "src/connectivity/wlan/lib/common/cpp/include/wlan/common/macaddr.h" |
| |
| namespace wlan::brcmfmac { |
| |
| namespace wlan_ieee80211 = ::fuchsia::wlan::ieee80211; |
| |
| using ::testing::IsEmpty; |
| using ::testing::NotNull; |
| using ::testing::SizeIs; |
| |
| // Some default AP and association request values |
| constexpr wlan_channel_t kDefaultChannel = { |
| .primary = 9, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0}; |
| constexpr wlan_ssid_t kDefaultSsid = {.len = 15, .ssid = "Fuchsia Fake AP"}; |
| const uint8_t kIes[] = { |
| // SSID |
| 0x00, 0x0f, 'F', 'u', 'c', 'h', 's', 'i', 'a', ' ', 'F', 'a', 'k', 'e', ' ', 'A', 'P', |
| // Supported rates |
| 0x01, 0x08, 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, |
| // DS parameter set - channel 157 |
| 0x03, 0x01, 0x9d, |
| // DTIM |
| 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, |
| // Power constraint |
| 0x20, 0x01, 0x03, |
| // HT capabilities |
| 0x2d, 0x1a, 0xef, 0x09, 0x1b, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // HT operation |
| 0x3d, 0x16, 0x9d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // Overlapping BSS scan parameters |
| 0x4a, 0x0e, 0x14, 0x00, 0x0a, 0x00, 0x2c, 0x01, 0xc8, 0x00, 0x14, 0x00, 0x05, 0x00, 0x19, 0x00, |
| // Extended capabilities |
| 0x7f, 0x08, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x40, |
| // VHT capabilities |
| 0xbf, 0x0c, 0xb2, 0x01, 0x80, 0x33, 0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, |
| // VHT operation |
| 0xc0, 0x05, 0x01, 0x9b, 0x00, 0xfc, 0xff, |
| // VHT Tx power envelope |
| 0xc3, 0x04, 0x02, 0xc4, 0xc4, 0xc4, |
| // Vendor IE - WMM parameters |
| 0xdd, 0x18, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x80, 0x00, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, |
| 0x00, 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00, |
| // Vendor IE - Atheros advanced capability |
| 0xdd, 0x09, 0x00, 0x03, 0x7f, 0x01, 0x01, 0x00, 0x00, 0xff, 0x7f, |
| // RSN |
| 0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00, |
| 0x00, 0x0f, 0xac, 0x02, 0x00, 0x00, |
| // Vendor IE - WPS |
| 0xdd, 0x1d, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10, 0x44, 0x00, 0x01, 0x02, |
| 0x10, 0x3c, 0x00, 0x01, 0x03, 0x10, 0x49, 0x00, 0x06, 0x00, 0x37, 0x2a, 0x00, 0x01, 0x20}; |
| const common::MacAddr kDefaultBssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}); |
| const common::MacAddr kMadeupClient({0xde, 0xad, 0xbe, 0xef, 0x00, 0x01}); |
| const uint16_t kDefaultApDisassocReason = 1; |
| // INVALID_AUTHENTICATION, IEEE 802.11-2016 9.4.17 |
| const uint16_t kDefaultApDeauthReason = 2; |
| // LEAVING_NETWORK_DISASSOC, IEEE 802.11-2016 9.4.17 |
| const uint16_t kDefaultClientDeauthReason = 8; |
| // Sim firmware returns these values for SNR and RSSI. |
| const uint8_t kDefaultSimFwSnr = 40; |
| const int8_t kDefaultSimFwRssi = -20; |
| |
| class AssocTest : public SimTest { |
| public: |
| // How long an individual test will run for. We need an end time because tests run until no more |
| // events remain and so we need to stop aps from beaconing to drain the event queue. |
| static constexpr zx::duration kTestDuration = zx::sec(100); |
| |
| void Init(); |
| |
| // Run through the join => auth => assoc flow |
| void StartAssoc(); |
| void StartDisassoc(); |
| void DisassocFromAp(); |
| |
| // Re-association, skips join => auth steps. |
| void ReAssoc(); |
| |
| // Send bad association responses |
| void SendBadResp(); |
| |
| // Send repeated association responses |
| void SendMultipleResp(); |
| |
| // Send association response with WMM IE |
| void SendAssocRespWithWmm(); |
| |
| // Send one authentication response to help client passing through authentication process |
| void SendOpenAuthResp(); |
| |
| // Send Disassociate request to SIM FW |
| void DisassocClient(const common::MacAddr& mac_addr); |
| |
| // Pretend to transmit Disassoc from AP |
| void TxFakeDisassocReq(); |
| |
| // Deauth routines |
| void StartDeauth(); |
| void DeauthClient(); |
| void DeauthFromAp(); |
| |
| void AssocErrorInject(); |
| |
| void SendStatsQuery(); |
| void DetailedHistogramErrorInject(); |
| |
| protected: |
| struct AssocContext { |
| // Information about the BSS we are attempting to associate with. Used to generate the |
| // appropriate MLME calls (Join => Auth => Assoc). |
| simulation::WlanTxInfo tx_info = {.channel = kDefaultChannel}; |
| common::MacAddr bssid = kDefaultBssid; |
| wlan_ssid_t ssid = kDefaultSsid; |
| std::vector<uint8_t> ies = std::vector<uint8_t>(kIes, kIes + sizeof(kIes)); |
| |
| // There should be one result for each association response received |
| std::list<wlan_assoc_result_t> expected_results; |
| std::vector<uint8_t> expected_wmm_param; |
| |
| // An optional function to call when we see the association request go out. |
| std::function<void()> on_assoc_req_callback; |
| // An optional function to call when we see the authentication request go out. |
| std::function<void()> on_auth_req_callback; |
| |
| // Track number of association responses |
| size_t assoc_resp_count = 0; |
| // Track number of disassociation confs (initiated from self) |
| size_t disassoc_conf_count = 0; |
| // Track number of disassoc indications (initiated from AP) |
| size_t disassoc_ind_count = 0; |
| // Track number of deauth indications (initiated from AP) |
| size_t deauth_ind_count = 0; |
| // Number of deauth confirmations (when initiated by self) |
| size_t deauth_conf_count = 0; |
| // Whether deauth/disassoc is locally initiated |
| size_t ind_locally_initiated_count = 0; |
| // Number of signal report indications (once client is assoc'd) |
| size_t signal_ind_count = 0; |
| // SNR seen in the signal report indication. |
| int16_t signal_ind_snr = 0; |
| // RSSI seen in the signal report indication. |
| int16_t signal_ind_rssi = 0; |
| // IfaceStats from StatsQuery response, if non-empty. |
| fuchsia::wlan::stats::IfaceStats iface_stats; |
| }; |
| |
| struct AssocRespInfo { |
| wlan_channel_t channel; |
| common::MacAddr src; |
| common::MacAddr dst; |
| wlan_ieee80211::StatusCode status; |
| }; |
| |
| // This is the interface we will use for our single client interface |
| SimInterface client_ifc_; |
| |
| AssocContext context_; |
| |
| // Keep track of the APs that are in operation so we can easily disable beaconing on all of them |
| // at the end of each test. |
| std::list<simulation::FakeAp*> aps_; |
| |
| // All of the association responses seen in the environment |
| std::list<AssocRespInfo> assoc_responses_; |
| std::list<wlan_ieee80211::StatusCode> auth_resp_status_list_; |
| |
| // Trigger to start disassociation. If set to true, disassociation is started |
| // soon after association completes. |
| bool start_disassoc_ = false; |
| // If disassoc_from_ap_ is set to true, the disassociation process is started |
| // from the FakeAP else from the station itself. |
| bool disassoc_from_ap_ = false; |
| |
| // This flag is checked only if disassoc_from_ap_ is false. If set to true, the |
| // local client mac is used in disassoc_req else a fake mac address is used |
| bool disassoc_self_ = true; |
| |
| // Indicates if deauth needs to be issued. |
| bool start_deauth_ = false; |
| // Indicates if deauth is from the AP or from self |
| bool deauth_from_ap_ = false; |
| |
| private: |
| // StationIfc overrides |
| void Rx(std::shared_ptr<const simulation::SimFrame> frame, |
| std::shared_ptr<const simulation::WlanRxInfo> info) override; |
| |
| // SME callbacks |
| static wlanif_impl_ifc_protocol_ops_t sme_ops_; |
| wlanif_impl_ifc_protocol sme_protocol_ = {.ops = &sme_ops_, .ctx = this}; |
| |
| // Event handlers |
| void OnJoinConf(const wlanif_join_confirm_t* resp); |
| void OnAuthConf(const wlanif_auth_confirm_t* resp); |
| void OnAssocConf(const wlanif_assoc_confirm_t* resp); |
| void OnDisassocInd(const wlanif_disassoc_indication_t* ind); |
| void OnDisassocConf(const wlanif_disassoc_confirm_t* resp); |
| void OnDeauthConf(const wlanif_deauth_confirm_t* resp); |
| void OnDeauthInd(const wlanif_deauth_indication_t* ind); |
| void OnSignalReport(const wlanif_signal_report_indication* ind); |
| void OnStatsQueryResp(const wlanif_stats_query_response_t* resp); |
| }; |
| |
| // Since we're acting as wlanif, we need handlers for any protocol calls we may receive |
| wlanif_impl_ifc_protocol_ops_t AssocTest::sme_ops_ = { |
| .on_scan_result = |
| [](void* cookie, const wlanif_scan_result_t* result) { |
| // Ignore |
| }, |
| .on_scan_end = |
| [](void* cookie, const wlanif_scan_end_t* end) { |
| // Ignore |
| }, |
| .join_conf = |
| [](void* cookie, const wlanif_join_confirm_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnJoinConf(resp); |
| }, |
| .auth_conf = |
| [](void* cookie, const wlanif_auth_confirm_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnAuthConf(resp); |
| }, |
| .deauth_conf = |
| [](void* cookie, const wlanif_deauth_confirm_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnDeauthConf(resp); |
| }, |
| .deauth_ind = |
| [](void* cookie, const wlanif_deauth_indication_t* ind) { |
| static_cast<AssocTest*>(cookie)->OnDeauthInd(ind); |
| }, |
| .assoc_conf = |
| [](void* cookie, const wlanif_assoc_confirm_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnAssocConf(resp); |
| }, |
| .disassoc_conf = |
| [](void* cookie, const wlanif_disassoc_confirm_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnDisassocConf(resp); |
| }, |
| .disassoc_ind = |
| [](void* cookie, const wlanif_disassoc_indication_t* ind) { |
| static_cast<AssocTest*>(cookie)->OnDisassocInd(ind); |
| }, |
| .signal_report = |
| [](void* cookie, const wlanif_signal_report_indication* ind) { |
| static_cast<AssocTest*>(cookie)->OnSignalReport(ind); |
| }, |
| .stats_query_resp = |
| [](void* cookie, const wlanif_stats_query_response_t* resp) { |
| static_cast<AssocTest*>(cookie)->OnStatsQueryResp(resp); |
| }, |
| }; |
| |
| void AssocTest::Rx(std::shared_ptr<const simulation::SimFrame> frame, |
| std::shared_ptr<const simulation::WlanRxInfo> info) { |
| ASSERT_EQ(frame->FrameType(), simulation::SimFrame::FRAME_TYPE_MGMT); |
| |
| auto mgmt_frame = std::static_pointer_cast<const simulation::SimManagementFrame>(frame); |
| // If a handler has been installed, call it |
| if (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_ASSOC_REQ) { |
| if (context_.on_assoc_req_callback) { |
| env_->ScheduleNotification(context_.on_assoc_req_callback, zx::msec(1)); |
| } |
| } |
| |
| if (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_ASSOC_RESP) { |
| auto assoc_resp = std::static_pointer_cast<const simulation::SimAssocRespFrame>(mgmt_frame); |
| AssocRespInfo resp_info = {.channel = info->channel, |
| .src = assoc_resp->src_addr_, |
| .dst = assoc_resp->dst_addr_, |
| .status = assoc_resp->status_}; |
| assoc_responses_.push_back(resp_info); |
| } |
| |
| if (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_AUTH) { |
| auto auth_frame = std::static_pointer_cast<const simulation::SimAuthFrame>(mgmt_frame); |
| // When we receive a authentication request, try to call the callback. |
| if (auth_frame->seq_num_ == 1) { |
| if (context_.on_auth_req_callback) { |
| env_->ScheduleNotification(context_.on_auth_req_callback, zx::msec(1)); |
| } |
| return; |
| } |
| |
| if (auth_frame->seq_num_ == 2 || auth_frame->seq_num_ == 4) |
| auth_resp_status_list_.push_back(auth_frame->status_); |
| } |
| } |
| |
| // Create our device instance and hook up the callbacks |
| void AssocTest::Init() { |
| ASSERT_EQ(SimTest::Init(), ZX_OK); |
| ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_, &sme_protocol_), ZX_OK); |
| context_.assoc_resp_count = 0; |
| context_.disassoc_conf_count = 0; |
| context_.deauth_ind_count = 0; |
| context_.disassoc_ind_count = 0; |
| context_.ind_locally_initiated_count = 0; |
| context_.signal_ind_count = 0; |
| context_.signal_ind_rssi = 0; |
| context_.signal_ind_snr = 0; |
| context_.iface_stats = {}; |
| |
| // Reset all of these settings, which should be set in tests that need them. |
| start_disassoc_ = false; |
| disassoc_from_ap_ = false; |
| disassoc_self_ = true; |
| start_deauth_ = false; |
| deauth_from_ap_ = false; |
| } |
| |
| void AssocTest::DisassocFromAp() { |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| |
| // Disassoc the STA |
| for (auto ap : aps_) { |
| ap->DisassocSta(my_mac, kDefaultApDisassocReason); |
| } |
| } |
| |
| void AssocTest::OnJoinConf(const wlanif_join_confirm_t* resp) { |
| // Send auth request |
| wlanif_auth_req_t auth_req; |
| std::memcpy(auth_req.peer_sta_address, context_.bssid.byte, ETH_ALEN); |
| auth_req.auth_type = WLAN_AUTH_TYPE_OPEN_SYSTEM; |
| auth_req.auth_failure_timeout = 1000; // ~1s (although value is ignored for now) |
| client_ifc_.if_impl_ops_->auth_req(client_ifc_.if_impl_ctx_, &auth_req); |
| } |
| |
| void AssocTest::OnAuthConf(const wlanif_auth_confirm_t* resp) { |
| // Send assoc request |
| wlanif_assoc_req_t assoc_req = {.rsne_len = 0, .vendor_ie_len = 0}; |
| memcpy(assoc_req.peer_sta_address, context_.bssid.byte, ETH_ALEN); |
| client_ifc_.if_impl_ops_->assoc_req(client_ifc_.if_impl_ctx_, &assoc_req); |
| } |
| |
| void AssocTest::ReAssoc(void) { |
| // Start directly with assoc request (skipping join -> auth). |
| // This is what SME does on a disassoc ind. |
| wlanif_assoc_req_t assoc_req = {.rsne_len = 0, .vendor_ie_len = 0}; |
| memcpy(assoc_req.peer_sta_address, context_.bssid.byte, ETH_ALEN); |
| client_ifc_.if_impl_ops_->assoc_req(client_ifc_.if_impl_ctx_, &assoc_req); |
| } |
| |
| void AssocTest::OnAssocConf(const wlanif_assoc_confirm_t* resp) { |
| context_.assoc_resp_count++; |
| EXPECT_EQ(resp->result_code, context_.expected_results.front()); |
| EXPECT_EQ(resp->wmm_param_present, !context_.expected_wmm_param.empty()); |
| if (resp->wmm_param_present && !context_.expected_wmm_param.empty()) { |
| EXPECT_EQ(memcmp(resp->wmm_param, context_.expected_wmm_param.data(), WLAN_WMM_PARAM_LEN), 0); |
| } |
| context_.expected_results.pop_front(); |
| context_.expected_wmm_param.clear(); |
| |
| if (start_disassoc_) { |
| env_->ScheduleNotification(std::bind(&AssocTest::StartDisassoc, this), zx::msec(200)); |
| } else if (start_deauth_) { |
| env_->ScheduleNotification(std::bind(&AssocTest::StartDeauth, this), zx::msec(200)); |
| } |
| } |
| |
| void AssocTest::OnDisassocConf(const wlanif_disassoc_confirm_t* resp) { |
| if (resp->status == ZX_OK) { |
| context_.disassoc_conf_count++; |
| } |
| } |
| |
| void AssocTest::OnDeauthConf(const wlanif_deauth_confirm_t* resp) { context_.deauth_conf_count++; } |
| |
| void AssocTest::OnDeauthInd(const wlanif_deauth_indication_t* ind) { |
| context_.deauth_ind_count++; |
| if (ind->locally_initiated) { |
| context_.ind_locally_initiated_count++; |
| } |
| client_ifc_.stats_.deauth_indications.push_back(*ind); |
| } |
| |
| void AssocTest::OnDisassocInd(const wlanif_disassoc_indication_t* ind) { |
| context_.disassoc_ind_count++; |
| if (ind->locally_initiated) { |
| context_.ind_locally_initiated_count++; |
| } |
| client_ifc_.stats_.disassoc_indications.push_back(*ind); |
| } |
| |
| void AssocTest::OnSignalReport(const wlanif_signal_report_indication* ind) { |
| context_.signal_ind_count++; |
| context_.signal_ind_rssi = ind->rssi_dbm; |
| context_.signal_ind_snr = ind->snr_db; |
| } |
| |
| void AssocTest::OnStatsQueryResp(const wlanif_stats_query_response_t* resp) { |
| wlanif::ConvertIfaceStats(&context_.iface_stats, resp->stats); |
| } |
| |
| void AssocTest::StartAssoc() { |
| // Send join request |
| wlanif_join_req join_req = {}; |
| std::memcpy(join_req.selected_bss.bssid, context_.bssid.byte, ETH_ALEN); |
| join_req.selected_bss.ies_bytes_list = context_.ies.data(); |
| join_req.selected_bss.ies_bytes_count = context_.ies.size(); |
| join_req.selected_bss.chan = context_.tx_info.channel; |
| client_ifc_.if_impl_ops_->join_req(client_ifc_.if_impl_ctx_, &join_req); |
| } |
| |
| // Verify that we get a signal report when associated. |
| TEST_F(AssocTest, SignalReportTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ((int64_t)context_.signal_ind_count, |
| kTestDuration.get() / BRCMF_SIGNAL_REPORT_TIMER_DUR_MS); |
| // Verify the plumbing between the firmware and the signal report. |
| EXPECT_EQ(context_.signal_ind_snr, kDefaultSimFwSnr); |
| EXPECT_EQ(context_.signal_ind_rssi, kDefaultSimFwRssi); |
| } |
| |
| void AssocTest::SendStatsQuery() { |
| client_ifc_.if_impl_ops_->stats_query_req(client_ifc_.if_impl_ctx_); |
| } |
| |
| // Verify that StatsQueryReq works when associated. |
| TEST_F(AssocTest, StatsQueryReqTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::SendStatsQuery, this), zx::msec(30)); |
| |
| env_->Run(kTestDuration); |
| |
| // Verify that a stats query response was received. |
| ASSERT_THAT(context_.iface_stats.mlme_stats, NotNull()); |
| ASSERT_TRUE(context_.iface_stats.mlme_stats->is_client_mlme_stats()); |
| const auto& client_mlme_stats = context_.iface_stats.mlme_stats->client_mlme_stats(); |
| |
| // Sim firmware returns these fake values for packet counters. |
| const uint8_t rx_in = 10; |
| const uint8_t rx_out = 6; |
| const uint8_t rx_drop = 4; |
| const uint8_t tx_in = 5; |
| const uint8_t tx_out = 3; |
| const uint8_t tx_drop = 2; |
| EXPECT_EQ(client_mlme_stats.rx_frame.in.name, "Good+Bad+Ocast"); |
| EXPECT_EQ(client_mlme_stats.rx_frame.in.count, rx_in); |
| EXPECT_EQ(client_mlme_stats.rx_frame.out.name, "Good+Ocast"); |
| EXPECT_EQ(client_mlme_stats.rx_frame.out.count, rx_out); |
| EXPECT_EQ(client_mlme_stats.rx_frame.drop.name, "Bad"); |
| EXPECT_EQ(client_mlme_stats.rx_frame.drop.count, rx_drop); |
| EXPECT_EQ(client_mlme_stats.tx_frame.in.name, "Good+Bad"); |
| EXPECT_EQ(client_mlme_stats.tx_frame.in.count, tx_in); |
| EXPECT_EQ(client_mlme_stats.tx_frame.out.name, "Good"); |
| EXPECT_EQ(client_mlme_stats.tx_frame.out.count, tx_out); |
| EXPECT_EQ(client_mlme_stats.tx_frame.drop.name, "Bad"); |
| EXPECT_EQ(client_mlme_stats.tx_frame.drop.count, tx_drop); |
| |
| // Sim firmware returns these fake values for per-antenna histograms. |
| const auto& expected_hist_scope = fuchsia::wlan::stats::HistScope::PER_ANTENNA; |
| const auto& expected_antenna_freq = fuchsia::wlan::stats::AntennaFreq::ANTENNA_2_G; |
| const uint8_t expected_antenna_index = 0; |
| const uint8_t expected_snr_index = 60; |
| const uint8_t expected_snr_num_frames = 50; |
| // TODO(fxbug.dev/29698): Test all bucket values when sim firmware fully supports wstats_counters. |
| // Sim firmware populates only SNR buckets, probably due to the discrepancies between the iovar |
| // get handling between real and sim firmware (e.g. fxr/404141). When wstats_counters is fully |
| // supported in sim firmware we can test for the expected noise floor, RSSI, and rate buckets. |
| |
| ASSERT_THAT(client_mlme_stats.noise_floor_histograms, SizeIs(1)); |
| EXPECT_EQ(client_mlme_stats.noise_floor_histograms[0].hist_scope, expected_hist_scope); |
| ASSERT_THAT(client_mlme_stats.noise_floor_histograms[0].antenna_id, NotNull()); |
| EXPECT_EQ(client_mlme_stats.noise_floor_histograms[0].antenna_id->freq, expected_antenna_freq); |
| EXPECT_EQ(client_mlme_stats.noise_floor_histograms[0].antenna_id->index, expected_antenna_index); |
| |
| ASSERT_THAT(client_mlme_stats.rssi_histograms, SizeIs(1)); |
| EXPECT_EQ(client_mlme_stats.rssi_histograms[0].hist_scope, expected_hist_scope); |
| ASSERT_THAT(client_mlme_stats.rssi_histograms[0].antenna_id, NotNull()); |
| EXPECT_EQ(client_mlme_stats.rssi_histograms[0].antenna_id->freq, expected_antenna_freq); |
| EXPECT_EQ(client_mlme_stats.rssi_histograms[0].antenna_id->index, expected_antenna_index); |
| |
| ASSERT_THAT(client_mlme_stats.rx_rate_index_histograms, SizeIs(1)); |
| EXPECT_EQ(client_mlme_stats.rx_rate_index_histograms[0].hist_scope, expected_hist_scope); |
| ASSERT_THAT(client_mlme_stats.rx_rate_index_histograms[0].antenna_id, NotNull()); |
| EXPECT_EQ(client_mlme_stats.rx_rate_index_histograms[0].antenna_id->freq, expected_antenna_freq); |
| EXPECT_EQ(client_mlme_stats.rx_rate_index_histograms[0].antenna_id->index, |
| expected_antenna_index); |
| |
| ASSERT_THAT(client_mlme_stats.snr_histograms, SizeIs(1)); |
| EXPECT_EQ(client_mlme_stats.snr_histograms[0].hist_scope, expected_hist_scope); |
| ASSERT_THAT(client_mlme_stats.snr_histograms[0].antenna_id, NotNull()); |
| EXPECT_EQ(client_mlme_stats.snr_histograms[0].antenna_id->freq, expected_antenna_freq); |
| EXPECT_EQ(client_mlme_stats.snr_histograms[0].antenna_id->index, expected_antenna_index); |
| ASSERT_THAT(client_mlme_stats.snr_histograms[0].snr_samples, SizeIs(1)); |
| EXPECT_EQ(client_mlme_stats.snr_histograms[0].snr_samples[0].bucket_index, expected_snr_index); |
| EXPECT_EQ(client_mlme_stats.snr_histograms[0].snr_samples[0].num_samples, |
| expected_snr_num_frames); |
| } |
| |
| // Verify that StatsQueryReq works when detailed histogram feature is disabled. |
| TEST_F(AssocTest, StatsQueryReqWithoutDetailedHistogramFeatureTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| DetailedHistogramErrorInject(); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::SendStatsQuery, this), zx::msec(30)); |
| |
| env_->Run(kTestDuration); |
| |
| // Verify that a stats query response was received. |
| ASSERT_THAT(context_.iface_stats.mlme_stats, NotNull()); |
| ASSERT_TRUE(context_.iface_stats.mlme_stats->is_client_mlme_stats()); |
| const auto& client_mlme_stats = context_.iface_stats.mlme_stats->client_mlme_stats(); |
| |
| // All detailed histogram fields should be empty. |
| ASSERT_THAT(client_mlme_stats.noise_floor_histograms, IsEmpty()); |
| ASSERT_THAT(client_mlme_stats.rssi_histograms, IsEmpty()); |
| ASSERT_THAT(client_mlme_stats.rx_rate_index_histograms, IsEmpty()); |
| ASSERT_THAT(client_mlme_stats.snr_histograms, IsEmpty()); |
| } |
| |
| void AssocTest::AssocErrorInject() { |
| brcmf_simdev* sim = device_->GetSim(); |
| sim->sim_fw->err_inj_.AddErrInjCmd(BRCMF_C_SET_SSID, ZX_OK, BCME_OK, client_ifc_.iface_id_); |
| } |
| |
| void AssocTest::StartDisassoc() { |
| // Send disassoc request |
| if (!disassoc_from_ap_) { |
| if (disassoc_self_) { |
| DisassocClient(context_.bssid); |
| } else { |
| DisassocClient(kMadeupClient); |
| } |
| } else { |
| DisassocFromAp(); |
| } |
| } |
| |
| void AssocTest::StartDeauth() { |
| // Send deauth request |
| if (!deauth_from_ap_) { |
| DeauthClient(); |
| } else { |
| // Send deauth frame |
| DeauthFromAp(); |
| } |
| } |
| |
| void AssocTest::DisassocClient(const common::MacAddr& mac_addr) { |
| wlanif_disassoc_req disassoc_req = {}; |
| |
| std::memcpy(disassoc_req.peer_sta_address, mac_addr.byte, ETH_ALEN); |
| client_ifc_.if_impl_ops_->disassoc_req(client_ifc_.if_impl_ctx_, &disassoc_req); |
| } |
| |
| void AssocTest::DeauthClient() { |
| wlanif_deauth_req_t deauth_req = {.reason_code = kDefaultClientDeauthReason}; |
| |
| std::memcpy(deauth_req.peer_sta_address, context_.bssid.byte, ETH_ALEN); |
| client_ifc_.if_impl_ops_->deauth_req(client_ifc_.if_impl_ctx_, &deauth_req); |
| } |
| |
| void AssocTest::DeauthFromAp() { |
| // Figure out our own MAC |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| |
| // Send a Deauth to our STA |
| simulation::SimDeauthFrame deauth_frame(context_.bssid, my_mac, kDefaultApDeauthReason); |
| env_->Tx(deauth_frame, context_.tx_info, this); |
| } |
| |
| void AssocTest::TxFakeDisassocReq() { |
| // Figure out our own MAC |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| |
| // Send a Disassoc Req to our STA (which is not associated) |
| simulation::SimDisassocReqFrame not_associated_frame(context_.bssid, my_mac, |
| kDefaultApDisassocReason); |
| env_->Tx(not_associated_frame, context_.tx_info, this); |
| |
| // Send a Disassoc Req from the wrong bss |
| common::MacAddr wrong_src(context_.bssid); |
| wrong_src.byte[ETH_ALEN - 1]++; |
| simulation::SimDisassocReqFrame wrong_bss_frame(wrong_src, my_mac, kDefaultApDisassocReason); |
| env_->Tx(wrong_bss_frame, context_.tx_info, this); |
| |
| // Send a Disassoc Req to a different STA |
| common::MacAddr wrong_dst(my_mac); |
| wrong_dst.byte[ETH_ALEN - 1]++; |
| simulation::SimDisassocReqFrame wrong_sta_frame(context_.bssid, wrong_dst, |
| kDefaultApDisassocReason); |
| env_->Tx(wrong_sta_frame, context_.tx_info, this); |
| } |
| |
| void AssocTest::DetailedHistogramErrorInject() { |
| brcmf_simdev* sim = device_->GetSim(); |
| sim->sim_fw->err_inj_.AddErrInjIovar("wstats_counters", ZX_ERR_NOT_SUPPORTED, BCME_OK, |
| client_ifc_.iface_id_); |
| } |
| |
| // For this test, we want the pre-assoc scan test to fail because no APs are found. |
| TEST_F(AssocTest, NoAps) { |
| // Create our device instance |
| Init(); |
| |
| const common::MacAddr kBssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}); |
| context_.bssid = kBssid; |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| context_.ssid = {.len = 6, .ssid = "TestAP"}; |
| context_.tx_info.channel = {.primary = 9, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0}; |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that we can successfully associate to a fake AP |
| TEST_F(AssocTest, SimpleTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ((int64_t)context_.signal_ind_count, |
| kTestDuration.get() / BRCMF_SIGNAL_REPORT_TIMER_DUR_MS); |
| } |
| |
| // Verify that we can associate using only SSID, not BSSID |
| TEST_F(AssocTest, SsidTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that APs with incorrect SSIDs or BSSIDs are ignored |
| TEST_F(AssocTest, WrongIds) { |
| // Create our device instance |
| Init(); |
| |
| constexpr wlan_channel_t kWrongChannel = { |
| .primary = 8, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0}; |
| ASSERT_NE(kDefaultChannel.primary, kWrongChannel.primary); |
| constexpr wlan_ssid_t kWrongSsid = {.len = 14, .ssid = "Fuchsia Fake AP"}; |
| ASSERT_NE(kDefaultSsid.len, kWrongSsid.len); |
| const common::MacAddr kWrongBssid({0x12, 0x34, 0x56, 0x78, 0x9b, 0xbc}); |
| ASSERT_NE(kDefaultBssid, kWrongBssid); |
| |
| // Start up fake APs |
| simulation::FakeAp ap1(env_.get(), kDefaultBssid, kDefaultSsid, kWrongChannel); |
| aps_.push_back(&ap1); |
| simulation::FakeAp ap2(env_.get(), kWrongBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap2); |
| simulation::FakeAp ap3(env_.get(), kDefaultBssid, kWrongSsid, kDefaultChannel); |
| aps_.push_back(&ap3); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| // The APs aren't giving us a response, but the driver is telling us that the operation failed |
| // because it couldn't find a matching AP. |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Attempt to associate while already associated |
| TEST_F(AssocTest, RepeatedAssocTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| // The associations at 11ms and 12ms should be immediately refused (because there is already an |
| // association in progress), and eventually the association that was in progress should succeed. |
| context_.expected_results.push_back(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| context_.expected_results.push_back(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| context_.expected_results.push_back(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(11)); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(12)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 3U); |
| } |
| |
| // Verify that if an AP does not respond to an association response we return a failure |
| TEST_F(AssocTest, ApIgnoredRequest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake APs |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| // Make sure no responses were sent back from the fake AP |
| EXPECT_EQ(assoc_responses_.size(), 0U); |
| |
| // But we still got our response from the driver |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that if an AP refuses an association request we return a failure |
| TEST_F(AssocTest, ApRejectedRequest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake APs |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_REFUSED); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| brcmf_simdev* sim = device_->GetSim(); |
| struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_); |
| uint32_t max_assoc_retries; |
| zx_status_t status = brcmf_fil_iovar_int_get(ifp, "assoc_retry_max", &max_assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| ASSERT_EQ(max_assoc_retries, kMaxAssocRetries); |
| // We should have gotten a refusal from the fake AP |
| EXPECT_EQ(auth_resp_status_list_.size(), max_assoc_retries + 1); |
| EXPECT_EQ(auth_resp_status_list_.front(), wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED); |
| EXPECT_EQ(assoc_responses_.size(), 0U); |
| |
| // Make sure we got our response from the driver |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // SIM FW ignore client assoc request. Note that currently there is no timeout |
| // mechanism in the driver to handle this situation. It is currently being |
| // worked on. |
| TEST_F(AssocTest, SimFwIgnoreAssocReq) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_back(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| AssocErrorInject(); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(50)); |
| env_->Run(kTestDuration); |
| |
| // We should not have received a assoc response from SIM FW |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| void AssocTest::SendBadResp() { |
| // Figure out our own MAC |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| |
| // Send a response from the wrong bss |
| common::MacAddr wrong_src(context_.bssid); |
| wrong_src.byte[ETH_ALEN - 1]++; |
| simulation::SimAssocRespFrame wrong_bss_frame(wrong_src, my_mac, |
| wlan_ieee80211::StatusCode::SUCCESS); |
| env_->Tx(wrong_bss_frame, context_.tx_info, this); |
| |
| // Send a response to a different STA |
| common::MacAddr wrong_dst(my_mac); |
| wrong_dst.byte[ETH_ALEN - 1]++; |
| simulation::SimAssocRespFrame wrong_dst_frame(context_.bssid, wrong_dst, |
| wlan_ieee80211::StatusCode::SUCCESS); |
| env_->Tx(wrong_dst_frame, context_.tx_info, this); |
| } |
| |
| // Verify that any non-applicable association responses (i.e., sent to or from the wrong MAC) |
| // are ignored |
| TEST_F(AssocTest, IgnoreRespMismatch) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| |
| // We want the association request to be ignored so we can inject responses and verify that |
| // they are being ignored. |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED); |
| |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| context_.on_assoc_req_callback = std::bind(&AssocTest::SendBadResp, this); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| // Make sure that the firmware/driver ignored bad responses and sent back its own (failure) |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| void AssocTest::SendMultipleResp() { |
| constexpr unsigned kRespCount = 100; |
| |
| // Figure out our own MAC |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| simulation::SimAssocRespFrame multiple_resp_frame(context_.bssid, my_mac, |
| wlan_ieee80211::StatusCode::SUCCESS); |
| for (unsigned i = 0; i < kRespCount; i++) { |
| env_->Tx(multiple_resp_frame, context_.tx_info, this); |
| } |
| } |
| |
| void AssocTest::SendAssocRespWithWmm() { |
| uint8_t mac_buf[ETH_ALEN]; |
| brcmf_simdev* sim = device_->GetSim(); |
| struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_); |
| zx_status_t status = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", mac_buf, ETH_ALEN, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| common::MacAddr my_mac(mac_buf); |
| simulation::SimAssocRespFrame assoc_resp_frame(context_.bssid, my_mac, |
| wlan_ieee80211::StatusCode::SUCCESS); |
| |
| uint8_t raw_ies[] = { |
| // WMM param |
| 0xdd, 0x18, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, // WMM header |
| 0x80, // Qos Info - U-ASPD enabled |
| 0x00, // reserved |
| 0x03, 0xa4, 0x00, 0x00, // Best effort AC params |
| 0x27, 0xa4, 0x00, 0x00, // Background AC params |
| 0x42, 0x43, 0x5e, 0x00, // Video AC params |
| 0x62, 0x32, 0x2f, 0x00, // Voice AC params |
| }; |
| assoc_resp_frame.AddRawIes(fbl::Span(raw_ies, sizeof(raw_ies))); |
| |
| env_->Tx(assoc_resp_frame, context_.tx_info, this); |
| } |
| |
| void AssocTest::SendOpenAuthResp() { |
| common::MacAddr my_mac; |
| client_ifc_.GetMacAddr(&my_mac); |
| simulation::SimAuthFrame auth_resp(context_.bssid, my_mac, 2, simulation::AUTH_TYPE_OPEN, |
| wlan_ieee80211::StatusCode::SUCCESS); |
| env_->Tx(auth_resp, context_.tx_info, this); |
| } |
| |
| // Verify that responses after association are ignored |
| TEST_F(AssocTest, IgnoreExtraResp) { |
| // Create our device instance |
| Init(); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.on_assoc_req_callback = std::bind(&AssocTest::SendMultipleResp, this); |
| context_.on_auth_req_callback = std::bind(&AssocTest::SendOpenAuthResp, this); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| // Make sure that the firmware/driver only responded to the first response |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Attempt to associate while a scan is in-progress |
| TEST_F(AssocTest, AssocWhileScanning) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.on_assoc_req_callback = std::bind(&AssocTest::SendMultipleResp, this); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| wlanif_scan_req_t scan_req = { |
| .txn_id = 42, |
| .bss_type = WLAN_BSS_TYPE_INFRASTRUCTURE, |
| .scan_type = WLAN_SCAN_TYPE_PASSIVE, |
| .num_channels = 11, |
| .channel_list = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, |
| .min_channel_time = 100, |
| .max_channel_time = 100, |
| .num_ssids = 0, |
| }; |
| client_ifc_.if_impl_ops_->start_scan(client_ifc_.if_impl_ctx_, &scan_req); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| TEST_F(AssocTest, AssocWithWmm) { |
| // Create our device instance |
| Init(); |
| |
| uint8_t expected_wmm_param[] = {0x80, 0x00, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, 0x00, |
| 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00}; |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.expected_wmm_param.insert(context_.expected_wmm_param.end(), expected_wmm_param, |
| expected_wmm_param + sizeof(expected_wmm_param)); |
| context_.on_assoc_req_callback = std::bind(&AssocTest::SendAssocRespWithWmm, this); |
| context_.on_auth_req_callback = std::bind(&AssocTest::SendOpenAuthResp, this); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that we can successfully associate to a fake AP & disassociate |
| TEST_F(AssocTest, DisassocFromSelfTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| start_disassoc_ = true; |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.disassoc_conf_count, 1U); |
| } |
| |
| // Verify that disassoc from fake AP fails when not associated. Also check |
| // disassoc meant for a different STA, different BSS or when not associated |
| // is not accepted by the current STA. |
| TEST_F(AssocTest, DisassocWithoutAssocTest) { |
| // Create our device instance |
| Init(); |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| // Attempt to disassociate. In this case client is not associated. AP |
| // will not transmit the disassoc request |
| env_->ScheduleNotification(std::bind(&AssocTest::StartDisassoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::TxFakeDisassocReq, this), zx::msec(50)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 0U); |
| EXPECT_EQ(context_.disassoc_conf_count, 0U); |
| } |
| |
| // Verify that disassociate for a different client is ignored |
| TEST_F(AssocTest, DisassocNotSelfTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| start_disassoc_ = true; |
| disassoc_self_ = false; |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.disassoc_conf_count, 0U); |
| } |
| |
| // After association, send disassoc from the AP |
| TEST_F(AssocTest, DisassocFromAPTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| disassoc_from_ap_ = true; |
| start_disassoc_ = true; |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.disassoc_ind_count, 1U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| |
| EXPECT_EQ(client_ifc_.stats_.disassoc_indications.size(), 1U); |
| const wlanif_disassoc_indication_t& disassoc_ind = |
| client_ifc_.stats_.disassoc_indications.front(); |
| EXPECT_EQ(disassoc_ind.locally_initiated, false); |
| } |
| |
| // After assoc & disassoc, send disassoc again to test event handling |
| TEST_F(AssocTest, LinkEventTest) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| disassoc_from_ap_ = true; |
| start_disassoc_ = true; |
| |
| env_->Run(kTestDuration); |
| |
| // Send Deauth frame after disassociation |
| DeauthFromAp(); |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.disassoc_ind_count, 1U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| } |
| |
| // After assoc, send a deauth from ap - client should disassociate |
| TEST_F(AssocTest, deauth_from_ap) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| deauth_from_ap_ = true; |
| start_deauth_ = true; |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.deauth_conf_count, 0U); |
| EXPECT_EQ(context_.deauth_ind_count, 1U); |
| EXPECT_EQ(context_.disassoc_conf_count, 0U); |
| EXPECT_EQ(context_.disassoc_ind_count, 0U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| } |
| |
| // After assoc, send a deauth from client - client should disassociate |
| TEST_F(AssocTest, deauth_from_self) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| deauth_from_ap_ = false; |
| start_deauth_ = true; |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.deauth_conf_count, 1U); |
| EXPECT_EQ(context_.deauth_ind_count, 0U); |
| EXPECT_EQ(context_.disassoc_conf_count, 0U); |
| EXPECT_EQ(context_.disassoc_ind_count, 0U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| } |
| |
| // Associate, send a deauth from client, associate again, then send deauth from AP. |
| TEST_F(AssocTest, deauth_from_self_then_from_ap) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.EnableBeacon(zx::msec(100)); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::DeauthClient, this), zx::sec(1)); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::sec(2)); |
| env_->ScheduleNotification(std::bind(&AssocTest::DeauthFromAp, this), zx::sec(3)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 2U); |
| EXPECT_EQ(context_.deauth_conf_count, 1U); |
| EXPECT_EQ(context_.deauth_ind_count, 1U); |
| EXPECT_EQ(context_.disassoc_conf_count, 0U); |
| EXPECT_EQ(context_.disassoc_ind_count, 0U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| } |
| |
| TEST_F(AssocTest, simple_reassoc) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::DisassocFromAp, this), zx::sec(2)); |
| env_->ScheduleNotification(std::bind(&AssocTest::ReAssoc, this), zx::sec(3)); |
| |
| env_->Run(kTestDuration); |
| |
| EXPECT_EQ(context_.assoc_resp_count, 2U); |
| EXPECT_EQ(context_.disassoc_ind_count, 1U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| } |
| |
| TEST_F(AssocTest, deauth_during_reassoc) { |
| // Create our device instance |
| Init(); |
| |
| // Start up our fake AP |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_SUCCESS); |
| |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| env_->ScheduleNotification(std::bind(&AssocTest::DisassocFromAp, this), zx::sec(2)); |
| env_->ScheduleNotification(std::bind(&AssocTest::ReAssoc, this), zx::sec(3)); |
| // Schedule a deauth immediately, before the above assoc can complete. |
| env_->ScheduleNotification(std::bind(&AssocTest::DeauthClient, this), zx::sec(3) + zx::usec(500)); |
| |
| env_->Run(kTestDuration); |
| |
| // If the deauth is successful, we will not get the second assoc response. |
| // If it fails for some reason (e.g. profile->bssid mismatch), then the |
| // assoc response count will be 2. |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| EXPECT_EQ(context_.disassoc_ind_count, 1U); |
| EXPECT_EQ(context_.ind_locally_initiated_count, 0U); |
| EXPECT_EQ(context_.deauth_conf_count, 1U); |
| } |
| |
| // Verify that association is retried as per the setting |
| TEST_F(AssocTest, AssocMaxRetries) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake APs |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_REFUSED); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| zx_status_t status; |
| uint32_t max_assoc_retries = 5; |
| brcmf_simdev* sim = device_->GetSim(); |
| struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_); |
| status = brcmf_fil_iovar_int_set(ifp, "assoc_retry_max", max_assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| uint32_t assoc_retries; |
| status = brcmf_fil_iovar_int_get(ifp, "assoc_retry_max", &assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| ASSERT_EQ(max_assoc_retries, assoc_retries); |
| // Should have received as many refusals as the configured # of retries. |
| EXPECT_EQ(auth_resp_status_list_.size(), max_assoc_retries + 1); |
| EXPECT_EQ(auth_resp_status_list_.front(), wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED); |
| EXPECT_EQ(assoc_responses_.size(), 0U); |
| |
| // Make sure we got our response from the driver |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that association is retried as per the setting |
| TEST_F(AssocTest, AssocMaxRetriesWhenTimedout) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake APs |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| uint32_t max_assoc_retries = 5; |
| brcmf_simdev* sim = device_->GetSim(); |
| struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_); |
| zx_status_t status = brcmf_fil_iovar_int_set(ifp, "assoc_retry_max", max_assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| // Should have not received any responses |
| EXPECT_EQ(assoc_responses_.size(), 0U); |
| // Make sure we got our response from the driver |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| |
| // Verify that association is attempted when retries is set to zero |
| TEST_F(AssocTest, AssocNoRetries) { |
| // Create our device instance |
| Init(); |
| |
| // Start up fake APs |
| simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel); |
| ap.SetAssocHandling(simulation::FakeAp::ASSOC_REFUSED); |
| aps_.push_back(&ap); |
| |
| context_.expected_results.push_front(WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED); |
| |
| zx_status_t status; |
| uint32_t max_assoc_retries = 0; |
| brcmf_simdev* sim = device_->GetSim(); |
| struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_); |
| status = brcmf_fil_iovar_int_set(ifp, "assoc_retry_max", max_assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| env_->ScheduleNotification(std::bind(&AssocTest::StartAssoc, this), zx::msec(10)); |
| |
| env_->Run(kTestDuration); |
| |
| uint32_t assoc_retries; |
| status = brcmf_fil_iovar_int_get(ifp, "assoc_retry_max", &assoc_retries, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| ASSERT_EQ(max_assoc_retries, assoc_retries); |
| // We should have gotten a refusal from the fake AP |
| EXPECT_EQ(auth_resp_status_list_.size(), 1U); |
| EXPECT_EQ(auth_resp_status_list_.front(), wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED); |
| |
| // Make sure we got our response from the driver |
| EXPECT_EQ(context_.assoc_resp_count, 1U); |
| } |
| } // namespace wlan::brcmfmac |