| // 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/ap/beacon_sender.h> |
| |
| #include <wlan/common/channel.h> |
| #include <wlan/common/logging.h> |
| #include <wlan/mlme/ap/bss_interface.h> |
| #include <wlan/mlme/ap/tim.h> |
| #include <wlan/mlme/beacon.h> |
| #include <wlan/mlme/debug.h> |
| #include <wlan/mlme/mac_frame.h> |
| #include <wlan/mlme/packet.h> |
| #include <wlan/mlme/service.h> |
| |
| #include <fuchsia/wlan/mlme/cpp/fidl.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/status.h> |
| |
| namespace wlan { |
| |
| namespace wlan_mlme = ::fuchsia::wlan::mlme; |
| |
| BeaconSender::BeaconSender(DeviceInterface* device) : device_(device) {} |
| |
| BeaconSender::~BeaconSender() { |
| // Ensure Beaconing is stopped when the object is destroyed. |
| Stop(); |
| } |
| |
| void BeaconSender::Start(BssInterface* bss, const PsCfg& ps_cfg, |
| const MlmeMsg<wlan_mlme::StartRequest>& req) { |
| ZX_DEBUG_ASSERT(!IsStarted()); |
| |
| bss_ = bss; |
| req.body()->Clone(&req_); |
| |
| // Build the template. |
| wlan_bcn_config_t bcn_cfg; |
| MgmtFrame<Beacon> frame; |
| auto status = BuildBeacon(ps_cfg, &frame, &bcn_cfg.tim_ele_offset); |
| if (status != ZX_OK) { |
| errorf("[bcn-sender] [%s] could not build beacon template: %d\n", |
| bss_->bssid().ToString().c_str(), status); |
| return; |
| } |
| |
| // Copy template content. |
| auto packet = frame.Take(); |
| bcn_cfg.tmpl.packet_head.len = packet->len(); |
| bcn_cfg.tmpl.packet_head.data = packet->data(); |
| bcn_cfg.beacon_interval = req.body()->beacon_period; |
| status = device_->EnableBeaconing(&bcn_cfg); |
| if (status != ZX_OK) { |
| errorf("[bcn-sender] [%s] could not start beacon sending: %d\n", |
| bss_->bssid().ToString().c_str(), status); |
| return; |
| } |
| |
| debugbss("[bcn-sender] [%s] enabled Beacon sending\n", bss_->bssid().ToString().c_str()); |
| } |
| |
| void BeaconSender::Stop() { |
| if (!IsStarted()) { return; } |
| |
| auto status = device_->EnableBeaconing(nullptr); |
| if (status != ZX_OK) { |
| errorf("[bcn-sender] [%s] could not stop beacon sending: %d\n", |
| bss_->bssid().ToString().c_str(), status); |
| return; |
| } |
| |
| debugbss("[bcn-sender] [%s] disabled Beacon sending\n", bss_->bssid().ToString().c_str()); |
| bss_ = nullptr; |
| } |
| |
| bool BeaconSender::IsStarted() { |
| return bss_ != nullptr; |
| } |
| |
| bool BeaconSender::ShouldSendProbeResponse(const MgmtFrameView<ProbeRequest>& probe_req_frame) { |
| size_t elt_len = probe_req_frame.body_len() - probe_req_frame.hdr()->len(); |
| ElementReader reader(probe_req_frame.body()->elements, elt_len); |
| while (reader.is_valid()) { |
| auto hdr = reader.peek(); |
| if (hdr == nullptr) { |
| // Invalid header and thus corrupted request. |
| return false; |
| } |
| |
| switch (hdr->id) { |
| case element_id::kSsid: { |
| auto ie = reader.read<SsidElement>(); |
| if (ie == nullptr) { return false; }; |
| if (hdr->len == 0) { |
| // Always respond to wildcard requests. |
| return true; |
| } |
| |
| // Send ProbeResponse if request was targeted towards this BSS. |
| size_t ssid_len = req_.ssid->size(); |
| bool to_bss = |
| (hdr->len == ssid_len) && (memcmp(ie->ssid, req_.ssid->data(), ssid_len) == 0); |
| return to_bss; |
| } |
| default: |
| reader.skip(sizeof(ElementHeader) + hdr->len); |
| break; |
| } |
| } |
| |
| // Request did not contain an SSID IE and is therefore a wildcard one. |
| return true; |
| } |
| |
| zx_status_t BeaconSender::BuildBeacon(const PsCfg& ps_cfg, MgmtFrame<Beacon>* frame, |
| size_t* tim_ele_offset) { |
| BeaconConfig c = { |
| .bssid = bss_->bssid(), |
| .ssid = req_.ssid->data(), |
| .ssid_len = req_.ssid->size(), |
| .rsne = req_.rsne.is_null() ? nullptr : req_.rsne->data(), |
| .rsne_len = req_.rsne->size(), |
| .beacon_period = req_.beacon_period, |
| .channel = bss_->Chan(), // looks like we are ignoring 'channel' in 'req'. Is that correct? |
| .ps_cfg = &ps_cfg, |
| .ht = bss_->Ht(), |
| }; |
| c.rates = bss_->Rates(&c.rates_len); |
| return ::wlan::BuildBeacon(c, frame, tim_ele_offset); |
| } |
| |
| zx_status_t BeaconSender::UpdateBeacon(const PsCfg& ps_cfg) { |
| debugfn(); |
| ZX_DEBUG_ASSERT(IsStarted()); |
| if (!IsStarted()) { return ZX_ERR_BAD_STATE; } |
| |
| MgmtFrame<Beacon> frame; |
| size_t tim_ele_offset; |
| BuildBeacon(ps_cfg, &frame, &tim_ele_offset); |
| |
| zx_status_t status = device_->ConfigureBeacon(frame.Take()); |
| if (status != ZX_OK) { |
| errorf("[bcn-sender] [%s] could not send beacon packet: %d\n", |
| bss_->bssid().ToString().c_str(), status); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| void BeaconSender::SendProbeResponse(const MgmtFrameView<ProbeRequest>& probe_req_frame) { |
| if (!IsStarted()) { return; } |
| if (!ShouldSendProbeResponse(probe_req_frame)) { return; } |
| |
| BeaconConfig c = { |
| .bssid = bss_->bssid(), |
| .ssid = req_.ssid->data(), |
| .ssid_len = req_.ssid->size(), |
| .rsne = req_.rsne.is_null() ? nullptr : req_.rsne->data(), |
| .rsne_len = req_.rsne->size(), |
| .beacon_period = req_.beacon_period, |
| .channel = bss_->Chan(), |
| .ps_cfg = nullptr, // no TIM element in probe response |
| .ht = bss_->Ht(), |
| }; |
| c.rates = bss_->Rates(&c.rates_len); |
| |
| MgmtFrame<ProbeResponse> frame; |
| zx_status_t status = BuildProbeResponse(c, probe_req_frame.hdr()->addr2, &frame); |
| if (status != ZX_OK) { |
| errorf("could not build a probe response frame: %s\n", zx_status_get_string(status)); |
| return; |
| } |
| |
| frame.FillTxInfo(); |
| |
| status = device_->SendWlan(frame.Take()); |
| if (status != ZX_OK) { |
| errorf("[bcn-sender] [%s] could not send ProbeResponse packet: %d\n", |
| bss_->bssid().ToString().c_str(), status); |
| } |
| } |
| |
| } // namespace wlan |