blob: a75da1cf21048af34f6a66d9e79e4587233ba925 [file] [log] [blame]
// Copyright 2017 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 <wlan/mlme/dispatcher.h>
#include <fbl/unique_ptr.h>
#include <wlan/common/band.h>
#include <wlan/common/channel.h>
#include <wlan/common/mac_frame.h>
#include <wlan/common/stats.h>
#include <wlan/mlme/ap/ap_mlme.h>
#include <wlan/mlme/client/client_mlme.h>
#include <wlan/mlme/debug.h>
#include <wlan/mlme/device_interface.h>
#include <wlan/mlme/mac_frame.h>
#include <wlan/mlme/packet.h>
#include <wlan/mlme/service.h>
#include <wlan/protocol/mac.h>
#include <zircon/fidl.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <fuchsia/wlan/minstrel/cpp/fidl.h>
#include <fuchsia/wlan/mlme/c/fidl.h>
#include <fuchsia/wlan/mlme/cpp/fidl.h>
#include <zircon/fidl.h>
#include <atomic>
#include <cinttypes>
#include <cstring>
#include <sstream>
#include "lib/svc/cpp/services.h"
namespace wlan {
namespace wlan_mlme = ::fuchsia::wlan::mlme;
namespace wlan_minstrel = ::fuchsia::wlan::minstrel;
namespace wlan_stats = ::fuchsia::wlan::stats;
Dispatcher::Dispatcher(DeviceInterface* device, fbl::unique_ptr<Mlme> mlme)
: device_(device), mlme_(std::move(mlme)) {
debugfn();
ZX_ASSERT(mlme_ != nullptr);
}
Dispatcher::~Dispatcher() {}
zx_status_t Dispatcher::HandlePacket(fbl::unique_ptr<Packet> packet) {
debugfn();
ZX_DEBUG_ASSERT(packet != nullptr);
ZX_DEBUG_ASSERT(packet->peer() != Packet::Peer::kUnknown);
finspect("Packet: %s\n", debug::Describe(*packet).c_str());
WLAN_STATS_INC(any_packet.in);
// If there is no active MLME, block all packets but service ones.
// MLME-JOIN.request and MLME-START.request implicitly select a mode and initialize the
// MLME. DEVICE_QUERY.request is used to obtain device capabilities.
auto service_msg = (packet->peer() == Packet::Peer::kService);
if (mlme_ == nullptr && !service_msg) {
WLAN_STATS_INC(any_packet.drop);
return ZX_OK;
}
WLAN_STATS_INC(any_packet.out);
zx_status_t status = ZX_OK;
switch (packet->peer()) {
case Packet::Peer::kEthernet:
status = mlme_->HandleFramePacket(std::move(packet));
break;
case Packet::Peer::kWlan:
if (auto fc = packet->field<FrameControl>(0)) {
switch (fc->type()) {
case FrameType::kManagement:
WLAN_STATS_INC(mgmt_frame.in);
break;
case FrameType::kControl:
WLAN_STATS_INC(ctrl_frame.in);
break;
case FrameType::kData:
WLAN_STATS_INC(data_frame.in);
break;
default:
break;
}
status = mlme_->HandleFramePacket(std::move(packet));
}
break;
default:
break;
}
return status;
}
zx_status_t Dispatcher::HandlePortPacket(uint64_t key) {
debugfn();
ZX_DEBUG_ASSERT(ToPortKeyType(key) == PortKeyType::kMlme);
ObjectId id(ToPortKeyId(key));
switch (id.subtype()) {
case to_enum_type(ObjectSubtype::kTimer): {
auto status = mlme_->HandleTimeout(id);
if (status == ZX_ERR_NOT_SUPPORTED) {
warnf("unknown MLME timer target: %u\n", id.target());
}
break;
}
default:
warnf("unknown MLME event subtype: %u\n", id.subtype());
}
return ZX_OK;
}
zx_status_t Dispatcher::HandleAnyMlmeMessage(Span<uint8_t> span) {
debugfn();
auto hdr = FromBytes<fidl_message_header_t>(span);
if (hdr == nullptr) {
errorf("short mlme message, len=%zu\n", span.size());
return ZX_OK;
}
debughdr("service packet txid=%u flags=%u ordinal=%u\n", hdr->txid, hdr->flags, hdr->ordinal);
// Messages in wlan_mlme_ext.fidl does not involve MLME.
switch (hdr->ordinal) {
case fuchsia_wlan_mlme_MLMEQueryDeviceInfoOrdinal:
return HandleQueryDeviceInfo(hdr->txid);
case fuchsia_wlan_mlme_MLMEStatsQueryReqOrdinal:
return HandleMlmeStats(hdr->ordinal);
case fuchsia_wlan_mlme_MLMEListMinstrelPeersOrdinal:
return HandleMinstrelPeerList(hdr->ordinal, hdr->txid);
case fuchsia_wlan_mlme_MLMEGetMinstrelStatsOrdinal:
return HandleMinstrelTxStats(span, hdr->ordinal, hdr->txid);
}
switch (hdr->ordinal) {
case fuchsia_wlan_mlme_MLMEResetReqOrdinal:
infof("resetting MLME\n");
HandleMlmeMessage<wlan_mlme::ResetRequest>(span, hdr->ordinal);
return ZX_OK;
case fuchsia_wlan_mlme_MLMEStartReqOrdinal:
return HandleMlmeMessage<wlan_mlme::StartRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEStopReqOrdinal:
return HandleMlmeMessage<wlan_mlme::StopRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEStartScanOrdinal:
return HandleMlmeMessage<wlan_mlme::ScanRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEJoinReqOrdinal:
return HandleMlmeMessage<wlan_mlme::JoinRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEAuthenticateReqOrdinal:
return HandleMlmeMessage<wlan_mlme::AuthenticateRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEAuthenticateRespOrdinal:
return HandleMlmeMessage<wlan_mlme::AuthenticateResponse>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEDeauthenticateReqOrdinal:
return HandleMlmeMessage<wlan_mlme::DeauthenticateRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEAssociateReqOrdinal:
return HandleMlmeMessage<wlan_mlme::AssociateRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEAssociateRespOrdinal:
return HandleMlmeMessage<wlan_mlme::AssociateResponse>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEEapolReqOrdinal:
return HandleMlmeMessage<wlan_mlme::EapolRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMESetKeysReqOrdinal:
return HandleMlmeMessage<wlan_mlme::SetKeysRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMESetControlledPortOrdinal:
return HandleMlmeMessage<wlan_mlme::SetControlledPortRequest>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMESendMpOpenActionOrdinal:
return HandleMlmeMessage<wlan_mlme::MeshPeeringOpenAction>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMESendMpConfirmActionOrdinal:
return HandleMlmeMessage<wlan_mlme::MeshPeeringConfirmAction>(span, hdr->ordinal);
case fuchsia_wlan_mlme_MLMEMeshPeeringEstablishedOrdinal:
return HandleMlmeMessage<wlan_mlme::MeshPeeringParams>(span, hdr->ordinal);
default:
warnf("unknown MLME method %u\n", hdr->ordinal);
return ZX_ERR_NOT_SUPPORTED;
}
}
template <typename Message>
zx_status_t Dispatcher::HandleMlmeMessage(Span<uint8_t> span, uint32_t ordinal) {
auto msg = MlmeMsg<Message>::Decode(span, ordinal);
if (!msg.has_value()) {
errorf("could not deserialize MLME primitive %d: \n", ordinal);
return ZX_ERR_INVALID_ARGS;
}
return mlme_->HandleMlmeMsg(*msg);
}
zx_status_t Dispatcher::HandleQueryDeviceInfo(zx_txid_t txid) {
debugfn();
wlan_mlme::DeviceInfo resp;
const wlan_info_t& info = device_->GetWlanInfo().ifc_info;
memcpy(resp.mac_addr.mutable_data(), info.mac_addr, ETH_MAC_SIZE);
// mac_role is a bitfield, but only a single value is supported for an interface
switch (info.mac_role) {
case WLAN_MAC_ROLE_CLIENT:
resp.role = wlan_mlme::MacRole::CLIENT;
break;
case WLAN_MAC_ROLE_AP:
resp.role = wlan_mlme::MacRole::AP;
break;
case WLAN_MAC_ROLE_MESH:
resp.role = wlan_mlme::MacRole::MESH;
break;
default:
// TODO(NET-1116): return an error!
break;
}
auto wlanmac_info = device_->GetWlanInfo().ifc_info;
resp.bands.resize(0);
for (uint8_t band_idx = 0; band_idx < info.num_bands; band_idx++) {
const wlan_band_info_t& band_info = info.bands[band_idx];
wlan_mlme::BandCapabilities band;
band.band_id = wlan::common::BandToFidl(band_info.band_id);
band.basic_rates.resize(0);
for (size_t rate_idx = 0; rate_idx < sizeof(band_info.basic_rates); rate_idx++) {
if (band_info.basic_rates[rate_idx] != 0) {
band.basic_rates.push_back(band_info.basic_rates[rate_idx]);
}
}
const wlan_chan_list_t& chan_list = band_info.supported_channels;
band.base_frequency = chan_list.base_freq;
band.channels.resize(0);
for (size_t chan_idx = 0; chan_idx < sizeof(chan_list.channels); chan_idx++) {
if (chan_list.channels[chan_idx] != 0) {
band.channels.push_back(chan_list.channels[chan_idx]);
}
}
band.cap = CapabilityInfo::FromDdk(wlanmac_info.caps).ToFidl();
if (band_info.ht_supported) {
auto ht_cap = HtCapabilities::FromDdk(band_info.ht_caps);
band.ht_cap = std::make_unique<wlan_mlme::HtCapabilities>(ht_cap.ToFidl());
}
if (band_info.vht_supported) {
auto vht_cap = VhtCapabilities::FromDdk(band_info.vht_caps);
band.vht_cap = std::make_unique<wlan_mlme::VhtCapabilities>(vht_cap.ToFidl());
}
resp.bands.push_back(std::move(band));
}
return SendServiceMsg(device_, &resp, fuchsia_wlan_mlme_MLMEQueryDeviceInfoOrdinal, txid);
}
zx_status_t Dispatcher::HandleMlmeStats(uint32_t ordinal) const {
debugfn();
wlan_mlme::StatsQueryResponse resp = GetStatsToFidl();
return SendServiceMsg(device_, &resp, fuchsia_wlan_mlme_MLMEStatsQueryRespOrdinal);
}
zx_status_t Dispatcher::HandleMinstrelPeerList(uint32_t ordinal, zx_txid_t txid) const {
debugfn();
wlan_mlme::MinstrelListResponse resp{};
zx_status_t status = device_->GetMinstrelPeers(&resp.peers);
if (status != ZX_OK) {
errorf("cannot get minstrel peer list: %s\n", zx_status_get_string(status));
resp.peers.peers.resize(0);
}
return SendServiceMsg(device_, &resp, fuchsia_wlan_mlme_MLMEListMinstrelPeersOrdinal, txid);
}
zx_status_t Dispatcher::HandleMinstrelTxStats(Span<uint8_t> span, uint32_t ordinal,
zx_txid_t txid) const {
debugfn();
wlan_mlme::MinstrelStatsResponse resp{};
auto req = MlmeMsg<wlan_mlme::MinstrelStatsRequest>::Decode(
span, fuchsia_wlan_mlme_MLMEGetMinstrelStatsOrdinal);
if (!req.has_value()) {
errorf("could not deserialize MLME primitive %d\n", ordinal);
return ZX_ERR_INVALID_ARGS;
}
common::MacAddr addr(req->body()->mac_addr);
wlan_minstrel::Peer peer;
auto status = device_->GetMinstrelStats(addr, &peer);
if (status == ZX_OK) {
resp.peer = std::make_unique<wlan_minstrel::Peer>(std::move(peer));
} else {
errorf("could not get peer stats: %s\n", zx_status_get_string(status));
}
return SendServiceMsg(device_, &resp, fuchsia_wlan_mlme_MLMEGetMinstrelStatsOrdinal, txid);
}
void Dispatcher::HwIndication(uint32_t ind) {
debugfn();
mlme_->HwIndication(ind);
}
void Dispatcher::HwScanComplete(uint8_t result_code) {
debugfn();
mlme_->HwScanComplete(result_code);
}
void Dispatcher::ResetStats() {
stats_.Reset();
if (mlme_) { mlme_->ResetMlmeStats(); }
}
wlan_mlme::StatsQueryResponse Dispatcher::GetStatsToFidl() const {
wlan_mlme::StatsQueryResponse stats_response;
stats_response.stats.dispatcher_stats = stats_.ToFidl();
auto mlme_stats = mlme_->GetMlmeStats();
if (!mlme_stats.has_invalid_tag()) {
stats_response.stats.mlme_stats =
std::make_unique<wlan_stats::MlmeStats>(mlme_->GetMlmeStats());
}
return stats_response;
}
} // namespace wlan