blob: c15465abf26f2f82fe49d3324d19f67b88feb503 [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 <wlan/common/buffer_writer.h>
#include <wlan/common/channel.h>
#include <wlan/common/write_element.h>
#include <wlan/mlme/beacon.h>
#include <wlan/mlme/rates_elements.h>
namespace wlan {
static void WriteSsid(BufferWriter* w, const BeaconConfig& config) {
if (config.ssid != nullptr) { common::WriteSsid(w, {config.ssid, config.ssid_len}); }
}
static void WriteDsssParamSet(BufferWriter* w, const BeaconConfig& config) {
common::WriteDsssParamSet(w, config.channel.primary);
}
static void WriteTim(BufferWriter* w, const PsCfg* ps_cfg, size_t* rel_tim_ele_offset) {
if (!ps_cfg) { return; }
// To get the TIM offset in frame, we have to count the header, fixed parameters and tagged
// parameters before TIM is written.
*rel_tim_ele_offset = w->WrittenBytes();
size_t bitmap_len;
uint8_t bitmap_offset;
uint8_t pvb[kMaxTimBitmapLen];
ps_cfg->GetTim()->WritePartialVirtualBitmap(pvb, sizeof(pvb), &bitmap_len, &bitmap_offset);
TimHeader header;
header.dtim_count = ps_cfg->dtim_count();
header.dtim_period = ps_cfg->dtim_period();
header.bmp_ctrl.set_offset(bitmap_offset);
if (ps_cfg->IsDtim()) {
header.bmp_ctrl.set_group_traffic_ind(ps_cfg->GetTim()->HasGroupTraffic());
}
common::WriteTim(w, header, {pvb, bitmap_len});
}
static void WriteCountry(BufferWriter* w, const BeaconConfig& config) {
// TODO(NET-799): Read from dot11CountryString MIB
const Country kCountry = {{'U', 'S', ' '}};
std::vector<SubbandTriplet> subbands;
// TODO(porce): Read from the AP's regulatory domain
if (wlan::common::Is2Ghz(config.channel)) {
subbands.push_back({1, 11, 36});
} else {
subbands.push_back({36, 4, 36});
subbands.push_back({52, 4, 30});
subbands.push_back({100, 12, 30});
subbands.push_back({149, 5, 36});
}
common::WriteCountry(w, kCountry, subbands);
}
static void WriteHtCapabilities(BufferWriter* w, const BeaconConfig& config) {
if (config.ht.ready) {
auto h = BuildHtCapabilities(config.ht);
common::WriteHtCapabilities(w, h);
}
}
static void WriteHtOperation(BufferWriter* w, const BeaconConfig& config) {
if (config.ht.ready) {
HtOperation hto = BuildHtOperation(config.channel);
common::WriteHtOperation(w, hto);
}
}
static void WriteRsne(BufferWriter* w, const BeaconConfig& config) {
if (config.rsne != nullptr) { w->Write({config.rsne, config.rsne_len}); }
}
static void WriteMeshConfiguration(BufferWriter* w, const BeaconConfig& config) {
if (config.mesh_config != nullptr) { common::WriteMeshConfiguration(w, *config.mesh_config); }
}
static void WriteMeshId(BufferWriter* w, const BeaconConfig& config) {
if (config.mesh_id != nullptr) { common::WriteMeshId(w, {config.mesh_id, config.mesh_id_len}); }
}
static void WriteElements(BufferWriter* w, const BeaconConfig& config, size_t* rel_tim_ele_offset) {
RatesWriter rates_writer{config.rates};
// TODO(hahnr): Query from hardware which IEs must be filled out here.
WriteSsid(w, config);
rates_writer.WriteSupportedRates(w);
WriteDsssParamSet(w, config);
WriteTim(w, config.ps_cfg, rel_tim_ele_offset);
WriteCountry(w, config);
rates_writer.WriteExtendedSupportedRates(w);
WriteRsne(w, config);
WriteHtCapabilities(w, config);
WriteHtOperation(w, config);
WriteMeshId(w, config);
WriteMeshConfiguration(w, config);
}
template <typename T> static void SetBssType(T* bcn, BssType bss_type) {
// IEEE Std 802.11-2016, 9.4.1.4
switch (bss_type) {
case BssType::kInfrastructure:
bcn->cap.set_ess(1);
bcn->cap.set_ibss(0);
break;
case BssType::kIndependent:
bcn->cap.set_ess(0);
bcn->cap.set_ibss(1);
break;
case BssType::kMesh:
bcn->cap.set_ess(0);
bcn->cap.set_ibss(0);
break;
}
}
template <typename T>
static zx_status_t BuildBeaconOrProbeResponse(const BeaconConfig& config,
const common::MacAddr& recv_addr,
MgmtFrame<T>* buffer, size_t* tim_ele_offset) {
constexpr size_t reserved_ie_len = 256;
constexpr size_t max_frame_size =
MgmtFrameHeader::max_len() + Beacon::max_len() + reserved_ie_len;
auto packet = GetWlanPacket(max_frame_size);
if (packet == nullptr) { return ZX_ERR_NO_RESOURCES; }
BufferWriter w(*packet);
auto mgmt_hdr = w.Write<MgmtFrameHeader>();
mgmt_hdr->fc.set_type(FrameType::kManagement);
mgmt_hdr->fc.set_subtype(T::Subtype());
mgmt_hdr->addr1 = recv_addr;
mgmt_hdr->addr2 = config.bssid;
mgmt_hdr->addr3 = config.bssid;
auto bcn = w.Write<Beacon>();
bcn->beacon_interval = config.beacon_period;
bcn->timestamp = config.timestamp;
bcn->cap.set_privacy(config.rsne != nullptr);
SetBssType(bcn, config.bss_type);
bcn->cap.set_short_preamble(1);
// Write elements.
BufferWriter elem_w(w.RemainingBuffer());
size_t rel_tim_ele_offset = SIZE_MAX;
WriteElements(&elem_w, config, &rel_tim_ele_offset);
// Update packet's final length and tx_info.
packet->set_len(w.WrittenBytes() + elem_w.WrittenBytes());
if (tim_ele_offset != nullptr) {
if (rel_tim_ele_offset == SIZE_MAX) {
// A tim element offset was requested but no element was written
return ZX_ERR_INVALID_ARGS;
}
*tim_ele_offset = w.WrittenBytes() + rel_tim_ele_offset;
}
*buffer = MgmtFrame<T>(std::move(packet));
return ZX_OK;
}
zx_status_t BuildBeacon(const BeaconConfig& config, MgmtFrame<Beacon>* buffer,
size_t* tim_ele_offset) {
return BuildBeaconOrProbeResponse(config, common::kBcastMac, buffer, tim_ele_offset);
}
zx_status_t BuildProbeResponse(const BeaconConfig& config, const common::MacAddr& recv_addr,
MgmtFrame<ProbeResponse>* buffer) {
return BuildBeaconOrProbeResponse(config, recv_addr, buffer, nullptr);
}
} // namespace wlan