blob: de6bcef84d514a8075eba29572669e264466d430 [file] [log] [blame]
// Copyright 2020 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 <gtest/gtest.h>
#include <wifi/wifi-config.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/third_party/broadcom/brcmfmac/cfg80211.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"
namespace wlan::brcmfmac {
constexpr uint16_t kDefaultCh = 149;
constexpr wlan_channel_t kDefaultChannel = {
.primary = kDefaultCh, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0};
const common::MacAddr kFakeMac({0xde, 0xad, 0xbe, 0xef, 0x00, 0x02});
constexpr wlan_ssid_t kDefaultSsid = {.len = 6, .ssid = "Sim_AP"};
class CreateSoftAPTest : public SimTest {
public:
CreateSoftAPTest() = default;
void Init();
void CreateInterface();
void DeleteInterface();
zx_status_t StartSoftAP();
zx_status_t StopSoftAP();
uint32_t DeviceCount();
void TxAuthReq(simulation::SimAuthType auth_type, common::MacAddr client_mac);
void TxAssocReq(common::MacAddr client_mac);
void TxDisassocReq(common::MacAddr client_mac);
void TxDeauthReq(common::MacAddr client_mac);
void VerifyAuth();
void VerifyAssoc();
void VerifyNotAssoc();
void VerifyStartAPConf(uint8_t status);
void VerifyStopAPConf(uint8_t status);
void VerifyNumOfClient(uint16_t expect_client_num);
void ClearAssocInd();
void InjectStartAPError();
void InjectStopAPError();
void SetExpectMacForInds(common::MacAddr set_mac);
// Status field in the last received authentication frame.
uint16_t auth_resp_status_;
bool auth_ind_recv_ = false;
bool assoc_ind_recv_ = false;
bool deauth_ind_recv_ = false;
bool disassoc_ind_recv_ = false;
bool start_conf_received_ = false;
bool stop_conf_received_ = false;
uint8_t start_conf_status_;
uint8_t stop_conf_status_;
// The expect mac address for indications
common::MacAddr ind_expect_mac_ = kFakeMac;
protected:
simulation::WlanTxInfo tx_info_ = {.channel = kDefaultChannel};
bool sec_enabled_ = false;
private:
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};
SimInterface softap_ifc_;
void OnAuthInd(const wlanif_auth_ind_t* ind);
void OnDeauthInd(const wlanif_deauth_indication_t* ind);
void OnAssocInd(const wlanif_assoc_ind_t* ind);
void OnDisassocInd(const wlanif_disassoc_indication_t* ind);
void OnStartConf(const wlanif_start_confirm_t* resp);
void OnStopConf(const wlanif_stop_confirm_t* resp);
void OnChannelSwitch(const wlanif_channel_switch_info_t* info);
uint16_t CreateRsneIe(uint8_t* buffer);
};
void CreateSoftAPTest::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 (mgmt_frame->MgmtFrameType() == simulation::SimManagementFrame::FRAME_TYPE_AUTH) {
auto auth_frame = std::static_pointer_cast<const simulation::SimAuthFrame>(mgmt_frame);
if (auth_frame->seq_num_ == 2)
auth_resp_status_ = auth_frame->status_;
}
}
wlanif_impl_ifc_protocol_ops_t CreateSoftAPTest::sme_ops_ = {
// SME operations
.auth_ind =
[](void* cookie, const wlanif_auth_ind_t* ind) {
static_cast<CreateSoftAPTest*>(cookie)->OnAuthInd(ind);
},
.deauth_ind =
[](void* cookie, const wlanif_deauth_indication_t* ind) {
static_cast<CreateSoftAPTest*>(cookie)->OnDeauthInd(ind);
},
.assoc_ind =
[](void* cookie, const wlanif_assoc_ind_t* ind) {
static_cast<CreateSoftAPTest*>(cookie)->OnAssocInd(ind);
},
.disassoc_ind =
[](void* cookie, const wlanif_disassoc_indication_t* ind) {
static_cast<CreateSoftAPTest*>(cookie)->OnDisassocInd(ind);
},
.start_conf =
[](void* cookie, const wlanif_start_confirm_t* resp) {
static_cast<CreateSoftAPTest*>(cookie)->OnStartConf(resp);
},
.stop_conf =
[](void* cookie, const wlanif_stop_confirm_t* resp) {
static_cast<CreateSoftAPTest*>(cookie)->OnStopConf(resp);
},
.on_channel_switch =
[](void* cookie, const wlanif_channel_switch_info_t* info) {
static_cast<CreateSoftAPTest*>(cookie)->OnChannelSwitch(info);
},
};
void CreateSoftAPTest::Init() { ASSERT_EQ(SimTest::Init(), ZX_OK); }
void CreateSoftAPTest::CreateInterface() {
zx_status_t status;
status = SimTest::StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_, &sme_protocol_);
ASSERT_EQ(status, ZX_OK);
}
void CreateSoftAPTest::DeleteInterface() { SimTest::DeleteInterface(softap_ifc_.iface_id_); }
uint32_t CreateSoftAPTest::DeviceCount() { return (dev_mgr_->DeviceCount()); }
uint16_t CreateSoftAPTest::CreateRsneIe(uint8_t* buffer) {
// construct a fake rsne ie in the input buffer
uint16_t offset = 0;
uint8_t* ie = buffer;
ie[offset++] = WLAN_IE_TYPE_RSNE;
ie[offset++] = 20; // The length of following content.
// These two bytes are 16-bit version number.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI,
TLV_OUI_LEN); // RSN OUI for multicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_TKIP; // Set multicast cipher suite.
// These two bytes indicate the length of unicast cipher list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI,
TLV_OUI_LEN); // RSN OUI for unicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_CCMP_128; // Set unicast cipher suite.
// These two bytes indicate the length of auth management suite list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI,
TLV_OUI_LEN); // RSN OUI for auth management suite.
offset += TLV_OUI_LEN;
ie[offset++] = RSN_AKM_PSK; // Set auth management suite.
// These two bytes indicate RSN capabilities, in this case is \x0c\x00.
ie[offset++] = 12; // Lower byte
ie[offset++] = 0; // Higher byte
// ASSERT_EQ(offset, (const uint32_t) (ie[TLV_LEN_OFF] + TLV_HDR_LEN));
return offset;
}
zx_status_t CreateSoftAPTest::StartSoftAP() {
wlanif_start_req_t start_req = {
.ssid = {.len = 6, .data = "Sim_AP"},
.bss_type = WLAN_BSS_TYPE_INFRASTRUCTURE,
.beacon_period = 100,
.dtim_period = 100,
.channel = kDefaultCh,
.rsne_len = 0,
};
// If sec mode is requested, create a dummy RSNE IE (our SoftAP only
// supports WPA2)
if (sec_enabled_ == true) {
start_req.rsne_len = CreateRsneIe(start_req.rsne);
}
softap_ifc_.if_impl_ops_->start_req(softap_ifc_.if_impl_ctx_, &start_req);
// Retrieve wsec from SIM FW to check if it is set appropriately
brcmf_simdev* sim = device_->GetSim();
int32_t wsec;
sim->sim_fw->IovarsGet(softap_ifc_.iface_id_, "wsec", &wsec, sizeof(wsec));
if (sec_enabled_ == true)
EXPECT_NE(wsec, 0);
else
EXPECT_EQ(wsec, 0);
return ZX_OK;
}
void CreateSoftAPTest::InjectStartAPError() {
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjCmd(BRCMF_C_SET_SSID, ZX_ERR_IO, softap_ifc_.iface_id_);
}
void CreateSoftAPTest::InjectStopAPError() {
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("bss", ZX_ERR_IO, softap_ifc_.iface_id_);
}
void CreateSoftAPTest::SetExpectMacForInds(common::MacAddr set_mac) { ind_expect_mac_ = set_mac; }
zx_status_t CreateSoftAPTest::StopSoftAP() {
wlanif_stop_req_t stop_req{
.ssid = {.len = 6, .data = "Sim_AP"},
};
softap_ifc_.if_impl_ops_->stop_req(softap_ifc_.if_impl_ctx_, &stop_req);
return ZX_OK;
}
void CreateSoftAPTest::OnAuthInd(const wlanif_auth_ind_t* ind) {
ASSERT_EQ(std::memcmp(ind->peer_sta_address, ind_expect_mac_.byte, ETH_ALEN), 0);
auth_ind_recv_ = true;
}
void CreateSoftAPTest::OnDeauthInd(const wlanif_deauth_indication_t* ind) {
ASSERT_EQ(std::memcmp(ind->peer_sta_address, ind_expect_mac_.byte, ETH_ALEN), 0);
deauth_ind_recv_ = true;
}
void CreateSoftAPTest::OnAssocInd(const wlanif_assoc_ind_t* ind) {
ASSERT_EQ(std::memcmp(ind->peer_sta_address, ind_expect_mac_.byte, ETH_ALEN), 0);
assoc_ind_recv_ = true;
}
void CreateSoftAPTest::OnDisassocInd(const wlanif_disassoc_indication_t* ind) {
ASSERT_EQ(std::memcmp(ind->peer_sta_address, ind_expect_mac_.byte, ETH_ALEN), 0);
disassoc_ind_recv_ = true;
}
void CreateSoftAPTest::OnStartConf(const wlanif_start_confirm_t* resp) {
start_conf_received_ = true;
start_conf_status_ = resp->result_code;
}
void CreateSoftAPTest::OnStopConf(const wlanif_stop_confirm_t* resp) {
stop_conf_received_ = true;
stop_conf_status_ = resp->result_code;
}
void CreateSoftAPTest::OnChannelSwitch(const wlanif_channel_switch_info_t* info) {}
void CreateSoftAPTest::TxAssocReq(common::MacAddr client_mac) {
// Get the mac address of the SoftAP
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
simulation::SimAssocReqFrame assoc_req_frame(client_mac, soft_ap_mac, kDefaultSsid);
env_->Tx(assoc_req_frame, tx_info_, this);
}
void CreateSoftAPTest::TxAuthReq(simulation::SimAuthType auth_type, common::MacAddr client_mac) {
// Get the mac address of the SoftAP
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
simulation::SimAuthFrame auth_req_frame(client_mac, soft_ap_mac, 1, auth_type,
WLAN_STATUS_CODE_SUCCESS);
env_->Tx(auth_req_frame, tx_info_, this);
}
void CreateSoftAPTest::TxDisassocReq(common::MacAddr client_mac) {
// Get the mac address of the SoftAP
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
// Disassociate with the SoftAP
simulation::SimDisassocReqFrame disassoc_req_frame(client_mac, soft_ap_mac, 0);
env_->Tx(disassoc_req_frame, tx_info_, this);
}
void CreateSoftAPTest::TxDeauthReq(common::MacAddr client_mac) {
// Get the mac address of the SoftAP
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
// Disassociate with the SoftAP
simulation::SimDeauthFrame deauth_frame(client_mac, soft_ap_mac, 0);
env_->Tx(deauth_frame, tx_info_, this);
}
void CreateSoftAPTest::VerifyAuth() {
ASSERT_EQ(auth_ind_recv_, true);
// When auth is done, the client is already added into client list.
VerifyNumOfClient(1);
}
void CreateSoftAPTest::VerifyAssoc() {
// Verify the event indications were received and
// the number of clients
ASSERT_EQ(auth_ind_recv_, true);
ASSERT_EQ(assoc_ind_recv_, true);
VerifyNumOfClient(1);
}
void CreateSoftAPTest::ClearAssocInd() { assoc_ind_recv_ = false; }
void CreateSoftAPTest::VerifyNumOfClient(uint16_t expect_client_num) {
brcmf_simdev* sim = device_->GetSim();
uint16_t num_clients = sim->sim_fw->GetNumClients(softap_ifc_.iface_id_);
ASSERT_EQ(num_clients, expect_client_num);
}
void CreateSoftAPTest::VerifyNotAssoc() {
ASSERT_EQ(assoc_ind_recv_, false);
ASSERT_EQ(auth_ind_recv_, false);
VerifyNumOfClient(0);
}
void CreateSoftAPTest::VerifyStartAPConf(uint8_t status) {
ASSERT_EQ(start_conf_received_, true);
ASSERT_EQ(start_conf_status_, status);
}
void CreateSoftAPTest::VerifyStopAPConf(uint8_t status) {
ASSERT_EQ(stop_conf_received_, true);
ASSERT_EQ(stop_conf_status_, status);
}
TEST_F(CreateSoftAPTest, SetDefault) {
Init();
CreateInterface();
DeleteInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(1));
}
TEST_F(CreateSoftAPTest, CreateSoftAP) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
zx::duration delay = zx::msec(10);
SCHEDULE_CALL(delay, &CreateSoftAPTest::StartSoftAP, this);
delay += kStartAPConfDelay + zx::msec(10);
SCHEDULE_CALL(delay, &CreateSoftAPTest::StopSoftAP, this);
// Wait until SoftAP start conf is received
// SCHEDULE_CALL(delay, &CreateSoftAPTest::VerifyStartAPConf, this, WLAN_START_RESULT_SUCCESS);
env_->Run();
VerifyStartAPConf(WLAN_START_RESULT_SUCCESS);
}
TEST_F(CreateSoftAPTest, CreateSoftAPFail) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
InjectStartAPError();
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::StartSoftAP, this);
env_->Run();
VerifyStartAPConf(WLAN_START_RESULT_NOT_SUPPORTED);
}
// Fail the iovar bss but Stop AP should still succeed
TEST_F(CreateSoftAPTest, BssIovarFail) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
InjectStopAPError();
// Start SoftAP
StartSoftAP();
StopSoftAP();
VerifyStopAPConf(WLAN_START_RESULT_SUCCESS);
}
// Start SoftAP in secure mode and then restart in open mode.
// Appropriate secure mode is checked in StartSoftAP() after SoftAP
// is started
TEST_F(CreateSoftAPTest, CreateSecureSoftAP) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
// Start SoftAP in secure mode
sec_enabled_ = true;
StartSoftAP();
StopSoftAP();
// Restart SoftAP in open mode
StartSoftAP();
StopSoftAP();
}
TEST_F(CreateSoftAPTest, AssociateWithSoftAP) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(8), &CreateSoftAPTest::VerifyAuth, this);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::VerifyAssoc, this);
env_->Run();
}
TEST_F(CreateSoftAPTest, ReassociateWithSoftAP) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(8), &CreateSoftAPTest::VerifyAuth, this);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::VerifyAssoc, this);
SCHEDULE_CALL(zx::msec(75), &CreateSoftAPTest::ClearAssocInd, this);
// Reassoc
SCHEDULE_CALL(zx::msec(100), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(150), &CreateSoftAPTest::VerifyAssoc, this);
env_->Run();
}
TEST_F(CreateSoftAPTest, DisassociateFromSoftAP) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(8), &CreateSoftAPTest::VerifyAuth, this);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::VerifyAssoc, this);
SCHEDULE_CALL(zx::msec(60), &CreateSoftAPTest::TxDisassocReq, this, kFakeMac);
env_->Run();
// Only disassoc ind should be seen.
EXPECT_EQ(deauth_ind_recv_, false);
EXPECT_EQ(disassoc_ind_recv_, true);
VerifyNumOfClient(0);
}
TEST_F(CreateSoftAPTest, AssocWithWrongAuth) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_SHARED_KEY,
kFakeMac);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(20), &CreateSoftAPTest::VerifyNotAssoc, this);
env_->Run();
EXPECT_EQ(auth_resp_status_, WLAN_STATUS_CODE_REFUSED);
}
TEST_F(CreateSoftAPTest, DeauthBeforeAssoc) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::VerifyAuth, this);
SCHEDULE_CALL(zx::msec(20), &CreateSoftAPTest::TxDeauthReq, this, kFakeMac);
env_->Run();
// Only deauth ind shoulb be seen.
EXPECT_EQ(deauth_ind_recv_, true);
EXPECT_EQ(disassoc_ind_recv_, false);
VerifyNumOfClient(0);
}
TEST_F(CreateSoftAPTest, DeauthWhileAssociated) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(8), &CreateSoftAPTest::VerifyAuth, this);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::VerifyAssoc, this);
SCHEDULE_CALL(zx::msec(60), &CreateSoftAPTest::TxDeauthReq, this, kFakeMac);
env_->Run();
// Both indication should be seen.
EXPECT_EQ(deauth_ind_recv_, true);
EXPECT_EQ(disassoc_ind_recv_, true);
VerifyNumOfClient(0);
}
const common::MacAddr kSecondClientMac({0xde, 0xad, 0xbe, 0xef, 0x00, 0x04});
TEST_F(CreateSoftAPTest, DeauthMultiClients) {
Init();
CreateInterface();
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
StartSoftAP();
SCHEDULE_CALL(zx::msec(5), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kFakeMac);
SCHEDULE_CALL(zx::msec(10), &CreateSoftAPTest::TxAssocReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(15), &CreateSoftAPTest::SetExpectMacForInds, this, kSecondClientMac);
SCHEDULE_CALL(zx::msec(20), &CreateSoftAPTest::TxAuthReq, this, simulation::AUTH_TYPE_OPEN,
kSecondClientMac);
SCHEDULE_CALL(zx::msec(30), &CreateSoftAPTest::TxAssocReq, this, kSecondClientMac);
SCHEDULE_CALL(zx::msec(40), &CreateSoftAPTest::VerifyNumOfClient, this, 2);
SCHEDULE_CALL(zx::msec(45), &CreateSoftAPTest::SetExpectMacForInds, this, kFakeMac);
SCHEDULE_CALL(zx::msec(50), &CreateSoftAPTest::TxDeauthReq, this, kFakeMac);
SCHEDULE_CALL(zx::msec(60), &CreateSoftAPTest::VerifyNumOfClient, this, 1);
env_->Run();
}
} // namespace wlan::brcmfmac