blob: adbd756d2f44be31337dcb3e54f3e98b1956227b [file] [log] [blame]
// Copyright 2018 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 <lib/timekeeper/clock.h>
#include <wlan/common/buffer_writer.h>
#include <wlan/mlme/client/client_mlme.h>
#include <wlan/mlme/mac_frame.h>
#include <wlan/mlme/packet.h>
#include <wlan/mlme/service.h>
#include <wlan/mlme/timer.h>
#include <fbl/unique_ptr.h>
#include <fuchsia/wlan/mlme/c/fidl.h>
#include <fuchsia/wlan/mlme/cpp/fidl.h>
#include "mock_device.h"
#include "test_bss.h"
#include "test_utils.h"
#include <gtest/gtest.h>
namespace wlan {
namespace {
namespace wlan_mlme = ::fuchsia::wlan::mlme;
static constexpr uint8_t kTestPayload[] = "Hello Fuchsia";
struct ClientTest : public ::testing::Test {
ClientTest() : device(), client(&device) {}
void SetUp() override {
device.SetTime(zx::time(0));
client.Init();
TriggerTimeout(ObjectTarget::kChannelScheduler);
}
zx_status_t SendDataFrame() {
auto frame = CreateDataFrame(kTestPayload, sizeof(kTestPayload));
if (frame.IsEmpty()) { return ZX_ERR_NO_RESOURCES; }
client.HandleFramePacket(frame.Take());
return ZX_OK;
}
zx_status_t SendEmptyDataFrame() {
auto frame = CreateDataFrame(nullptr, 0);
if (frame.IsEmpty()) { return ZX_ERR_NO_RESOURCES; }
client.HandleFramePacket(frame.Take());
return ZX_OK;
}
zx_status_t SendNullDataFrame() {
auto frame = CreateNullDataFrame();
if (frame.IsEmpty()) { return ZX_ERR_NO_RESOURCES; }
client.HandleFramePacket(frame.Take());
return ZX_OK;
}
zx_status_t SendEthFrame() {
auto eth_frame = CreateEthFrame(kTestPayload, sizeof(kTestPayload));
if (eth_frame.IsEmpty()) { return ZX_ERR_NO_RESOURCES; }
client.HandleFramePacket(eth_frame.Take());
return ZX_OK;
}
void SendBeaconFrame(common::MacAddr bssid = common::MacAddr(kBssid1)) {
client.HandleFramePacket(CreateBeaconFrame(bssid));
}
void TriggerTimeout(ObjectTarget target) {
ObjectId timer_id;
timer_id.set_subtype(to_enum_type(ObjectSubtype::kTimer));
timer_id.set_target(to_enum_type(target));
client.HandleTimeout(timer_id);
}
void Join() {
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateJoinRequest()));
device.svc_queue.clear();
}
void Authenticate() {
client.HandleMlmeMsg(CreateAuthRequest());
client.HandleFramePacket(CreateAuthRespFrame(AuthAlgorithm::kOpenSystem));
device.svc_queue.clear();
device.wlan_queue.clear();
TriggerTimeout(ObjectTarget::kStation);
TriggerTimeout(ObjectTarget::kChannelScheduler);
}
void Associate() {
client.HandleMlmeMsg(CreateAssocRequest(false));
client.HandleFramePacket(CreateAssocRespFrame());
device.svc_queue.clear();
device.wlan_queue.clear();
TriggerTimeout(ObjectTarget::kStation);
TriggerTimeout(ObjectTarget::kChannelScheduler);
}
void Connect() {
Join();
Authenticate();
Associate();
TriggerTimeout(ObjectTarget::kStation);
}
zx::duration BeaconPeriodsToDuration(size_t periods) {
return zx::usec(1024) * (periods * kBeaconPeriodTu);
}
void SetTimeInBeaconPeriods(size_t periods) {
device.SetTime(zx::time(0) + BeaconPeriodsToDuration(periods));
}
void IncreaseTimeByBeaconPeriods(size_t periods) {
device.SetTime(device.GetTime() + BeaconPeriodsToDuration(periods));
}
void GoOffChannel() {
// For our test, scan duration doesn't matter for now since we explicit force station
// to go back on channel by calling `HandleTimeout`
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateScanRequest(kBeaconPeriodTu)));
device.wlan_queue.erase(device.wlan_queue.begin()); // dequeue the power-saving frame
ASSERT_FALSE(client.OnChannel()); // sanity check
}
// Forces station to go back on channel by issuing a timeout to channel scheduler. This assumes
// that a scan message was previously issue to cause station to go off channel.
void GoOnChannel() {
TriggerTimeout(ObjectTarget::kChannelScheduler);
device.wlan_queue.erase(device.wlan_queue.begin()); // dequeue the power-saving frame
ASSERT_TRUE(client.OnChannel()); // sanity check
}
void AssertAuthConfirm(MlmeMsg<wlan_mlme::AuthenticateConfirm> msg,
wlan_mlme::AuthenticateResultCodes result_code) {
ASSERT_EQ(msg.body()->result_code, result_code);
}
void AssertAssocConfirm(MlmeMsg<wlan_mlme::AssociateConfirm> msg, uint16_t aid,
wlan_mlme::AssociateResultCodes result_code) {
ASSERT_EQ(msg.body()->association_id, aid);
ASSERT_EQ(msg.body()->result_code, result_code);
}
void AssertDeauthFrame(fbl::unique_ptr<Packet> pkt, wlan_mlme::ReasonCode reason_code) {
ASSERT_EQ(pkt->peer(), Packet::Peer::kWlan);
auto type_checked_frame = MgmtFrameView<Deauthentication>::CheckType(pkt.get());
ASSERT_TRUE(type_checked_frame);
auto frame = type_checked_frame.CheckLength();
ASSERT_TRUE(frame);
ASSERT_EQ(std::memcmp(frame.hdr()->addr1.byte, kBssid1, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr2.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr3.byte, kBssid1, 6), 0);
ASSERT_EQ(frame.body()->reason_code, static_cast<uint16_t>(reason_code));
}
MockDevice device;
ClientMlme client;
};
TEST_F(ClientTest, Join) {
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateJoinRequest()));
ASSERT_EQ(device.svc_queue.size(), static_cast<size_t>(1));
auto joins =
device.GetServiceMsgs<wlan_mlme::JoinConfirm>(fuchsia_wlan_mlme_MLMEJoinConfOrdinal);
ASSERT_EQ(joins.size(), 1ULL);
ASSERT_EQ(joins[0].body()->result_code, wlan_mlme::JoinResultCodes::SUCCESS);
}
TEST_F(ClientTest, Authenticate) {
Join();
// Send AUTHENTICATION.request. Verify that no confirmation was sent yet.
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateAuthRequest()));
ASSERT_TRUE(device.svc_queue.empty());
// Verify wlan frame is correct.
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
auto pkt = std::move(*device.wlan_queue.begin());
ASSERT_EQ(pkt->peer(), Packet::Peer::kWlan);
auto type_checked_frame = MgmtFrameView<Authentication>::CheckType(pkt.get());
ASSERT_TRUE(type_checked_frame);
auto frame = type_checked_frame.CheckLength();
ASSERT_TRUE(frame);
ASSERT_EQ(std::memcmp(frame.hdr()->addr1.byte, kBssid1, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr2.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr3.byte, kBssid1, 6), 0);
ASSERT_EQ(frame.body()->auth_algorithm_number, AuthAlgorithm::kOpenSystem);
ASSERT_EQ(frame.body()->auth_txn_seq_number, 1);
ASSERT_EQ(frame.body()->status_code, 0);
// Respond with a Authentication frame and verify a AUTHENTICATION.confirm message was sent.
ASSERT_EQ(ZX_OK, client.HandleFramePacket(CreateAuthRespFrame(AuthAlgorithm::kOpenSystem)));
ASSERT_EQ(device.svc_queue.size(), static_cast<size_t>(1));
auto auths = device.GetServiceMsgs<wlan_mlme::AuthenticateConfirm>(
fuchsia_wlan_mlme_MLMEAuthenticateConfOrdinal);
ASSERT_EQ(auths.size(), 1ULL);
AssertAuthConfirm(std::move(auths[0]), wlan_mlme::AuthenticateResultCodes::SUCCESS);
// Verify a delayed timeout won't cause another confirmation.
device.svc_queue.clear();
SetTimeInBeaconPeriods(100);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.svc_queue.empty());
}
TEST_F(ClientTest, Associate) {
Join();
Authenticate();
// Send ASSOCIATE.request. Verify that no confirmation was sent yet.
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateAssocRequest(false)));
ASSERT_TRUE(device.svc_queue.empty());
// Verify wlan frame is correct.
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
auto pkt = std::move(*device.wlan_queue.begin());
ASSERT_EQ(pkt->peer(), Packet::Peer::kWlan);
auto type_checked_frame = MgmtFrameView<AssociationRequest>::CheckType(pkt.get());
ASSERT_TRUE(type_checked_frame);
auto frame = type_checked_frame.CheckLength();
ASSERT_TRUE(frame);
ASSERT_EQ(std::memcmp(frame.hdr()->addr1.byte, kBssid1, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr2.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr3.byte, kBssid1, 6), 0);
auto assoc_req_frame = frame.NextFrame();
Span<const uint8_t> ie_chain{assoc_req_frame.body()->data, assoc_req_frame.body_len()};
ASSERT_TRUE(frame.body()->Validate(ie_chain));
// Respond with a Association Response frame and verify a ASSOCIATE.confirm message was sent.
ASSERT_EQ(ZX_OK, client.HandleFramePacket(CreateAssocRespFrame()));
ASSERT_FALSE(device.svc_queue.empty());
auto assocs = device.GetServiceMsgs<wlan_mlme::AssociateConfirm>(
fuchsia_wlan_mlme_MLMEAssociateConfOrdinal);
ASSERT_EQ(assocs.size(), 1ULL);
AssertAssocConfirm(std::move(assocs[0]), kAid, wlan_mlme::AssociateResultCodes::SUCCESS);
// Verify a delayed timeout won't cause another confirmation.
device.svc_queue.clear();
SetTimeInBeaconPeriods(100);
TriggerTimeout(ObjectTarget::kStation);
assocs = device.GetServiceMsgs<wlan_mlme::AssociateConfirm>(
fuchsia_wlan_mlme_MLMEAssociateConfOrdinal);
ASSERT_EQ(assocs.size(), 0ULL);
}
TEST_F(ClientTest, AuthTimeout) {
Join();
// Send AUTHENTICATE.request. Verify that no confirmation was sent yet.
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateAuthRequest()));
ASSERT_TRUE(device.svc_queue.empty());
// Timeout not yet hit.
SetTimeInBeaconPeriods(kAuthTimeout - 1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.svc_queue.empty());
// Timeout hit, verify a AUTHENTICATION.confirm message was sent.
SetTimeInBeaconPeriods(kAuthTimeout);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.svc_queue.size(), static_cast<size_t>(1));
auto auths = device.GetServiceMsgs<wlan_mlme::AuthenticateConfirm>(
fuchsia_wlan_mlme_MLMEAuthenticateConfOrdinal);
ASSERT_EQ(auths.size(), 1ULL);
AssertAuthConfirm(std::move(auths[0]),
wlan_mlme::AuthenticateResultCodes::AUTH_FAILURE_TIMEOUT);
}
TEST_F(ClientTest, AssocTimeout) {
Join();
Authenticate();
// Send ASSOCIATE.request. Verify that no confirmation was sent yet.
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateAssocRequest(false)));
ASSERT_TRUE(device.svc_queue.empty());
// Timeout not yet hit.
SetTimeInBeaconPeriods(10);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.svc_queue.empty());
// Timeout hit, verify a ASSOCIATE.confirm message was sent.
SetTimeInBeaconPeriods(40);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.svc_queue.size(), static_cast<size_t>(1));
auto assocs = device.GetServiceMsgs<wlan_mlme::AssociateConfirm>(
fuchsia_wlan_mlme_MLMEAssociateConfOrdinal);
ASSERT_EQ(assocs.size(), 1ULL);
AssertAssocConfirm(std::move(assocs[0]), 0,
wlan_mlme::AssociateResultCodes::REFUSED_TEMPORARILY);
}
TEST_F(ClientTest, ExchangeDataAfterAssociation) {
// Verify no data frame is exchanged before being associated.
Join();
device.eth_queue.clear();
SendDataFrame();
SendNullDataFrame();
ASSERT_TRUE(device.eth_queue.empty());
ASSERT_TRUE(device.wlan_queue.empty());
ASSERT_TRUE(device.svc_queue.empty());
Authenticate();
SendDataFrame();
SendNullDataFrame();
ASSERT_TRUE(device.eth_queue.empty());
ASSERT_TRUE(device.wlan_queue.empty());
ASSERT_TRUE(device.svc_queue.empty());
// Associate and send a data frame.
Associate();
SendDataFrame();
auto eth_frames = device.GetEthPackets();
ASSERT_EQ(eth_frames.size(), static_cast<size_t>(1));
ASSERT_TRUE(device.wlan_queue.empty());
ASSERT_TRUE(device.svc_queue.empty());
// Verify queued up ethernet frame is correct.
auto eth_frame = std::move(*eth_frames.begin());
ASSERT_EQ(sizeof(EthernetII) + sizeof(kTestPayload), eth_frame.size());
auto eth_header = reinterpret_cast<const EthernetII*>(eth_frame.data());
ASSERT_EQ(std::memcmp(eth_header->src.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(eth_header->dest.byte, kBssid1, 6), 0);
ASSERT_EQ(eth_header->ether_type, 42);
auto eth_payload = Span<const uint8_t>(eth_frame).subspan(sizeof(EthernetII));
ASSERT_RANGES_EQ(eth_payload, kTestPayload);
// Send null data frame which shouldn't queue up any Ethernet frames but instead a "Keep Alive"
// one.
SendNullDataFrame();
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
ASSERT_TRUE(device.svc_queue.empty());
// Verify queued up "Keep Alive" frame is correct.
auto pkt = std::move(*device.wlan_queue.begin());
ASSERT_EQ(pkt->peer(), Packet::Peer::kWlan);
auto partially_checked = DataFrameView<>::CheckType(pkt.get());
ASSERT_TRUE(partially_checked);
auto data_frame = partially_checked.CheckLength();
ASSERT_EQ(data_frame.hdr()->fc.to_ds(), 1);
ASSERT_EQ(data_frame.hdr()->fc.from_ds(), 0);
ASSERT_EQ(std::memcmp(data_frame.hdr()->addr1.byte, kBssid1, 6), 0);
ASSERT_EQ(std::memcmp(data_frame.hdr()->addr2.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(data_frame.hdr()->addr3.byte, kBssid1, 6), 0);
ASSERT_EQ(data_frame.body_len(), static_cast<size_t>(0));
}
TEST_F(ClientTest, ProcessEmptyDataFrames) {
Connect();
// Send a data frame which carries an LLC frame with no payload.
// Verify no ethernet frame was queued.
SendEmptyDataFrame();
ASSERT_TRUE(device.eth_queue.empty());
}
TEST_F(ClientTest, DropManagementFrames) {
Connect();
// Construct and send deauthentication frame from another BSS.
constexpr size_t max_frame_len = MgmtFrameHeader::max_len() + Deauthentication::max_len();
auto packet = GetWlanPacket(max_frame_len);
ASSERT_NE(packet, nullptr);
BufferWriter w(*packet);
auto mgmt_hdr = w.Write<MgmtFrameHeader>();
mgmt_hdr->fc.set_type(FrameType::kManagement);
mgmt_hdr->fc.set_subtype(ManagementSubtype::kDeauthentication);
mgmt_hdr->addr1 = common::MacAddr(kBssid2);
mgmt_hdr->addr2 = common::MacAddr(kClientAddress);
mgmt_hdr->addr3 = common::MacAddr(kBssid2);
w.Write<Deauthentication>()->reason_code = 42;
client.HandleFramePacket(std::move(packet));
// Verify neither a management frame nor service message were sent.
ASSERT_TRUE(device.svc_queue.empty());
ASSERT_TRUE(device.wlan_queue.empty());
ASSERT_TRUE(device.eth_queue.empty());
// Verify data frames can still be send and the clientis presumably associated.
SendDataFrame();
ASSERT_EQ(device.eth_queue.size(), static_cast<size_t>(1));
}
TEST_F(ClientTest, AutoDeauth_NoBeaconReceived) {
Connect();
// Timeout not yet hit.
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 0ULL);
// Auto-deauth timeout, client should be deauthenticated.
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
TEST_F(ClientTest, AutoDeauth_NoBeaconsShortlyAfterConnecting) {
Connect();
IncreaseTimeByBeaconPeriods(1);
SendBeaconFrame();
// Not enough time has passed yet since beacon frame was sent, so no deauth.
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Auto-deauth triggers now.
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
TEST_F(ClientTest, AutoDeauth_DoNotDeauthWhileSwitchingChannel) {
Connect();
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 1);
GoOffChannel();
// For next two timeouts, still off channel, so should not deauth.
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Have not been back on main channel for long enough, so should not deauth.
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout);
GoOnChannel();
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Before going off channel, we did not receive beacon for `kAutoDeauthTimeout - 1` period.
// Now one more beacon period has passed after going back on channel, so should auto deauth.
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
TEST_F(ClientTest, AutoDeauth_InterleavingBeaconsAndChannelSwitches) {
Connect();
// Going off channel.
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 5); // -- On-channel time without beacon -- //
GoOffChannel();
// No deauth since off channel.
IncreaseTimeByBeaconPeriods(5);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
IncreaseTimeByBeaconPeriods(1);
GoOnChannel();
// Got beacon frame, which should reset the timeout.
IncreaseTimeByBeaconPeriods(3); // -- On-channel time without beacon -- //
SendBeaconFrame(); // -- Beacon timeout refresh -- ///
// No deauth since beacon was received not too long ago.
IncreaseTimeByBeaconPeriods(2); // -- On-channel time without beacon -- //
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Going off channel and back on channel
// Total on-channel time without beacons so far: 2 beacon intervals
GoOffChannel();
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout);
GoOnChannel();
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 3); // -- On-channel time without beacon -- //
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Going off channel and back on channel again
// Total on-channel time without beacons so far: 2 + kAutoDeauthTimeout - 3
GoOffChannel();
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout);
GoOnChannel();
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// One more beacon period and auto-deauth triggers
IncreaseTimeByBeaconPeriods(1); // -- On-channel time without beacon -- //
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
// This test explores what happens if the whole auto-deauth timeout duration is exhausted, but
// the client switches channel before auto-deauth can trigger. For the current implementation
// where we cancel timer when going off channel and reschedule when going back on channel,
// this test is intended to be a safeguard against making the mistake of scheduling or exactly
// in the present when going back on channel.
TEST_F(ClientTest, AutoDeauth_SwitchingChannelBeforeDeauthTimeoutCouldTrigger) {
Connect();
// No deauth since off channel.
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout);
GoOffChannel();
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
IncreaseTimeByBeaconPeriods(1);
GoOnChannel();
// Auto-deauth timeout shouldn't trigger yet. This is because after going back on channel,
// the client should always schedule timeout sufficiently far enough in the future
// (at least one beacon interval)
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.wlan_queue.empty());
// Auto-deauth now
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
TEST_F(ClientTest, AutoDeauth_ForeignBeaconShouldNotPreventDeauth) {
Connect();
IncreaseTimeByBeaconPeriods(kAutoDeauthTimeout - 1);
SendBeaconFrame(common::MacAddr(kBssid2)); // beacon frame from another AP
IncreaseTimeByBeaconPeriods(1);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
AssertDeauthFrame(std::move(*device.wlan_queue.begin()),
wlan_mlme::ReasonCode::LEAVING_NETWORK_DEAUTH);
auto deauth_inds = device.GetServiceMsgs<wlan_mlme::DeauthenticateIndication>(
fuchsia_wlan_mlme_MLMEDeauthenticateIndOrdinal);
ASSERT_EQ(deauth_inds.size(), 1ULL);
}
TEST_F(ClientTest, BufferFramesWhileOffChannelAndSendWhenOnChannel) {
Connect();
GoOffChannel();
SendEthFrame();
ASSERT_TRUE(device.wlan_queue.empty());
GoOnChannel();
ASSERT_EQ(device.wlan_queue.size(), static_cast<size_t>(1));
auto pkt = std::move(*device.wlan_queue.begin());
ASSERT_EQ(pkt->peer(), Packet::Peer::kWlan);
auto type_checked_frame = DataFrameView<LlcHeader>::CheckType(pkt.get());
ASSERT_TRUE(type_checked_frame);
auto frame = type_checked_frame.CheckLength();
ASSERT_TRUE(frame);
ASSERT_EQ(std::memcmp(frame.hdr()->addr1.byte, kBssid1, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr2.byte, kClientAddress, 6), 0);
ASSERT_EQ(std::memcmp(frame.hdr()->addr3.byte, kBssid1, 6), 0);
auto llc_hdr = frame.body();
ASSERT_EQ(frame.body_len() - llc_hdr->len(), sizeof(kTestPayload));
ASSERT_EQ(std::memcmp(llc_hdr->payload, kTestPayload, sizeof(kTestPayload)), 0);
}
TEST_F(ClientTest, InvalidAuthenticationResponse) {
Join();
// Send AUTHENTICATION.request. Verify that no confirmation was sent yet.
ASSERT_EQ(ZX_OK, client.HandleMlmeMsg(CreateAuthRequest()));
ASSERT_TRUE(device.svc_queue.empty());
// Send authentication frame with wrong algorithm.
ASSERT_EQ(ZX_OK, client.HandleFramePacket(CreateAuthRespFrame(AuthAlgorithm::kSae)));
// Verify that AUTHENTICATION.confirm was received.
ASSERT_EQ(device.svc_queue.size(), static_cast<size_t>(1));
auto auths = device.GetServiceMsgs<wlan_mlme::AuthenticateConfirm>(
fuchsia_wlan_mlme_MLMEAuthenticateConfOrdinal);
ASSERT_EQ(auths.size(), 1ULL);
AssertAuthConfirm(std::move(auths[0]),
wlan_mlme::AuthenticateResultCodes::AUTHENTICATION_REJECTED);
// Fast forward in time would have caused a timeout.
// The timeout however should have been canceled and we should not receive
// and additional confirmation.
SetTimeInBeaconPeriods(kAuthTimeout);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.svc_queue.empty());
// Send a second, now valid authentication frame.
// This frame should be ignored as the client reset.
ASSERT_EQ(ZX_OK, client.HandleFramePacket(CreateAuthRespFrame(AuthAlgorithm::kOpenSystem)));
// Fast forward in time far beyond an authentication timeout.
// There should not be any AUTHENTICATION.confirm sent as the client
// is expected to have been reset into |idle| state after failing
// to authenticate.
SetTimeInBeaconPeriods(1000);
TriggerTimeout(ObjectTarget::kStation);
ASSERT_TRUE(device.svc_queue.empty());
}
// Add additional tests for (tracked in NET-801):
// AP refuses Authentication/Association
// Regression tests for:
// - NET-898: PS-POLL after TIM indication.
// Deauthenticate in any state issued by AP/SME.
// Disassociation in any state issued by AP/SME.
// Handle Action frames and setup Block-Ack session.
// Drop data frames from unknown BSS.
// Handle AMSDUs.
// Connect to a:
// - protected network, exchange keys and send protected frames.
// - HT/VHT capable network
// - 5GHz network
// - different network than currently associated to
// Notify driver about association
// Ensure Deauthentication Indicaiton and notification is sent whenever deauthenticating.
// Enter/Leave power management when going off/on channel.
// Verify timeouts don't hit after resetting the station.
} // namespace
} // namespace wlan