blob: 0a1a18ab5bfdfd88f55c21ec57e05c1296f9cf0b [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/client/bss.h>
#include <wlan/mlme/debug.h>
#include <wlan/mlme/parse_beacon.h>
#include <wlan/common/channel.h>
#include <wlan/common/element_splitter.h>
#include <wlan/common/parse_element.h>
#include <fuchsia/wlan/mlme/cpp/fidl.h>
#include <memory>
#include <string>
namespace wlan {
namespace wlan_mlme = ::fuchsia::wlan::mlme;
// TODO(NET-500): This file needs some clean-up.
zx_status_t Bss::ProcessBeacon(const Beacon& beacon, Span<const uint8_t> ie_chain,
const wlan_rx_info_t* rx_info) {
if (!IsBeaconValid(beacon)) { return ZX_ERR_INTERNAL; }
Renew(beacon, rx_info);
if (!HasBeaconChanged(beacon, ie_chain)) {
// If unchanged, it is sufficient to renew the BSS. Bail out.
return ZX_OK;
}
if (last_ie_chain_len_ != 0) {
// TODO(porce): Identify varying IE, and do IE-by-IE comparison.
// BSS had been discovered, but the beacon changed.
// Suspicious situation. Consider Deauth if in assoc.
debugbcn("BSSID %s beacon change detected. (len %zu -> %zu)\n", bssid_.ToString().c_str(),
last_ie_chain_len_, ie_chain.size());
}
auto status = Update(beacon, ie_chain);
if (status != ZX_OK) {
debugbcn("BSSID %s failed to update its BSS object: (%d)\n", bssid_.ToString().c_str(),
status);
return status;
}
return ZX_OK;
}
std::string Bss::ToString() const {
// TODO(porce): Convert to finspect Describe()
char buf[1024];
snprintf(
buf, sizeof(buf), "BSSID %s Infra %s RSSI %3d Country %3s Channel %4s SSID [%s]",
bssid_.ToString().c_str(),
bss_desc_.bss_type == wlan_mlme::BSSTypes::INFRASTRUCTURE ? "Y" : "N", bss_desc_.rssi_dbm,
bss_desc_.country.is_null() ? "---"
: reinterpret_cast<const char*>(bss_desc_.country->data()),
common::ChanStr(bcn_rx_chan_).c_str(), debug::ToAsciiOrHexStr(bss_desc_.ssid).c_str());
return std::string(buf);
}
bool Bss::IsBeaconValid(const Beacon& beacon) const {
// TODO(porce): Place holder. Add sanity logics.
if (bss_desc_.timestamp > beacon.timestamp) {
// Suspicious. Wrap around?
// TODO(porce): deauth if the client was in association.
}
// TODO(porce): Size check.
// Note: Some beacons in 5GHz may not include DSSS Parameter Set IE
return true;
}
void Bss::Renew(const Beacon& beacon, const wlan_rx_info_t* rx_info) {
bss_desc_.timestamp = beacon.timestamp;
// TODO(porce): Take a deep look. Which resolution do we want to track?
if (zx::clock::get(&ts_refreshed_) != ZX_OK) { ts_refreshed_ = zx::time_utc(); }
// Radio statistics.
if (rx_info == nullptr) return;
bcn_rx_chan_ = rx_info->chan;
// If the latest beacons lack measurements, keep the last report.
// TODO(NET-856): Change DDK wlan_rx_info_t and do translation in the vendor device driver.
// TODO(porce): Don't trust instantaneous values. Keep history.
bss_desc_.rssi_dbm = (rx_info->valid_fields & WLAN_RX_INFO_VALID_RSSI) ? rx_info->rssi_dbm
: WLAN_RSSI_DBM_INVALID;
bss_desc_.rcpi_dbmh = (rx_info->valid_fields & WLAN_RX_INFO_VALID_RCPI)
? rx_info->rcpi_dbmh
: WLAN_RCPI_DBMH_INVALID;
bss_desc_.rsni_dbh =
(rx_info->valid_fields & WLAN_RX_INFO_VALID_SNR) ? rx_info->snr_dbh : WLAN_RSNI_DBH_INVALID;
}
bool Bss::HasBeaconChanged(const Beacon& beacon, Span<const uint8_t> ie_chain) const {
// Test changes in beacon, except for the timestamp field.
if (last_ie_chain_len_ != ie_chain.size()) { return true; }
auto sig = GetBeaconSignature(beacon, ie_chain);
return sig != last_bcn_signature_;
}
zx_status_t Bss::Update(const Beacon& beacon, Span<const uint8_t> ie_chain) {
last_ie_chain_len_ = ie_chain.size();
last_bcn_signature_ = GetBeaconSignature(beacon, ie_chain);
// Fields that are always present.
bssid_.CopyTo(bss_desc_.bssid.mutable_data());
bss_desc_.beacon_period = beacon.beacon_interval; // name mismatch is spec-compliant.
bss_desc_.cap = beacon.cap.ToFidl();
bss_desc_.bss_type = GetBssType(beacon.cap);
ParseBeaconElements(ie_chain, bcn_rx_chan_.primary, &bss_desc_);
debugbcn("beacon BSSID %s Chan %u CBW %u Sec80 %u\n",
common::MacAddr(bss_desc_.bssid).ToString().c_str(), bss_desc_.chan.primary,
bss_desc_.chan.cbw, bss_desc_.chan.secondary80);
return ZX_OK;
}
BeaconHash Bss::GetBeaconSignature(const Beacon& beacon, Span<const uint8_t> ie_chain) const {
// Get a hash of the beacon except for its first field: timestamp.
// TODO(porce): Change to a less humble version.
BeaconHash hash = beacon.beacon_interval + beacon.cap.val();
for (auto b : ie_chain) {
hash += b;
}
return hash;
}
wlan_mlme::BSSTypes GetBssType(const CapabilityInfo& cap) {
// Note. This is in Beacon / Probe Response frames context.
// IEEE Std 802.11-2016, 9.4.1.4
if (cap.ess() == 0x1 && cap.ibss() == 0x0) {
return ::fuchsia::wlan::mlme::BSSTypes::INFRASTRUCTURE;
} else if (cap.ess() == 0x0 && cap.ibss() == 0x1) {
return ::fuchsia::wlan::mlme::BSSTypes::INDEPENDENT;
} else if (cap.ess() == 0x0 && cap.ibss() == 0x0) {
return ::fuchsia::wlan::mlme::BSSTypes::MESH;
} else {
// Undefined
return ::fuchsia::wlan::mlme::BSSTypes::ANY_BSS;
}
}
} // namespace wlan