diff --git a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp new file mode 100644 index 0000000000..268a58036c --- /dev/null +++ b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp @@ -0,0 +1,559 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include "ap_wlan_hal_nl80211.h" + +#include +#include +#include +#include +#include + +#include + +#include + +////////////////////////////////////////////////////////////////////////////// +////////////////////////// Local Module Definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +namespace bwl { +namespace nl80211 { + +#define BUFFER_SIZE 4096 +#define CSA_EVENT_FILTERING_TIMEOUT_MS 1000 + +// Allocate a char array wrapped in a shared_ptr +#define ALLOC_SMART_BUFFER(size) \ + std::shared_ptr(new char[size], [](char *obj) { \ + if (obj) \ + delete[] obj; \ + }) + +// Temporary storage for station capabilities +struct SRadioCapabilitiesStrings { + std::string supported_rates; + std::string ht_cap; + std::string ht_mcs; + std::string vht_cap; + std::string vht_mcs; + std::string btm_supported; + std::string nr_enabled; + std::string non_pref_chan; + std::string cell_capa; +}; + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////// Local Module Functions /////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static ap_wlan_hal::Event nl80211_to_bwl_event(const std::string &opcode) +{ + if (opcode == "AP-ENABLED") { + return ap_wlan_hal::Event::AP_Enabled; + } else if (opcode == "AP-DISABLED") { + return ap_wlan_hal::Event::AP_Disabled; + } else if (opcode == "AP-STA-CONNECTED") { + return ap_wlan_hal::Event::STA_Connected; + } else if (opcode == "AP-STA-DISCONNECTED") { + return ap_wlan_hal::Event::STA_Disconnected; + } else if (opcode == "INTERFACE-ENABLED") { + return ap_wlan_hal::Event::Interface_Enabled; + } else if (opcode == "INTERFACE-DISABLED") { + return ap_wlan_hal::Event::Interface_Disabled; + } else if (opcode == "ACS-STARTED") { + return ap_wlan_hal::Event::ACS_Started; + } else if (opcode == "ACS-COMPLETED") { + return ap_wlan_hal::Event::ACS_Completed; + } else if (opcode == "ACS-FAILED") { + return ap_wlan_hal::Event::ACS_Failed; + } else if (opcode == "AP-CSA-FINISHED") { + return ap_wlan_hal::Event::CSA_Finished; + } else if (opcode == "BSS-TM-RESP") { + return ap_wlan_hal::Event::BSS_TM_Response; + } else if (opcode == "DFS-CAC-COMPLETED") { + return ap_wlan_hal::Event::DFS_CAC_Completed; + } else if (opcode == "DFS-NOP-FINISHED") { + return ap_wlan_hal::Event::DFS_NOP_Finished; + } + + return ap_wlan_hal::Event::Invalid; +} + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Implementation /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// NOTE: Since *base_wlan_hal_nl80211* inherits *base_wlan_hal* virtually, we +// need to explicitly call it's from any deriving class +ap_wlan_hal_nl80211::ap_wlan_hal_nl80211(std::string iface_name, hal_event_cb_t callback, + hal_conf_t hal_conf) + : base_wlan_hal(bwl::HALType::AccessPoint, iface_name, IfaceType::Intel, callback, hal_conf), + base_wlan_hal_nl80211(bwl::HALType::AccessPoint, iface_name, callback, BUFFER_SIZE) +{ +} + +ap_wlan_hal_nl80211::~ap_wlan_hal_nl80211() {} + +HALState ap_wlan_hal_nl80211::attach(bool block) +{ + auto state = base_wlan_hal_nl80211::attach(block); + + // On Operational send the AP_Attached event to the AP Manager + if (state == HALState::Operational) { + event_queue_push(Event::AP_Attached); + } + + return state; +} + +bool ap_wlan_hal_nl80211::enable() +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::disable() +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::set_start_disabled(bool enable, int vap_id) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::set_channel(int chan, int bw, int center_channel) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::sta_allow(const std::string &mac) +{ + LOG(TRACE) << __func__ << " mac: " << mac; + + // Build command string + // We use the DENY_ACL list only + const std::string cmd = "DENY_ACL DEL_MAC " + mac; + + // Send command + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "sta_allow() failed!"; + return false; + } + + return true; +} + +bool ap_wlan_hal_nl80211::sta_deny(const std::string &mac, int reject_sta) +{ + LOG(TRACE) << __func__ << " mac: " << mac; + + // Build command string + // We use the DENY_ACL list only + const std::string cmd = "DENY_ACL ADD_MAC " + mac; + + // Send command + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "sta_deny() failed!"; + return false; + } + + return true; +} + +bool ap_wlan_hal_nl80211::sta_disassoc(int8_t vap_id, const std::string &mac, uint32_t reason) +{ + LOG(TRACE) << __func__ << " mac: " << mac; + + // Build command string + const std::string cmd = "DISASSOCIATE " + mac + " reason=" + std::to_string(reason) + " tx=0"; + + // Send command + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "sta_disassoc() failed!"; + return false; + } + + return true; +} + +bool ap_wlan_hal_nl80211::sta_deauth(int8_t vap_id, const std::string &mac, uint32_t reason) +{ + LOG(TRACE) << __func__ << " mac: " << mac; + + // Build command string + const std::string cmd = "DEAUTHENTICATE " + mac + " reason=" + std::to_string(reason) + " tx=0"; + + // Send command + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "sta_disassoc() failed!"; + return false; + } + + return true; +} + +bool ap_wlan_hal_nl80211::sta_bss_steer(const std::string &mac, const std::string &bssid, int chan, + int disassoc_timer, int valid_int) +{ + LOG(TRACE) << __func__ << " mac: " << mac << ", BSS: " << bssid << ", channel: " << chan + << ", disassoc: " << disassoc_timer << ", valid_int: " << valid_int; + + // Build command string + std::string cmd = + // Set the STA MAC address + "BSS_TM_REQ " + + mac + // Transition management parameters + + " pref=" + "1" + " abridged=" + "1" + + // Target BSSID + " neighbor=" + bssid + ",0,0," + std::to_string(chan) + ",0"; + + if (disassoc_timer) { + cmd += std::string() + " disassoc_imminent=" + "1" + + " disassoc_timer=" + std::to_string(disassoc_timer); + } + // " bss_term=" // Unused Param + // " url=" // Unused Param + // " mbo=" // Unused Param + + if (valid_int) { + cmd += " valid_int=" + std::to_string(valid_int); + } + + // Send command + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "sta_bss_steer() failed!"; + return false; + } + + return true; +} + +bool ap_wlan_hal_nl80211::sta_unassoc_rssi_measurement(const std::string &mac, int chan, int bw, + int vht_center_frequency, int delay, + int window_size) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return false; +} + +bool ap_wlan_hal_nl80211::sta_softblock_add( + const std::string &vap_name, const std::string &client_mac, uint8_t reject_error_code, + uint8_t probe_snr_threshold_hi, uint8_t probe_snr_threshold_lo, + uint8_t authetication_snr_threshold_hi, uint8_t authetication_snr_threshold_lo) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return false; +} + +bool ap_wlan_hal_nl80211::sta_softblock_remove(const std::string &vap_name, + const std::string &client_mac) +{ + return false; +} + +bool ap_wlan_hal_nl80211::switch_channel(int chan, int bw, int vht_center_frequency) +{ + LOG(TRACE) << __func__ << " channel: " << chan << ", bw: " << bw + << ", vht_center_frequency: " << vht_center_frequency; + + // TODO: IMPLEMENT! + + LOG(DEBUG) << "Got channel switch, simulate ACS-STARTED;ACS-COMPLETED"; + event_queue_push(Event::ACS_Started); + event_queue_push(Event::ACS_Completed); + event_queue_push(Event::CSA_Finished); + + return true; +} + +bool ap_wlan_hal_nl80211::set_antenna_mode(AntMode mode) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::wds_set_mode(WDSMode mode) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::wds_add_sta(const std::string &mac) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::wds_del_sta(const std::string &mac) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::wds_clear_list() +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::failsafe_channel_set(int chan, int bw, int vht_center_frequency) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::failsafe_channel_get(int &chan, int &bw) +{ + LOG(TRACE) << __func__; + + // Build command string + std::string cmd = "GET_FAILSAFE_CHAN"; + char *reply; + + // Send command + if (!wpa_ctrl_send_msg(cmd, &reply)) { + LOG(ERROR) << "failsafe_channel_get() failed!"; + return false; + } + + // Custom reply parsing + + std::string reply_str(reply); + if (reply_str.find("UNSPECIFIED", 0, 11) != std::string::npos) { + chan = -1; + bw = -1; + } else if (reply_str.find("ACS", 0, 3) != std::string::npos) { + chan = bw = 0; + + } else { + int freq; + std::string tmp; + std::stringstream ss(reply_str); + // parsing string in form: "%d %*s %*s bandwidth=%d" + ss >> freq >> tmp >> tmp >> tmp; + auto tmp_vec = beerocks::string_utils::str_split(tmp, '='); + if (tmp_vec.size() != 2 || tmp_vec[0] != std::string("bandwidth")) { + return false; + } + bw = beerocks::string_utils::stoi(tmp_vec[1]); + chan = beerocks::utils::wifi_freq_to_channel(freq); + } + + return true; +} + +bool ap_wlan_hal_nl80211::restricted_channels_set(char *channel_list) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::restricted_channels_get(char *channel_list) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + memset(channel_list, 0, beerocks::message::RESTRICTED_CHANNEL_LENGTH); + return true; +} + +bool ap_wlan_hal_nl80211::read_acs_report() +{ + LOG(TRACE) << __func__ << " for interface: " << get_radio_info().iface_name; + + // TODO: Get actual list of supported channels + // See: https://github.com/prplfoundation/prplMesh/issues/457 + + uint8_t idx = 0; + if (m_radio_info.is_5ghz == false) { + // 2.4G simulated report + for (uint16_t ch = 1; ch <= 11; ch++) { + m_radio_info.supported_channels[idx].channel = ch; + m_radio_info.supported_channels[idx].bandwidth = 20; + m_radio_info.supported_channels[idx].bss_overlap = 10; + m_radio_info.supported_channels[idx].is_dfs = 0; + idx++; + } + } else { + // 5G simulated report + for (uint16_t ch = 36; ch <= 64; ch += 4) { + for (uint16_t step = 0; step < 3; step++) { + m_radio_info.supported_channels[idx].channel = ch; + m_radio_info.supported_channels[idx].bandwidth = 20 + step * 20; + m_radio_info.supported_channels[idx].bss_overlap = 10 + step * 10; + m_radio_info.supported_channels[idx].is_dfs = (ch > 48) ? 1 : 0; + idx++; + } + } + for (uint16_t ch = 100; ch <= 144; ch += 4) { + for (uint16_t step = 0; step < 3; step++) { + m_radio_info.supported_channels[idx].channel = ch; + m_radio_info.supported_channels[idx].bandwidth = 20 + step * 20; + m_radio_info.supported_channels[idx].bss_overlap = 10 + step * 10; + m_radio_info.supported_channels[idx].is_dfs = 1; + idx++; + } + } + for (uint16_t ch = 149; ch <= 165; ch += 4) { + for (uint16_t step = 0; step < 3; step++) { + m_radio_info.supported_channels[idx].channel = ch; + m_radio_info.supported_channels[idx].bandwidth = 20 + step * 20; + m_radio_info.supported_channels[idx].bss_overlap = 10 + step * 10; + m_radio_info.supported_channels[idx].is_dfs = 0; + idx++; + } + } + } + + return true; +} + +bool ap_wlan_hal_nl80211::read_supported_channels() +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::set_vap_enable(const std::string &iface_name, const bool enable) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::get_vap_enable(const std::string &iface_name, bool &enable) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool ap_wlan_hal_nl80211::generate_connected_clients_events() +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +std::string ap_wlan_hal_nl80211::get_radio_driver_version() { return "nl80211"; } + +bool ap_wlan_hal_nl80211::process_nl80211_event(parsed_obj_map_t &parsed_obj) +{ + // Filter out empty events + std::string opcode; + if (!(parsed_obj.find("_opcode") != parsed_obj.end() && + !(opcode = parsed_obj["_opcode"]).empty())) { + return true; + } + + // LOG(TRACE) << __func__ << " - opcode: |" << opcode << "|"; + + auto event = nl80211_to_bwl_event(opcode); + + switch (event) { + + // STA Connected + case Event::STA_Connected: { + + // TODO: Change to HAL objects + auto msg_buff = + ALLOC_SMART_BUFFER(sizeof(sACTION_APMANAGER_CLIENT_ASSOCIATED_NOTIFICATION)); + auto msg = + reinterpret_cast(msg_buff.get()); + LOG_IF(!msg, FATAL) << "Memory allocation failed!"; + + // Initialize the message + memset(msg_buff.get(), 0, sizeof(sACTION_APMANAGER_CLIENT_ASSOCIATED_NOTIFICATION)); + + msg->params.vap_id = 0; + msg->params.mac = beerocks::net::network_utils::mac_from_string(parsed_obj["_mac"]); + + // Add the message to the queue + event_queue_push(Event::STA_Connected, msg_buff); + + } break; + + // STA Disconnected + case Event::STA_Disconnected: { + + // TODO: Change to HAL objects + auto msg_buff = + ALLOC_SMART_BUFFER(sizeof(sACTION_APMANAGER_CLIENT_DISCONNECTED_NOTIFICATION)); + auto msg = + reinterpret_cast(msg_buff.get()); + LOG_IF(!msg, FATAL) << "Memory allocation failed!"; + + // Initialize the message + memset(msg_buff.get(), 0, sizeof(sACTION_APMANAGER_CLIENT_DISCONNECTED_NOTIFICATION)); + + // Store the MAC address of the disconnected STA + msg->params.vap_id = 0; + msg->params.mac = beerocks::net::network_utils::mac_from_string(parsed_obj["_mac"]); + + // Add the message to the queue + event_queue_push(Event::STA_Disconnected, msg_buff); + + } break; + + // BSS Transition (802.11v) + case Event::BSS_TM_Response: { + + // TODO: Change to HAL objects + auto msg_buff = ALLOC_SMART_BUFFER(sizeof(sACTION_APMANAGER_CLIENT_BSS_STEER_RESPONSE)); + auto msg = reinterpret_cast(msg_buff.get()); + LOG_IF(!msg, FATAL) << "Memory allocation failed!"; + + // Initialize the message + memset(msg_buff.get(), 0, sizeof(sACTION_APMANAGER_CLIENT_BSS_STEER_RESPONSE)); + + // Client params + msg->params.mac = beerocks::net::network_utils::mac_from_string(parsed_obj["_mac"]); + msg->params.status_code = beerocks::string_utils::stoi(parsed_obj["status_code"]); + + // Add the message to the queue + event_queue_push(Event::BSS_TM_Response, msg_buff); + + } break; + + // ACS/CSA Completed + case Event::ACS_Completed: + case Event::CSA_Finished: + case Event::Interface_Disabled: + case Event::ACS_Failed: { + // Forward to the AP manager + event_queue_push(event); + } break; + + // Gracefully ignore unhandled events + // TODO: Probably should be changed to an error once WAV will stop + // sending empty or irrelevant events... + default: { + LOG(WARNING) << "Unhandled event received: " << opcode; + return true; + + } break; + } + + return true; +} + +} // namespace nl80211 +} // namespace bwl + +// AP WAV HAL Factory Functions +extern "C" { + +bwl::ap_wlan_hal *ap_wlan_hal_create(std::string iface_name, bwl::hal_conf_t hal_conf, + bwl::base_wlan_hal::hal_event_cb_t callback) +{ + return new bwl::nl80211::ap_wlan_hal_nl80211(iface_name, callback, hal_conf); +} + +void ap_wlan_hal_destroy(bwl::ap_wlan_hal *obj) { delete obj; } +} diff --git a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.h b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.h new file mode 100644 index 0000000000..38cb90cdf0 --- /dev/null +++ b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#ifndef _BWL_AP_WLAN_HAL_NL80211_H_ +#define _BWL_AP_WLAN_HAL_NL80211_H_ + +#include "../common/ap_wlan_hal.h" +#include "base_wlan_hal_nl80211.h" + +namespace bwl { +namespace nl80211 { + +/*! + * Hardware abstraction layer for WLAN Access Point. + */ +class ap_wlan_hal_nl80211 : public base_wlan_hal_nl80211, public ap_wlan_hal { + + // Public methods +public: + /*! + * Constructor. + * + * @param [in] iface_name AP interface name. + * @param [in] callback Callback for handling internal events. + */ + ap_wlan_hal_nl80211(std::string iface_name, hal_event_cb_t callback, hal_conf_t hal_conf); + + virtual ~ap_wlan_hal_nl80211(); + + virtual HALState attach(bool block = false) override; + virtual bool enable() override; + virtual bool disable() override; + virtual bool set_start_disabled(bool enable, int vap_id = beerocks::IFACE_RADIO_ID) override; + virtual bool set_channel(int chan, int bw, int center_channel) override; + virtual bool sta_allow(const std::string &mac) override; + virtual bool sta_deny(const std::string &mac, int reject_sta) override; + virtual bool sta_disassoc(int8_t vap_id, const std::string &mac, uint32_t reason = 0) override; + virtual bool sta_deauth(int8_t vap_id, const std::string &mac, uint32_t reason = 0) override; + virtual bool sta_bss_steer(const std::string &mac, const std::string &bssid, int chan, + int disassoc_timer, int valid_int) override; + virtual bool sta_unassoc_rssi_measurement(const std::string &mac, int chan, int bw, + int vht_center_frequency, int delay, + int window_size) override; + virtual bool sta_softblock_add(const std::string &vap_name, const std::string &client_mac, + uint8_t reject_error_code, uint8_t probe_snr_threshold_hi, + uint8_t probe_snr_threshold_lo, + uint8_t authetication_snr_threshold_hi, + uint8_t authetication_snr_threshold_lo) override; + virtual bool sta_softblock_remove(const std::string &vap_name, + const std::string &client_mac) override; + virtual bool switch_channel(int chan, int bw, int vht_center_frequency) override; + virtual bool set_antenna_mode(AntMode mode) override; + virtual bool wds_set_mode(WDSMode mode) override; + virtual bool wds_add_sta(const std::string &mac) override; + virtual bool wds_del_sta(const std::string &mac) override; + virtual bool wds_clear_list() override; + virtual bool failsafe_channel_set(int chan, int bw, int vht_center_frequency) override; + virtual bool failsafe_channel_get(int &chan, int &bw) override; + virtual bool restricted_channels_set(char *channel_list) override; + virtual bool restricted_channels_get(char *channel_list) override; + virtual bool read_acs_report() override; + virtual bool read_supported_channels() override; + virtual std::string get_radio_driver_version() override; + virtual bool set_vap_enable(const std::string &iface_name, const bool enable) override; + virtual bool get_vap_enable(const std::string &iface_name, bool &enable) override; + virtual bool generate_connected_clients_events() override; + + // Protected methods: +protected: + virtual bool process_nl80211_event(parsed_obj_map_t &parsed_obj) override; + + // Overload for AP events + bool event_queue_push(ap_wlan_hal::Event event, std::shared_ptr data = {}) + { + return base_wlan_hal::event_queue_push(int(event), data); + } + +private: + // Unassociated measurement state variables + std::chrono::steady_clock::time_point m_unassoc_measure_start; + int m_unassoc_measure_window_size = 0; + int m_unassoc_measure_delay = 0; + + bool m_drop_csa = false; + std::chrono::steady_clock::time_point m_csa_event_filtering_timestamp; +}; + +} // namespace nl80211 +} // namespace bwl + +#endif // _BWL_AP_WLAN_HAL_NL80211_H_ diff --git a/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.cpp b/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.cpp new file mode 100644 index 0000000000..5a18f3c59a --- /dev/null +++ b/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.cpp @@ -0,0 +1,947 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include "base_wlan_hal_nl80211.h" + +#include +#include +#include +#include + +#include + +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#include +#include + +namespace bwl { +namespace nl80211 { + +////////////////////////////////////////////////////////////////////////////// +///////////////////////// Local Module Definitions /////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#define BASE_CTRL_PATH "/var/run/" +#define AP_ENABELED_TIMEOUT_SEC 15 +#define AP_ENABELED_FIXED_DFS_TIMEOUT_SEC 660 + +////////////////////////////////////////////////////////////////////////////// +////////////////////////// Local Module Functions //////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +std::ostream &operator<<(std::ostream &out, const nl80211_fsm_state &value) +{ + switch (value) { + case nl80211_fsm_state::Delay: + out << "Delay"; + break; + case nl80211_fsm_state::Init: + out << "Init"; + break; + case nl80211_fsm_state::GetRadioInfo: + out << "GetRadioInfo"; + break; + case nl80211_fsm_state::Attach: + out << "Attach"; + break; + case nl80211_fsm_state::Operational: + out << "Operational"; + break; + case nl80211_fsm_state::Detach: + out << "Detach"; + break; + } + return out; +} + +std::ostream &operator<<(std::ostream &out, const nl80211_fsm_event &value) +{ + switch (value) { + case nl80211_fsm_event::Attach: + out << "Attach"; + break; + case nl80211_fsm_event::Detach: + out << "Detach"; + break; + } + return out; +} + +static void map_obj_parser(std::stringstream &ss_in, std::list delimiter_list, + base_wlan_hal_nl80211::parsed_obj_map_t &map_obj) +{ + if (delimiter_list.empty()) + return; + + std::string str_storage, key; + bool kv = true; // '1'=key, '0'=val; + while (std::getline(ss_in, str_storage, delimiter_list.front())) { + if (delimiter_list.size() == 1) { + if (kv) { + key = str_storage; // save key + } else { + map_obj[key] = str_storage; // save val + } + kv = !kv; + + } else { + auto delimiter_list_out(delimiter_list); + delimiter_list_out.erase(delimiter_list_out.begin()); // delete first delimiter + std::stringstream ss_out(str_storage); + map_obj_parser(ss_out, delimiter_list_out, map_obj); + } + } +} + +static void listed_map_obj_parser(std::stringstream &ss_in, std::list delimiter_list, + base_wlan_hal_nl80211::parsed_obj_listed_map_t &listed_map_obj) +{ + if (delimiter_list.empty()) + return; + + std::string str_storage; + while (std::getline(ss_in, str_storage, delimiter_list.front())) { + auto delimiter_list_out(delimiter_list); + delimiter_list_out.erase(delimiter_list_out.begin()); // delete first delimiter + std::stringstream ss_out(str_storage); + base_wlan_hal_nl80211::parsed_obj_map_t map_obj; + map_obj_parser(ss_out, delimiter_list_out, map_obj); + listed_map_obj.push_back(map_obj); + } +} + +static void map_event_obj_parser(std::string event_str, + base_wlan_hal_nl80211::parsed_obj_map_t &map_obj) +{ + // eliminate event log level from the begining of the event string : "<3>" + auto idx_start = event_str.find_first_of(">"); + if (idx_start != std::string::npos) { + idx_start++; + } else { + LOG(WARNING) << "empty event! event_string: " << event_str; + } + + // Parse all the args + std::stringstream ss(event_str.c_str() + idx_start); + std::string str_storage; + bool opcode = false; + bool mac = false; + int arg = 0; + + while (std::getline(ss, str_storage, ' ')) { + auto idx = str_storage.find_first_of('=', idx_start); + if (idx == std::string::npos) { + if (!opcode) { + map_obj["_opcode"] = str_storage; + opcode = true; + } else if (!mac && beerocks::net::network_utils::is_valid_mac(str_storage)) { + map_obj["_mac"] = str_storage; + mac = true; + } else { + map_obj["_arg" + std::to_string(arg++)] = str_storage; + } + } else { + map_obj[str_storage.substr(0, idx)] = str_storage.substr(idx + 1, std::string::npos); + } + } +} + +#if 0 + +static void parsed_obj_debug(base_wlan_hal_nl80211::parsed_obj_map_t &obj) +{ + LOG(TRACE) << "parsed_obj_debug:"; + std::stringstream ss_obj; + ss_obj << std::endl << "parsed_obj_debug: " << std::endl; + for (auto element : obj) { + LOG(TRACE) << "key: " << element.first << ", value: " << element.second; + ss_obj << "key: " << element.first << ", value: " << element.second << std::endl; + } + + LOG(DEBUG) << ss_obj.str(); +} + +static void parsed_obj_debug(base_wlan_hal_nl80211::parsed_obj_listed_map_t &obj) +{ + LOG(TRACE) << "parsed_obj_debug:"; + std::stringstream ss_obj; + ss_obj << std::endl << "parsed_obj_debug: " << std::endl; + int element_num = 0; + for (auto list_element : obj) { + LOG(TRACE) << "vector element: " << element_num; + ss_obj << "vector element: " << element_num << std::endl; + for (auto map_element : list_element) { + LOG(TRACE) << "key: " << map_element.first << ", value: " << map_element.second; + ss_obj << "key: " << map_element.first << ", value: " << map_element.second + << std::endl; + } + element_num++; + } + LOG(DEBUG) << ss_obj.str(); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Implementation /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +base_wlan_hal_nl80211::base_wlan_hal_nl80211(HALType type, std::string iface_name, + hal_event_cb_t callback, int wpa_ctrl_buffer_size, + hal_conf_t hal_conf) + : base_wlan_hal(type, iface_name, IfaceType::Intel, callback, hal_conf), + beerocks::beerocks_fsm(nl80211_fsm_state::Delay), + m_wpa_ctrl_buffer_size(wpa_ctrl_buffer_size) +{ + // Allocate wpa_ctrl buffer + if (m_wpa_ctrl_buffer_size) { + m_wpa_ctrl_buffer = std::shared_ptr(new char[m_wpa_ctrl_buffer_size], [](char *obj) { + if (obj) { + delete[] obj; + } + }); + } + + m_wpa_ctrl_path = BASE_CTRL_PATH; + if (get_type() == HALType::AccessPoint || get_type() == HALType::Monitor) { + m_wpa_ctrl_path += "hostapd/"; + } else if (get_type() == HALType::Station) { + m_wpa_ctrl_path += "wpa_supplicant/"; + } else { + LOG(ERROR) << "Unsupported HAL Type: " << int(get_type()); + return; // HACK TODO what should we do in that case? + } + + m_wpa_ctrl_path += m_radio_info.iface_name; + + // Initialize the FSM + fsm_setup(); +} + +base_wlan_hal_nl80211::~base_wlan_hal_nl80211() { detach(); } + +bool base_wlan_hal_nl80211::fsm_setup() +{ + config() + + // Setup states: + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////// State: Delay ////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::Delay) + + // On Entry + .entry([&](const void *args) -> bool { + m_state_timeout = std::chrono::steady_clock::now() + std::chrono::seconds(5); + return true; + }) + + // EVENT -> Attach + .on(nl80211_fsm_event::Attach, nl80211_fsm_state::Init, + [&](TTransition &transition, const void *args) -> bool { + return (std::chrono::steady_clock::now() >= m_state_timeout); + }) + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////// State: Init /////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::Init) + + .entry([&](const void *args) -> bool { + m_state_timeout = std::chrono::steady_clock::now() + std::chrono::seconds(200); + return true; + }) + + // Handle "Detach" event + .on(nl80211_fsm_event::Detach, nl80211_fsm_state::Detach) + + // Handle "Attach" event + .on(nl80211_fsm_event::Attach, + {nl80211_fsm_state::Attach, nl80211_fsm_state::Detach, nl80211_fsm_state::GetRadioInfo}, + [&](TTransition &transition, const void *args) -> bool { + // Allocate and connect the netlink socket + if (!m_nl80211_sock) { + if (!(m_nl80211_sock = std::shared_ptr( + nl_socket_alloc(), [](struct nl_sock *obj) { + if (obj) { + nl_socket_free(obj); + } + }))) { + LOG(ERROR) << "Failed allocating netlink socket!"; + return false; + } + + // Increase the socket's internal buffer size + nl_socket_set_buffer_size(m_nl80211_sock.get(), 8192, 8192); + + // Connect the socket + if (genl_connect(m_nl80211_sock.get()) != 0) { + LOG(ERROR) << "Failed connecting netlink socket!"; + m_nl80211_sock.reset(); + return false; + } + + // Resolve the generic nl80211 id + if ((m_nl80211_id = genl_ctrl_resolve(m_nl80211_sock.get(), "nl80211")) < 0) { + LOG(ERROR) << "nl80211 not found!"; + return false; + } + + // Store the current interface index + if ((m_iface_index = if_nametoindex(get_iface_name().c_str())) == 0) { + LOG(ERROR) << "Failed reading the index of interface " << get_iface_name() + << ": " << strerror(errno); + + return false; + } + } + + // Open a control interface to wpa_supplicant/hostapd. + if ((m_wpa_ctrl_cmd = wpa_ctrl_open(m_wpa_ctrl_path.c_str()))) { + if (get_type() != HALType::Station) { + transition.change_destination(nl80211_fsm_state::GetRadioInfo); + } + return true; + } else { + LOG(DEBUG) << "wpa_ctrl_open() failed, ctrl_iface_path: " << m_wpa_ctrl_path; + } + + // False if timeout not reached yet, and True otherwise (switch state) + if (std::chrono::steady_clock::now() >= m_state_timeout) { + LOG(ERROR) << "Failed attaching to the hostapd control interface of " + << m_radio_info.iface_name; + + return (transition.change_destination(nl80211_fsm_state::Detach)); + } + + // Stay in the current state + return false; + }) + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////// State: GetRadioInfo /////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::GetRadioInfo) + + .entry([&](const void *args) -> bool { + m_state_timeout = + std::chrono::steady_clock::now() + std::chrono::seconds(AP_ENABELED_TIMEOUT_SEC); + return true; + }) + + // Handle "Detach" event + .on(nl80211_fsm_event::Detach, nl80211_fsm_state::Detach) + + .on(nl80211_fsm_event::Attach, {nl80211_fsm_state::Attach, nl80211_fsm_state::Detach}, + [&](TTransition &transition, const void *args) -> bool { + // Attempt to read radio info + if (!refresh_radio_info()) { + return (transition.change_destination(nl80211_fsm_state::Detach)); + } + + // Move to the next state + return true; + + // if (error || (std::chrono::steady_clock::now() >= m_state_timeout)) { + // return (transition.change_destination(nl80211_fsm_state::Detach)); + // } + + // // Remain in the current state + // return false; + }) + + ////////////////////////////////////////////////////////////////////////// + ///////////////////////////// State: Attach ////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::Attach) + + // Handle "Detach" event + .on(nl80211_fsm_event::Detach, nl80211_fsm_state::Detach) + + .on(nl80211_fsm_event::Attach, {nl80211_fsm_state::Operational, nl80211_fsm_state::Delay}, + [&](TTransition &transition, const void *args) -> bool { + // Open a event interface to wpa_supplicant/hostapd. + if (!(m_wpa_ctrl_event = wpa_ctrl_open(m_wpa_ctrl_path.c_str()))) { + LOG(DEBUG) << "wpa_ctrl_open() failed, ctrl_iface_path: " << m_wpa_ctrl_path; + return (transition.change_destination(nl80211_fsm_state::Delay)); + } + + // Get the wpa_supplicant/hostapd event interface file descriptor + m_fd_ext_events = wpa_ctrl_get_fd((wpa_ctrl *)m_wpa_ctrl_event); + + // Attach to the control interface for events receiving + int result; + int try_cnt = 0; + do { + result = wpa_ctrl_attach( + m_wpa_ctrl_event); // return values: 0 on success, -1 on failure, -2 on timeout + } while (result == -2 && ++try_cnt < 3); + + if (result < 0) { + // Return with error + LOG(ERROR) << "Failed attaching to control interface of " + << m_radio_info.iface_name; + return (transition.change_destination(nl80211_fsm_state::Delay)); + } + + // Success + LOG(DEBUG) + << "Open and attach an event interface to wpa_supplicant/hostapd - SUCCESS!"; + return true; + }) + + ////////////////////////////////////////////////////////////////////////// + /////////////////////////// State: Operational /////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::Operational) + + // Handle "Detach" event + .on(nl80211_fsm_event::Detach, nl80211_fsm_state::Detach) + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////// State: Detach ///////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + .state(nl80211_fsm_state::Detach) + + .entry([&](const void *args) -> bool { + LOG(DEBUG) << "nl80211_attach_fsm - Detaching..."; + + // Detach from the WPA control interface + if (m_wpa_ctrl_event) { + int result; + int try_cnt = 0; + do { + result = wpa_ctrl_detach( + m_wpa_ctrl_event); // return values: 0 on success, -1 on failure, -2 on timeout + } while (result == -2 && ++try_cnt < 3); + + if (result < 0) { + LOG(WARNING) << "can't detach wpa_ctrl_event"; + } + } + + // Release the nl80211 socket + if (m_nl80211_sock) { + m_nl80211_sock.reset(); + m_nl80211_id = 0; + } + + // Close the events control interface + wpa_ctrl_close(m_wpa_ctrl_event); + wpa_ctrl_close(m_wpa_ctrl_cmd); + + m_wpa_ctrl_event = nullptr; + m_wpa_ctrl_cmd = nullptr; + + m_fd_ext_events = -1; + + return true; + }) + + // Handle "Attach" event + .on(nl80211_fsm_event::Attach, nl80211_fsm_state::Delay); + + // Start the FSM + return (start()); +} + +HALState base_wlan_hal_nl80211::attach(bool block) +{ + while (true) { + + fire(nl80211_fsm_event::Attach); + auto attach_state = state(); + if (m_last_attach_state != attach_state) { + LOG(DEBUG) << "NL80211 FSM " << m_radio_info.iface_name + << " Attach State: " << attach_state; + m_last_attach_state = attach_state; + } + + switch (attach_state) { + // Initializing + case nl80211_fsm_state::Delay: + case nl80211_fsm_state::Init: + case nl80211_fsm_state::GetRadioInfo: + case nl80211_fsm_state::Attach: { + if (block) { + // TODO: Delay? + continue; + } else { + return (m_hal_state = HALState::Initializing); + } + } + + // Initialization completed + case nl80211_fsm_state::Operational: { + return (m_hal_state = HALState::Operational); + } + + // Initialization failed + case nl80211_fsm_state::Detach: { + return (m_hal_state = HALState::Failed); + } + + // Invalid state + default: { + LOG(ERROR) << "Invalid NL80211 Attach State: " << int(attach_state); + } + } + }; +} + +bool base_wlan_hal_nl80211::detach() +{ + fire(nl80211_fsm_event::Detach); + return (state() == nl80211_fsm_state::Detach); +} + +bool base_wlan_hal_nl80211::set(const std::string ¶m, const std::string &value, int vap_id) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool base_wlan_hal_nl80211::ping() +{ + parsed_obj_map_t reply; + + if (!wpa_ctrl_send_msg("PING", reply)) { + return false; + } + + return true; +} + +bool base_wlan_hal_nl80211::wpa_ctrl_send_msg(const std::string &cmd) +{ + int result; + int try_cnt = 0; + + if (!m_wpa_ctrl_cmd) { + LOG(ERROR) << "Control socket not available!"; + return false; + } + + auto buffer = m_wpa_ctrl_buffer.get(); + + auto buff_size_copy = m_wpa_ctrl_buffer_size; + do { + result = wpa_ctrl_request(m_wpa_ctrl_cmd, cmd.c_str(), cmd.size(), buffer, &buff_size_copy, + NULL); + } while (result == -2 && ++try_cnt < 3); + + if (result < 0) { + LOG(ERROR) << "can't send wpa_ctrl_request"; + LOG(ERROR) << "failed cmd: " << cmd; + return false; + } + + if (buff_size_copy >= m_wpa_ctrl_buffer_size) { + LOG(ERROR) << "wpa_ctrl_request returned reply of size " << buff_size_copy; + return false; + } + + buffer[buff_size_copy] = + 0; // the wpa_ctrl does not put null terminator at the and of the string + + if ((!strncmp(buffer, "FAIL", 4)) || (!strncmp(buffer, "UNKNOWN", 7))) { + LOG(DEBUG) << std::endl << "cmd failed: " << cmd; + LOG(WARNING) << std::endl << "reply: " << buffer; + return false; + } + + return true; +} + +bool base_wlan_hal_nl80211::wpa_ctrl_send_msg(const std::string &cmd, parsed_obj_map_t &reply) +{ + if (!wpa_ctrl_send_msg(cmd)) { + return false; + } + + if (strncmp(m_wpa_ctrl_buffer.get(), "", 1)) { + // if reply is not empty + std::stringstream ss_in(m_wpa_ctrl_buffer.get()); + map_obj_parser(ss_in, {'\n', '='}, reply); + } + + return true; +} + +bool base_wlan_hal_nl80211::wpa_ctrl_send_msg(const std::string &cmd, + parsed_obj_listed_map_t &reply) +{ + if (!wpa_ctrl_send_msg(cmd)) { + return false; + } + + if (strncmp(m_wpa_ctrl_buffer.get(), "", 1)) { + // if reply is not empty + std::stringstream ss_in(m_wpa_ctrl_buffer.get()); + listed_map_obj_parser(ss_in, {'\n', ' ', '='}, reply); + } + + return true; +} + +bool base_wlan_hal_nl80211::wpa_ctrl_send_msg(const std::string &cmd, char **reply) +{ + if (!reply || !(*reply)) { + LOG(ERROR) << "Invalid reply pointer!"; + return false; + } + + if (!wpa_ctrl_send_msg(cmd)) { + return false; + } + + *reply = m_wpa_ctrl_buffer.get(); + + return true; +} + +bool base_wlan_hal_nl80211::refresh_radio_info() +{ + parsed_obj_map_t reply; + + if (!wpa_ctrl_send_msg("STATUS", reply)) { + LOG(ERROR) << __func__ << " failed"; + return false; + } + + // LOG(DEBUG) << "*** refresh_radio_info ***"; + // parsed_obj_debug(reply); + + // Station HAL + if (get_type() == HALType::Station) { + // update radio info struct + // TODO test the the output + // m_radio_info.wifi_ctrl_enabled = beerocks::string_utils::stoi(reply["HostapdEnabled"]); + // m_radio_info.tx_enabled = beerocks::string_utils::stoi(reply["TxPower"]); + // m_radio_info.channel = beerocks::string_utils::stoi(reply["Channel"]); + if (m_radio_info.channel <= 0) { + //LOG(ERROR) << "X_LANTIQ_COM_Vendor_Channel not valid: " << radio_info.channel; + return false; + } else if (m_radio_info.channel > 14) { + m_radio_info.is_5ghz = true; + } + + } else { + + // Unavailable + m_radio_info.ant_num = 0; + m_radio_info.conducted_power = 0; + + int ieee80211ac = beerocks::string_utils::stoi(reply["ieee80211ac"]); + + // 2.4Ghz + if (ieee80211ac == 0) { + if (reply.find("num_sta_ht40_intolerant") != reply.end()) { + m_radio_info.bandwidth = 40; + } else { + m_radio_info.bandwidth = 20; + } + + } else { + + // 0 = 20 or 40 MHz operating Channel width + // 1 = 80 MHz channel width + // 2 = 160 MHz channel width + // 3 = 80+80 MHz channel width + int vht_oper_chwidth = beerocks::string_utils::stoi(reply["vht_oper_chwidth"]); + + switch (vht_oper_chwidth) { + case 0: + m_radio_info.bandwidth = 40; + break; + case 1: + m_radio_info.bandwidth = 80; + break; + default: + m_radio_info.bandwidth = 160; + } + + m_radio_info.vht_center_freq = + beerocks::string_utils::stoi(reply["vht_oper_centr_freq_seg0_idx"]); + } + + // State + if (reply["state"] == "ENABLED") { + m_radio_info.wifi_ctrl_enabled = 2; // Assume Operational + m_radio_info.tx_enabled = 1; + } + + // Channel + m_radio_info.channel = beerocks::string_utils::stoi(reply["channel"]); + + m_radio_info.is_5ghz = + (son::wireless_utils::which_freq(m_radio_info.channel) == beerocks::eFreqType::FREQ_5G); + + // If the VAPs map is empty, refresh it as well + // TODO: update on every refresh? + if (!m_radio_info.available_vaps.size()) { + if (!refresh_vaps_info(beerocks::IFACE_RADIO_ID)) { + return false; + } + } + } + + return true; +} + +bool base_wlan_hal_nl80211::refresh_vaps_info(int id) +{ + LOG(TRACE) << __func__ << " - id = " << id; + + parsed_obj_map_t reply; + + // Read the radio status + if (!wpa_ctrl_send_msg("STATUS", reply)) { + LOG(ERROR) << __func__ << " failed"; + return false; + } + + // Scan all VAPs + if (id == beerocks::IFACE_RADIO_ID) { + + for (int vap_id = beerocks::IFACE_VAP_ID_MIN; vap_id <= beerocks::IFACE_VAP_ID_MAX; + vap_id++) { + VAPElement vap_element; + + // Try reading the BSSID and SSID of the requested VAP + vap_element.mac = reply["bssid[" + std::to_string(vap_id) + "]"]; + vap_element.ssid = reply["ssid[" + std::to_string(vap_id) + "]"]; + + // VAP does not exists + if (vap_element.mac.empty()) { + if (m_radio_info.available_vaps.find(vap_id) != m_radio_info.available_vaps.end()) { + m_radio_info.available_vaps.erase(vap_id); + } + continue; + } + + // Store the VAP element + LOG(DEBUG) << "Detected VAP ID (" << vap_id << ") - MAC: " << vap_element.mac + << ", SSID: " << vap_element.ssid; + + m_radio_info.available_vaps[vap_id] = vap_element; + } + } else { + + VAPElement vap_element; + + // Try reading the BSSID and SSID of the requested VAP + vap_element.mac = reply["bssid[" + std::to_string(id) + "]"]; + vap_element.ssid = reply["ssid[" + std::to_string(id) + "]"]; + + // VAP does not exists + if (vap_element.mac.empty()) { + if (m_radio_info.available_vaps.find(id) != m_radio_info.available_vaps.end()) { + m_radio_info.available_vaps.erase(id); + } + + return false; + } + + m_radio_info.available_vaps[id] = vap_element; + } + + return true; +} + +bool base_wlan_hal_nl80211::process_ext_events() +{ + if (!m_wpa_ctrl_event) { + LOG(ERROR) << "Invalid WPA Control socket (m_wpa_ctrl_event == nullptr)"; + return false; + } + + // Check if there are pending events + int status = wpa_ctrl_pending(m_wpa_ctrl_event); + + // No pending messages + if (status == 0) { + LOG(WARNING) << "Process external events called but there are no pending messages..."; + return false; + } else if (status < 0) { + LOG(ERROR) << "Invalid WPA Control socket status: " << status << " --> detaching!"; + detach(); + return false; + } + + auto buffer = m_wpa_ctrl_buffer.get(); + auto buff_size_copy = m_wpa_ctrl_buffer_size; + + if (wpa_ctrl_recv(m_wpa_ctrl_event, buffer, &buff_size_copy) < 0) { + LOG(ERROR) << "wpa_ctrl_recv() failed!"; + return false; + } + + // the wpa_ctrl does not put null termintaor at the and of the string + buffer[buff_size_copy] = 0; + + LOG(DEBUG) << "event received:" << buffer; + + parsed_obj_map_t event_obj; + map_event_obj_parser(buffer, event_obj); + + // parsed_obj_debug(event_obj); + + // Process the event + if (!process_nl80211_event(event_obj)) { + // LOG(ERROR) << "Failed processing NL80211 event: " << event_obj[WAV_EVENT_KEYLESS_PARAM_OPCODE]; + LOG(ERROR) << "Failed processing NL80211 event: " << event_obj["_opcode"]; + return false; + } + + return true; +} + +std::string base_wlan_hal_nl80211::get_radio_mac() +{ + std::string mac; + if (!beerocks::net::network_utils::linux_iface_get_mac(m_radio_info.iface_name, mac)) { + LOG(ERROR) << "Failed to get radio mac from ifname " << m_radio_info.iface_name; + } + return mac; +} + +void base_wlan_hal_nl80211::send_ctrl_iface_cmd(std::string cmd) +{ + parsed_obj_map_t obj1; + parsed_obj_listed_map_t obj2; + + auto last_char = cmd.back(); + + LOG(DEBUG) << "last char: '" << last_char << "'"; + + if (last_char == '1' || last_char == '2') { + cmd.pop_back(); + } + cmd = beerocks::string_utils::trimmed_substr(cmd); + + if (last_char == '1') { + wpa_ctrl_send_msg(cmd, obj1); + } else if (last_char == '2') { + wpa_ctrl_send_msg(cmd, obj2); + } else { + wpa_ctrl_send_msg(cmd); + } +} + +bool base_wlan_hal_nl80211::send_nl80211_msg(uint8_t command, int flags, + std::function msg_create, + std::function msg_handle) +{ + // Netlink Message + std::shared_ptr nl_message = + std::shared_ptr(nlmsg_alloc(), [](struct nl_msg *obj) { + if (obj) { + nlmsg_free(obj); + } + }); + LOG_IF(!nl_message, FATAL) << "Failed creating netlink message!"; + + // Netlink Callback + std::shared_ptr nl_callback = + std::shared_ptr(nl_cb_alloc(NL_CB_DEFAULT), [](struct nl_cb *obj) { + if (obj) { + nl_cb_put(obj); + } + }); + LOG_IF(!nl_callback, FATAL) << "Failed creating netlink callback!"; + + // Create standard callbacks + int err = 1; + static auto nl_err_cb = [](struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) -> int { + int *ret = (int *)arg; + *ret = err->error; + return NL_STOP; + }; + + static auto nl_finish_cb = [](struct nl_msg *msg, void *arg) -> int { + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; + }; + + static auto nl_ack_cb = [](struct nl_msg *msg, void *arg) -> int { + int *ret = (int *)arg; + *ret = 0; + return NL_STOP; + }; + + // Passing a lambda with capture is not supported for standard C function + // pointers. As a workaround, we create a static (but thread local) wrapper + // function that calls the capturing lambda function. + static __thread std::function nl_handler_cb_wrapper; + nl_handler_cb_wrapper = [&](struct nl_msg *msg, void *arg) -> int { + if (!msg_handle(msg)) { + LOG(ERROR) << "User's netlink handler function failed!"; + } + return NL_SKIP; + }; + auto nl_handler_cb = [](struct nl_msg *msg, void *arg) -> int { + return nl_handler_cb_wrapper(msg, arg); + }; + + // Initialize the netlink message + if (!genlmsg_put(nl_message.get(), 0, 0, m_nl80211_id, 0, flags, command, 0) || + nla_put_u32(nl_message.get(), NL80211_ATTR_IFINDEX, m_iface_index) != 0) { + LOG(ERROR) << "Failed initializing the netlink message!"; + return false; + } + + // Call the user's message create function + if (!msg_create(nl_message.get())) { + LOG(ERROR) << "User's netlink create function failed!"; + return false; + } + + // Set the callbacks + nl_cb_err(nl_callback.get(), NL_CB_CUSTOM, nl_err_cb, &err); // error + nl_cb_set(nl_callback.get(), NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_cb, &err); // finish + nl_cb_set(nl_callback.get(), NL_CB_ACK, NL_CB_CUSTOM, nl_ack_cb, &err); // ack + nl_cb_set(nl_callback.get(), NL_CB_VALID, NL_CB_CUSTOM, nl_handler_cb, + nullptr); // response handler + + // Send the netlink message + err = nl_send_auto_complete(m_nl80211_sock.get(), nl_message.get()); + if (err < 0) { + LOG(ERROR) << "nl_send_auto_complete failed: " << err; + return false; + } + + // Process the response + // TODO: Probably should be changed to select/epoll... + // The file descriptor can be extracted by calling the + // nl_socket_get_fd() function. + while (err > 0) { + nl_recvmsgs(m_nl80211_sock.get(), nl_callback.get()); + } + + return true; +} + +} // namespace nl80211 +} // namespace bwl \ No newline at end of file diff --git a/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.h b/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.h new file mode 100644 index 0000000000..7ef6deef3d --- /dev/null +++ b/common/beerocks/bwl/nl80211/base_wlan_hal_nl80211.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#ifndef _BWL_BASE_WLAN_HAL_NL80211_H_ +#define _BWL_BASE_WLAN_HAL_NL80211_H_ + +#include "../common/base_wlan_hal.h" + +#include + +#include +#include +#include +#include + +// Forward declaration +struct wpa_ctrl; +struct nl_sock; +struct nl_msg; + +namespace bwl { +namespace nl80211 { + +enum class nl80211_fsm_state { Delay, Init, GetRadioInfo, Attach, Operational, Detach }; +enum class nl80211_fsm_event { Attach, Detach }; + +/*! + * Base class for the wav abstraction layer. + * Read more about virtual inheritance: https://en.wikipedia.org/wiki/Virtual_inheritance + */ +class base_wlan_hal_nl80211 + : public virtual base_wlan_hal, + protected beerocks::beerocks_fsm { + + // Public types: +public: + typedef std::unordered_map parsed_obj_map_t; + typedef std::list parsed_obj_listed_map_t; + + // Public methods +public: + virtual ~base_wlan_hal_nl80211(); + + virtual HALState attach(bool block = false) override; + virtual bool detach() override; + virtual bool ping() override; + virtual bool refresh_radio_info() override; + virtual bool refresh_vaps_info(int id) override; + virtual bool process_ext_events() override; + virtual std::string get_radio_mac() override; + + // Protected methods +protected: + base_wlan_hal_nl80211(HALType type, std::string iface_name, hal_event_cb_t callback, + int wpa_ctrl_buffer_size, hal_conf_t hal_conf = {}); + + // Process hostapd/wpa_supplicant event + virtual bool process_nl80211_event(parsed_obj_map_t &event) = 0; + + bool set(const std::string ¶m, const std::string &value, + int vap_id = beerocks::IFACE_RADIO_ID); + + // Send a message via the WPA Control Interface + bool wpa_ctrl_send_msg(const std::string &cmd, parsed_obj_map_t &reply); + bool wpa_ctrl_send_msg(const std::string &cmd, parsed_obj_listed_map_t &reply); + bool wpa_ctrl_send_msg(const std::string &cmd, char **reply); // for external process + bool wpa_ctrl_send_msg(const std::string &cmd); + + virtual void send_ctrl_iface_cmd(std::string cmd); // HACK for development, to be removed + + // Send NL80211 message + bool send_nl80211_msg(uint8_t command, int flags, + std::function msg_create, + std::function msg_handle); + + // Private data-members: +private: + bool fsm_setup(); + + // FSM State and Timeout + nl80211_fsm_state m_last_attach_state = nl80211_fsm_state::Detach; + std::chrono::steady_clock::time_point m_state_timeout; + + // WPA Control Interface Objects + struct wpa_ctrl *m_wpa_ctrl_cmd = nullptr; + struct wpa_ctrl *m_wpa_ctrl_event = nullptr; + + // NL80211 Socket + std::shared_ptr m_nl80211_sock; + int m_nl80211_id = 0; + int m_iface_index = 0; + + // WPA Control Interface Communication Buffer + std::shared_ptr m_wpa_ctrl_buffer; + size_t m_wpa_ctrl_buffer_size = 0; + + // Path for the WPA Control Interface Socket + std::string m_wpa_ctrl_path; +}; + +} // namespace nl80211 +} // namespace bwl + +#endif // _BWL_BASE_WLAN_HAL_NL80211_H_ diff --git a/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.cpp b/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.cpp new file mode 100644 index 0000000000..b3c7267e9c --- /dev/null +++ b/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.cpp @@ -0,0 +1,545 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include "mon_wlan_hal_nl80211.h" + +#include +#include + +#include + +#include + +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#include + +namespace bwl { +namespace nl80211 { + +////////////////////////////////////////////////////////////////////////////// +////////////////////////// Local Module Definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#define GET_OP_CLASS(channel) ((channel < 14) ? 4 : 5) +#define BUFFER_SIZE 4096 + +// Allocate a char array wrapped in a shared_ptr +#define ALLOC_SMART_BUFFER(size) \ + std::shared_ptr(new char[size], [](char *obj) { \ + if (obj) \ + delete[] obj; \ + }) + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////// Local Module Functions /////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static mon_wlan_hal::Event wav_to_bwl_event(const std::string &opcode) +{ + if (opcode == "BEACON-REQ-TX-STATUS") { + return mon_wlan_hal::Event::RRM_Beacon_Request_Status; + } else if (opcode == "BEACON-RESP-RX") { + return mon_wlan_hal::Event::RRM_Beacon_Response; + } + // } else if (opcode == "RRM-STA-STATISTICS-RECEIVED") { + // return mon_wlan_hal::Event::RRM_STA_Statistics_Response; + // } else if (opcode == "RRM-LINK-MEASUREMENT-RECEIVED") { + // return mon_wlan_hal::Event::RRM_Link_Measurement_Response; + // } + + return mon_wlan_hal::Event::Invalid; +} + +#if 0 + +static void calc_curr_traffic(std::string buff, uint64_t &total, uint32_t &curr) +{ + // Convert to numeric value + uint64_t val = beerocks::string_utils::stoi(buff); + + if (val >= total) { + curr = val - total; + } else { + curr = val; + } + total = val; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Implementation /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +mon_wlan_hal_nl80211::mon_wlan_hal_nl80211(std::string iface_name, hal_event_cb_t callback) + : base_wlan_hal(bwl::HALType::Monitor, iface_name, IfaceType::Intel, callback), + base_wlan_hal_nl80211(bwl::HALType::Monitor, iface_name, callback, BUFFER_SIZE) +{ +} + +mon_wlan_hal_nl80211::~mon_wlan_hal_nl80211() {} + +bool mon_wlan_hal_nl80211::update_radio_stats(SRadioStats &radio_stats) +{ + radio_stats.tx_bytes_cnt = 0; + radio_stats.tx_bytes = 0; + + radio_stats.rx_bytes_cnt = 0; + radio_stats.rx_bytes = 0; + + radio_stats.tx_packets_cnt = 0; + radio_stats.tx_packets = 0; + + radio_stats.rx_packets_cnt = 0; + radio_stats.rx_packets = 0; + + radio_stats.bss_load = 0; + radio_stats.errors_sent = 0; + radio_stats.errors_received = 0; + radio_stats.noise = 0; + + return true; +} + +bool mon_wlan_hal_nl80211::update_vap_stats(const std::string vap_iface_name, SVapStats &vap_stats) +{ + vap_stats.tx_bytes_cnt = 0; + vap_stats.tx_bytes = 0; + + vap_stats.rx_bytes_cnt = 0; + vap_stats.rx_bytes = 0; + + vap_stats.tx_packets_cnt = 0; + vap_stats.tx_packets = 0; + + vap_stats.rx_packets_cnt = 0; + vap_stats.rx_packets = 0; + + vap_stats.errors_sent = 0; + vap_stats.errors_received = 0; + vap_stats.retrans_count = 0; + + // TODO: Handle timeouts/deltas externally! + // auto now = std::chrono::steady_clock::now(); + // auto time_span = std::chrono::duration_cast(now - vap_stats->last_update_time); + // vap_stats->delta_ms = float(time_span.count()); + // vap_stats->last_update_time = now; + + return true; +} + +bool mon_wlan_hal_nl80211::update_stations_stats(const std::string vap_iface_name, + const std::string sta_mac, SStaStats &sta_stats) +{ + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1]; + stats_policy[NL80211_STA_INFO_INACTIVE_TIME] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_RX_BYTES] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_TX_BYTES] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_RX_PACKETS] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_TX_PACKETS] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_SIGNAL] = {NLA_U8, 0, 0}; + stats_policy[NL80211_STA_INFO_T_OFFSET] = {NLA_U64, 0, 0}; + stats_policy[NL80211_STA_INFO_TX_BITRATE] = {NLA_NESTED, 0, 0}; + stats_policy[NL80211_STA_INFO_RX_BITRATE] = {NLA_NESTED, 0, 0}; + stats_policy[NL80211_STA_INFO_LLID] = {NLA_U16, 0, 0}; + stats_policy[NL80211_STA_INFO_PLID] = {NLA_U16, 0, 0}; + stats_policy[NL80211_STA_INFO_PLINK_STATE] = {NLA_U8, 0, 0}; + stats_policy[NL80211_STA_INFO_TX_RETRIES] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_TX_FAILED] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_STA_FLAGS] = {NLA_UNSPEC, sizeof(struct nl80211_sta_flag_update), + 0}; + stats_policy[NL80211_STA_INFO_LOCAL_PM] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_PEER_PM] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_NONPEER_PM] = {NLA_U32, 0, 0}; + stats_policy[NL80211_STA_INFO_CHAIN_SIGNAL] = {NLA_NESTED, 0, 0}; + stats_policy[NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = {NLA_NESTED, 0, 0}; + + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1]; + rate_policy[NL80211_RATE_INFO_BITRATE] = {NLA_U16, 0, 0}; + rate_policy[NL80211_RATE_INFO_BITRATE32] = {NLA_U32, 0, 0}; + rate_policy[NL80211_RATE_INFO_MCS] = {NLA_U8, 0, 0}; + rate_policy[NL80211_RATE_INFO_40_MHZ_WIDTH] = {NLA_FLAG, 0, 0}; + rate_policy[NL80211_RATE_INFO_SHORT_GI] = {NLA_FLAG, 0, 0}; + + auto ret = send_nl80211_msg( + NL80211_CMD_GET_STATION, 0, + // Create the message + [&](struct nl_msg *msg) -> bool { + auto mac = beerocks::net::network_utils::mac_from_string(sta_mac); + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac.oct); + return true; + }, + // Handle the reponse + [&](struct nl_msg *msg) -> bool { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + + // Parse the netlink message + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), + NULL); + + if (!tb[NL80211_ATTR_STA_INFO]) { + LOG(ERROR) << "sta stats missing!"; + return false; + } + + // Parse nested station stats + if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], + stats_policy)) { + LOG(ERROR) << "failed to parse nested attributes!"; + return false; + } + + // RX RSSI + if (sinfo[NL80211_STA_INFO_SIGNAL]) { + int8_t signal = int8_t(nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL])); + sta_stats.rx_rssi_watt = pow(10, (signal / 10.0)); + sta_stats.rx_rssi_watt_samples_cnt++; + } + + // RX SNR is not supported + sta_stats.rx_snr_watt = 0; + sta_stats.rx_snr_watt_samples_cnt = 0; + + // Bitrate parsing helper function + auto parse_bitrate_func = [&](struct nlattr *bitrate_attr) -> int { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, bitrate_attr, rate_policy)) { + LOG(ERROR) << "Failed to parse nested rate attributes!"; + return 0; + } + + int rate = 0; + if (rinfo[NL80211_RATE_INFO_BITRATE32]) + rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]); + else if (rinfo[NL80211_RATE_INFO_BITRATE]) + rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]); + + return rate; + }; + + // TX Phy Rate + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + sta_stats.tx_phy_rate_100kb = + parse_bitrate_func(sinfo[NL80211_STA_INFO_TX_BITRATE]) / 100; + } + + // RX Phy Rate + if (sinfo[NL80211_STA_INFO_RX_BITRATE]) { + sta_stats.tx_phy_rate_100kb = + parse_bitrate_func(sinfo[NL80211_STA_INFO_RX_BITRATE]) / 100; + } + + // Traffic values calculations helper function + auto calc_curr_traffic = [](uint64_t val, uint64_t &total, uint32_t &curr) { + if (val >= total) { + curr = val - total; + } else { + curr = val; + } + total = val; + }; + + // TX Bytes + if (sinfo[NL80211_STA_INFO_TX_BYTES]) { + calc_curr_traffic(nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]), + sta_stats.tx_bytes_cnt, sta_stats.tx_bytes); + } + + // RX Bytes + if (sinfo[NL80211_STA_INFO_RX_BYTES]) { + calc_curr_traffic(nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]), + sta_stats.rx_bytes_cnt, sta_stats.rx_bytes); + } + + // TX Packets + if (sinfo[NL80211_STA_INFO_TX_PACKETS]) { + calc_curr_traffic(nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]), + sta_stats.tx_packets_cnt, sta_stats.tx_packets); + } + + // RX Packets + if (sinfo[NL80211_STA_INFO_RX_PACKETS]) { + calc_curr_traffic(nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]), + sta_stats.rx_packets_cnt, sta_stats.rx_packets); + } + + // TX Retries + if (sinfo[NL80211_STA_INFO_TX_RETRIES]) { + sta_stats.retrans_count = nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]); + } + + return true; + }); + + if (!ret) { + LOG(ERROR) << "Failed updating stats for station: " << sta_mac; + return false; + } + + return true; +} + +bool mon_wlan_hal_nl80211::sta_channel_load_11k_request(const SStaChannelLoadRequest11k &req) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool mon_wlan_hal_nl80211::sta_beacon_11k_request(const SBeaconRequest11k &req, int &dialog_token) +{ + LOG(TRACE) << __func__; + + // parameters preperations + + // Mode + auto request = (!req.enable) ? 0 : req.request; + auto report = (!req.enable) ? 0 : req.report; + + uint8_t req_mode = (req.parallel | (req.enable ? 0x02 : 0) | (request ? 0x04 : 0) | + (report ? 0x08 : 0) | (req.mandatory_duration ? 0x10 : 0)); + + auto op_class = req.op_class < 0 ? GET_OP_CLASS(get_radio_info().channel) : req.op_class; + + if (req.op_class <= 0) { + LOG(WARNING) << __func__ << " op_class not set!"; + } + + uint8_t measurement_mode; + switch ((SBeaconRequest11k::MeasurementMode)(req.measurement_mode)) { + case SBeaconRequest11k::MeasurementMode::Passive: + measurement_mode = 0; + break; + case SBeaconRequest11k::MeasurementMode::Active: + measurement_mode = 1; + break; + case SBeaconRequest11k::MeasurementMode::Table: + measurement_mode = 2; + break; + default: { + LOG(WARNING) << "Invalid measuremetn mode: " << int(req.measurement_mode) + << ", using PASSIVE..."; + + measurement_mode = 0; + } + } + + // REQ_BEACON 02:14:5f:b8:ae:9f req_mode=01 510B000011110004f021318193 + // op_class (2), channel (2), randomization_interval (4), measurement duration (4), measurement mode (2), bssid (12) + + // build command + std::string cmd = "REQ_BEACON " + beerocks::net::network_utils::mac_to_string(req.sta_mac.oct) + + " " + // Destination MAC Address + "req_mode=" + beerocks::string_utils::int_to_hex_string(req_mode, 2) + + " " + // Measurements Request Mode + beerocks::string_utils::int_to_hex_string(op_class, 2) + + beerocks::string_utils::int_to_hex_string(req.channel, 2) + + beerocks::string_utils::int_to_hex_string(htobe16(req.rand_ival), 4) + + beerocks::string_utils::int_to_hex_string(htobe16(req.duration), 4) + + beerocks::string_utils::int_to_hex_string(measurement_mode, 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[0], 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[1], 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[2], 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[3], 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[4], 2) + + beerocks::string_utils::int_to_hex_string(req.bssid.oct[5], 2); + + // Print the command + LOG(DEBUG) << __func__ << " - " << cmd; + + // Send the command + parsed_obj_map_t reply; + if (!wpa_ctrl_send_msg(cmd, reply)) { + LOG(ERROR) << __func__ << " failed"; + return false; + } + + dialog_token = 0; //tmp_int; + + return true; +} + +bool mon_wlan_hal_nl80211::sta_statistics_11k_request(const SStatisticsRequest11k &req) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool mon_wlan_hal_nl80211::sta_link_measurements_11k_request(const std::string &sta_mac) +{ + LOG(TRACE) << __func__ << " - NOT IMPLEMENTED!"; + return true; +} + +bool mon_wlan_hal_nl80211::process_nl80211_event(parsed_obj_map_t &parsed_obj) +{ + // Filter out empty events + std::string opcode; + if (!(parsed_obj.find("_opcode") != parsed_obj.end() && + !(opcode = parsed_obj["_opcode"]).empty())) { + return true; + } + + auto event = wav_to_bwl_event(opcode); + + // Handle the event + switch (event) { + + case Event::RRM_Beacon_Request_Status: { + + // Allocate response object + auto resp_buff = ALLOC_SMART_BUFFER(sizeof(SBeaconRequestStatus11k)); + auto resp = reinterpret_cast(resp_buff.get()); + LOG_IF(!resp, FATAL) << "Memory allocation failed!"; + + // Initialize the message + memset(resp_buff.get(), 0, sizeof(SBeaconRequestStatus11k)); + + // STA Mac Address + beerocks::net::network_utils::mac_from_string(resp->sta_mac.oct, parsed_obj["_mac"]); + + // Dialog token and ACK + resp->dialog_token = beerocks::string_utils::stoi(parsed_obj["_arg0"]); + resp->ack = beerocks::string_utils::stoi(parsed_obj["ack"]); + + // Add the message to the queue + event_queue_push(event, resp_buff); + + } break; + + case Event::RRM_Beacon_Response: { + + // Allocate response object + auto resp_buff = ALLOC_SMART_BUFFER(sizeof(SBeaconResponse11k)); + auto resp = reinterpret_cast(resp_buff.get()); + LOG_IF(!resp, FATAL) << "Memory allocation failed!"; + + // Initialize the message + memset(resp_buff.get(), 0, sizeof(SBeaconResponse11k)); + + // STA Mac Address + beerocks::net::network_utils::mac_from_string(resp->sta_mac.oct, parsed_obj["_mac"]); + + // Dialog token and rep_mode + resp->dialog_token = beerocks::string_utils::stoi(parsed_obj["_arg0"]); + resp->rep_mode = beerocks::string_utils::stoi(parsed_obj["_arg1"]); + + // Parse the report + auto report = parsed_obj["_arg2"]; + if (report.length() < 52) { + LOG(WARNING) << "Invalid 11k report length!"; + break; + } + + // TODO: Check for argument validity and use a safer stoi version + int idx = 0; + + // op_class + resp->op_class = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // channel + resp->channel = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // start_time + resp->start_time = std::stoull(report.substr(idx, 16), 0, 16); + resp->start_time = be64toh(resp->start_time); + idx += 16; + + // measurement_duration + resp->duration = std::stoi(report.substr(idx, 4), 0, 16); + resp->duration = be16toh(resp->duration); + idx += 4; + + // phy_type + resp->phy_type = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // rcpi + resp->rcpi = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // rsni + resp->rsni = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // bssid + resp->bssid.oct[0] = std::stoi(report.substr(idx + 0, 2), 0, 16); + resp->bssid.oct[1] = std::stoi(report.substr(idx + 2, 2), 0, 16); + resp->bssid.oct[2] = std::stoi(report.substr(idx + 4, 2), 0, 16); + resp->bssid.oct[3] = std::stoi(report.substr(idx + 6, 2), 0, 16); + resp->bssid.oct[4] = std::stoi(report.substr(idx + 8, 2), 0, 16); + resp->bssid.oct[5] = std::stoi(report.substr(idx + 10, 2), 0, 16); + idx += 12; + + // ant_id + resp->ant_id = std::stoi(report.substr(idx, 2), 0, 16); + idx += 2; + + // parent_tsf + resp->parent_tsf = std::stoull(report.substr(idx, 8), 0, 16); + idx += 8; + + // TODO: Ignore everything else? + // WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY == 01 + // frame_body.length = ?? + + LOG(DEBUG) << "Beacon Response:" << std::endl + << " op_class = " << int(resp->op_class) << std::endl + << " channel = " << int(resp->channel) << std::endl + << " start_time = " << int(resp->start_time) << std::endl + << " duration = " << int(resp->duration) << std::endl + << " phy_type = " << int(resp->phy_type) << std::endl + << " rcpi = " << int(resp->rcpi) << std::endl + << " rsni = " << int(resp->rsni) << std::endl + << " bssid = " << beerocks::net::network_utils::mac_to_string(resp->bssid.oct) + << std::endl + << " ant_id = " << int(resp->ant_id) << std::endl + << " parent_tfs = " << int(resp->parent_tsf); + + // Add the message to the queue + event_queue_push(event, resp_buff); + + } break; + + // Gracefully ignore unhandled events + // TODO: Probably should be changed to an error once WAV will stop + // sending empty or irrelevant events... + default: { + LOG(WARNING) << "Unhandled event received: " << opcode; + break; + }; + } + + return true; +} + +} // namespace nl80211 +} // namespace bwl + +// AP WAV HAL Factory Functions +extern "C" { + +bwl::mon_wlan_hal *mon_wlan_hal_create(std::string iface_name, + bwl::base_wlan_hal::hal_event_cb_t callback) +{ + return new bwl::nl80211::mon_wlan_hal_nl80211(iface_name, callback); +} + +void mon_wlan_hal_destroy(bwl::mon_wlan_hal *obj) { delete obj; } +} diff --git a/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.h b/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.h new file mode 100644 index 0000000000..2424e3123e --- /dev/null +++ b/common/beerocks/bwl/nl80211/mon_wlan_hal_nl80211.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Copyright (c) 2016-2019 Intel Corporation + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#ifndef _BWL_MON_WLAN_HAL_NL80211_H_ +#define _BWL_MON_WLAN_HAL_NL80211_H_ + +#include "../common/mon_wlan_hal.h" +#include "base_wlan_hal_nl80211.h" + +namespace bwl { +namespace nl80211 { + +/*! + * Hardware abstraction layer for WLAN Monitor. + */ +class mon_wlan_hal_nl80211 : public base_wlan_hal_nl80211, public mon_wlan_hal { + + // Public methods +public: + /*! + * Constructor. + * + * @param [in] iface_name Monitor interface name. + * @param [in] callback Callback for handling internal events. + */ + mon_wlan_hal_nl80211(std::string iface_name, hal_event_cb_t callback); + virtual ~mon_wlan_hal_nl80211(); + + virtual bool update_radio_stats(SRadioStats &radio_stats) override; + virtual bool update_vap_stats(const std::string vap_iface_name, SVapStats &vap_stats) override; + virtual bool update_stations_stats(const std::string vap_iface_name, const std::string sta_mac, + SStaStats &sta_stats) override; + + virtual bool sta_channel_load_11k_request(const SStaChannelLoadRequest11k &req) override; + virtual bool sta_beacon_11k_request(const SBeaconRequest11k &req, int &dialog_token) override; + virtual bool sta_statistics_11k_request(const SStatisticsRequest11k &req) override; + virtual bool sta_link_measurements_11k_request(const std::string &sta_mac) override; + + // Protected methods: +protected: + virtual bool process_nl80211_event(parsed_obj_map_t &parsed_obj) override; + + // Overload for Monitor events + bool event_queue_push(mon_wlan_hal::Event event, std::shared_ptr data = {}) + { + return base_wlan_hal::event_queue_push(int(event), data); + } + + // Private data-members: +private: + std::shared_ptr m_temp_wav_value; +}; + +} // namespace nl80211 +} // namespace bwl + +#endif // _BWL_MON_WLAN_HAL_NL80211_H_