blob: 55a399ae28330d9b817a73b8372090eeea56b767 [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 <arpa/inet.h>
#include <fuchsia/hardware/wlanif/c/banjo.h>
#include <fuchsia/wlan/ieee80211/cpp/fidl.h>
#include <zircon/errors.h>
#include <gtest/gtest.h>
#include "src/connectivity/wlan/drivers/testing/lib/sim-device/device.h"
#include "src/connectivity/wlan/drivers/testing/lib/sim-env/sim-env.h"
#include "src/connectivity/wlan/drivers/testing/lib/sim-fake-ap/sim-fake-ap.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/sim.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/test/sim_test.h"
#include "src/connectivity/wlan/lib/common/cpp/include/wlan/common/macaddr.h"
namespace wlan::brcmfmac {
#define OUR_MAC \
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa }
#define THEIR_MAC \
{ 0xde, 0xad, 0xbe, 0xef, 0x00, 0x02 }
const common::MacAddr kOurMac(OUR_MAC);
const common::MacAddr kTheirMac(THEIR_MAC);
// A simple ARP request
const ether_arp kSampleArpReq = {.ea_hdr = {.ar_hrd = htons(ETH_P_802_3),
.ar_pro = htons(ETH_P_IP),
.ar_hln = 6,
.ar_pln = 4,
.ar_op = htons(ARPOP_REQUEST)},
.arp_sha = OUR_MAC,
.arp_spa = {192, 168, 42, 11},
.arp_tha = THEIR_MAC,
.arp_tpa = {100, 101, 102, 103}};
const std::vector<uint8_t> kDummyData = {0, 1, 2, 3, 4};
struct GenericIfc : public SimInterface {
void OnAssocInd(const wlanif_assoc_ind_t* ind) override;
void OnDataRecv(const void* frame, size_t frame_size, uint32_t flags) override;
void OnStartConf(const wlanif_start_confirm_t* resp) override;
void OnStopConf(const wlanif_stop_confirm_t* resp) override;
unsigned int arp_frames_received_ = 0;
unsigned int non_arp_frames_received_ = 0;
bool assoc_ind_recv_ = false;
};
class ArpTest : public SimTest {
public:
static constexpr zx::duration kTestDuration = zx::sec(100);
ArpTest() = default;
void Init();
void CleanupApInterface();
// Send a frame directly into the environment
void Tx(const std::vector<uint8_t>& ethFrame);
// Interface management
zx_status_t SetMulticastPromisc(bool enable);
// Simulation of client associating to a SoftAP interface
void TxAuthandAssocReq();
void VerifyAssoc();
// Frame processing
void ScheduleArpFrameTx(zx::duration when, bool expect_rx);
void ScheduleNonArpFrameTx(zx::duration when);
static std::vector<uint8_t> CreateEthernetFrame(common::MacAddr dstAddr, common::MacAddr srcAddr,
uint16_t ethType, const uint8_t* ethBody,
size_t ethBodySize);
protected:
std::vector<uint8_t> ethFrame;
GenericIfc sim_ifc_;
};
void GenericIfc::OnAssocInd(const wlanif_assoc_ind_t* ind) {
ASSERT_EQ(std::memcmp(ind->peer_sta_address, kTheirMac.byte, ETH_ALEN), 0);
assoc_ind_recv_ = true;
}
void GenericIfc::OnDataRecv(const void* frame, size_t size, uint32_t flags) {
const uint8_t* frame_bytes = reinterpret_cast<const uint8_t*>(frame);
// For this test only, assume anything that is the right size is an ARP frame
if (size != sizeof(ethhdr) + sizeof(ether_arp)) {
non_arp_frames_received_++;
ASSERT_EQ(size, sizeof(ethhdr) + kDummyData.size());
EXPECT_EQ(memcmp(kDummyData.data(), &frame_bytes[sizeof(ethhdr)], kDummyData.size()), 0);
return;
}
arp_frames_received_++;
const ethhdr* eth_hdr = reinterpret_cast<const ethhdr*>(frame_bytes);
EXPECT_EQ(memcmp(eth_hdr->h_dest, common::kBcastMac.byte, ETH_ALEN), 0);
EXPECT_EQ(memcmp(eth_hdr->h_source, kTheirMac.byte, ETH_ALEN), 0);
EXPECT_EQ(ntohs(eth_hdr->h_proto), ETH_P_ARP);
const ether_arp* arp_hdr = reinterpret_cast<const ether_arp*>(frame_bytes + sizeof(ethhdr));
EXPECT_EQ(memcmp(arp_hdr, &kSampleArpReq, sizeof(ether_arp)), 0);
}
void GenericIfc::OnStartConf(const wlanif_start_confirm_t* resp) {
ASSERT_EQ(resp->result_code, WLAN_START_RESULT_SUCCESS);
}
void GenericIfc::OnStopConf(const wlanif_stop_confirm_t* resp) {
ASSERT_EQ(resp->result_code, WLAN_STOP_RESULT_SUCCESS);
}
void ArpTest::Init() { ASSERT_EQ(SimTest::Init(), ZX_OK); }
// static
std::vector<uint8_t> ArpTest::CreateEthernetFrame(common::MacAddr dstAddr, common::MacAddr srcAddr,
uint16_t ethType, const uint8_t* ethBody,
size_t ethBodySize) {
std::vector<uint8_t> ethFrame;
ethFrame.resize(14 + ethBodySize);
memcpy(ethFrame.data(), &dstAddr, sizeof(dstAddr));
memcpy(ethFrame.data() + common::kMacAddrLen, &srcAddr, sizeof(srcAddr));
ethFrame.at(common::kMacAddrLen * 2) = ethType >> 8;
ethFrame.at(common::kMacAddrLen * 2 + 1) = ethType;
memcpy(ethFrame.data() + 14, ethBody, ethBodySize);
return ethFrame;
}
zx_status_t ArpTest::SetMulticastPromisc(bool enable) {
wlanif_impl_protocol_ops_t* ops = sim_ifc_.if_impl_ops_;
void* ctx = sim_ifc_.if_impl_ctx_;
return ops->set_multicast_promisc(ctx, enable);
}
void ArpTest::TxAuthandAssocReq() {
// Get the mac address of the SoftAP
const common::MacAddr mac(kTheirMac);
simulation::WlanTxInfo tx_info = {.channel = SimInterface::kDefaultSoftApChannel};
simulation::SimAuthFrame auth_req_frame(mac, kOurMac, 1, simulation::AUTH_TYPE_OPEN,
::fuchsia::wlan::ieee80211::StatusCode::SUCCESS);
env_->Tx(auth_req_frame, tx_info, this);
simulation::SimAssocReqFrame assoc_req_frame(mac, kOurMac, SimInterface::kDefaultSoftApSsid);
env_->Tx(assoc_req_frame, tx_info, this);
}
void ArpTest::VerifyAssoc() {
// Verify the event indications were received and
// the number of clients
ASSERT_EQ(sim_ifc_.assoc_ind_recv_, true);
brcmf_simdev* sim = device_->GetSim();
uint16_t num_clients = sim->sim_fw->GetNumClients(sim_ifc_.iface_id_);
ASSERT_EQ(num_clients, 1U);
}
void ArpTest::CleanupApInterface() {
sim_ifc_.StopSoftAp();
EXPECT_EQ(DeleteInterface(&sim_ifc_), ZX_OK);
}
void ArpTest::Tx(const std::vector<uint8_t>& ethFrame) {
const ethhdr* eth_hdr = reinterpret_cast<const ethhdr*>(ethFrame.data());
common::MacAddr dst(eth_hdr->h_dest);
common::MacAddr src(eth_hdr->h_source);
simulation::SimQosDataFrame dataFrame(true, false, dst, src, common::kBcastMac, 0, ethFrame);
simulation::WlanTxInfo tx_info = {.channel = SimInterface::kDefaultSoftApChannel};
env_->Tx(dataFrame, tx_info, this);
}
void ArpTest::ScheduleArpFrameTx(zx::duration when, bool expect_rx) {
ether_arp arp_frame = kSampleArpReq;
// If we don't expect to receive this frame, corrupt it so that it will fail if it does arrive
if (!expect_rx) {
arp_frame.arp_tpa[0] = ~arp_frame.arp_tpa[0];
}
std::vector<uint8_t> frame_bytes =
CreateEthernetFrame(common::kBcastMac, kTheirMac, ETH_P_ARP,
reinterpret_cast<const uint8_t*>(&arp_frame), sizeof(arp_frame));
env_->ScheduleNotification(std::bind(&ArpTest::Tx, this, frame_bytes), when);
}
void ArpTest::ScheduleNonArpFrameTx(zx::duration when) {
std::vector<uint8_t> frame_bytes =
CreateEthernetFrame(kOurMac, kTheirMac, 0, kDummyData.data(), kDummyData.size());
env_->ScheduleNotification(std::bind(&ArpTest::Tx, this, frame_bytes), when);
}
// Verify that an ARP frame received by an AP interface is not offloaded, even after multicast
// promiscuous mode is enabled.
TEST_F(ArpTest, SoftApArpOffload) {
Init();
ASSERT_EQ(SimTest::StartInterface(WLAN_INFO_MAC_ROLE_AP, &sim_ifc_, std::nullopt, kOurMac),
ZX_OK);
sim_ifc_.StartSoftAp();
// Have the test associate with the AP
env_->ScheduleNotification(std::bind(&ArpTest::TxAuthandAssocReq, this), zx::sec(1));
env_->ScheduleNotification(std::bind(&ArpTest::VerifyAssoc, this), zx::sec(2));
// Send an ARP frame that we expect to be received
ScheduleArpFrameTx(zx::sec(3), true);
ScheduleNonArpFrameTx(zx::sec(4));
env_->ScheduleNotification(std::bind(&ArpTest::SetMulticastPromisc, this, true), zx::sec(5));
// Send an ARP frame that we expect to be received
ScheduleArpFrameTx(zx::sec(6), true);
ScheduleNonArpFrameTx(zx::sec(7));
// Stop AP and remove interface
env_->ScheduleNotification(std::bind(&ArpTest::CleanupApInterface, this), zx::sec(8));
env_->Run(kTestDuration);
// Verify that no ARP frames were offloaded
EXPECT_EQ(sim_ifc_.arp_frames_received_, 2U);
// Verify that no non-ARP frames were suppressed
EXPECT_EQ(sim_ifc_.non_arp_frames_received_, 2U);
}
// On a client interface, we expect all ARP frames to be offloaded to firmware, regardless of
// the multicast promiscuous setting.
TEST_F(ArpTest, ClientArpOffload) {
Init();
ASSERT_EQ(SimTest::StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &sim_ifc_, std::nullopt, kOurMac),
ZX_OK);
// Start a fake AP
simulation::FakeAp ap(env_.get(), kTheirMac, SimInterface::kDefaultSoftApSsid,
SimInterface::kDefaultSoftApChannel);
// Associate with fake AP
sim_ifc_.AssociateWith(ap, zx::sec(1));
// Send an ARP frame that we expect to be offloaded
ScheduleArpFrameTx(zx::sec(2), false);
ScheduleNonArpFrameTx(zx::sec(3));
env_->ScheduleNotification(std::bind(&ArpTest::SetMulticastPromisc, this, true), zx::sec(4));
// Send an ARP frame that we expect to be offloaded
ScheduleArpFrameTx(zx::sec(5), false);
ScheduleNonArpFrameTx(zx::sec(6));
env_->Run(kTestDuration);
// Verify that we completed the association process
EXPECT_EQ(sim_ifc_.stats_.assoc_successes, 1U);
// Verify that all ARP frames were offloaded
EXPECT_EQ(sim_ifc_.arp_frames_received_, 0U);
// Verify that no non-ARP frames were suppressed
EXPECT_EQ(sim_ifc_.non_arp_frames_received_, 2U);
}
} // namespace wlan::brcmfmac