From 51cc4b8448aa9c50314cf3046d54b79dc7a09c8d Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Tue, 7 Jul 2020 16:46:36 +0300 Subject: [PATCH 01/28] prplmesh-hostapd: info: added README.md file A file explaining the content of this directory https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/README.md | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 common/beerocks/hostapd/README.md diff --git a/common/beerocks/hostapd/README.md b/common/beerocks/hostapd/README.md new file mode 100644 index 0000000000..6b38e522bf --- /dev/null +++ b/common/beerocks/hostapd/README.md @@ -0,0 +1,90 @@ + +# Directory Content +This diectory contains prplMesh utilities to work with hostapd +It has few features as listed below + +## Configuration +Manipulates hostapd configuration. +* load +* save +* add +* remove +* edit +* disable +* find + +## Process (yet to implement) +Manages hostapd process +* start +* stop +* restart + +## Commands (yet to implement) +Manages sending and receiving commands to and from ostapd + +## Events (yet to implement) +Manages events originated by hostapd itself + +# Code + +## namespace +The entire code is under prplmesh::hostapd +* Configuration: there is a class named Configuration +* Process: prplmesh::hostapd::process (yet to implement) +* Commands: prplmesh::hostapd::commands (yet to implement) +* Events: prplmesh::hostapd::event (yet to implement) + +## Limitations +We are updating hostapd's configuration files with the info that we get from +the controller. Once we changed the configuration we send UPDATE command to hostapd +making it re-read the configuration and updating the VAPs accordingly. + +With that approach there are the following limitations: + - all the VAPs are expected to be always present in the config, but to be + commented out if not active. + - any configuration change causing the hostapd config files to be re-generated + and the hostapd processes restarted will override the prplMesh configuration. + +## hostapd Configuration Format + +hostapd has a special format that is NOT an ini like format. +below, between /// BEGIN hostapd.conf /// and /// END hostapd.conf /// is the +format of the file. +note: the string "bss=" may be replaced by the +user in the call to load() with another vap-indicator (e.g. "interface=") + +/// BEGIN hostapd.conf /// + +\### "head" part + +\# everything until the first `bss=` in the file +\# is considered "head" +\# after the first `bss=` "vaps" are presented. +\# so we expect no parametrs that does not belong to vaps +\# after the first `bss=` +\# take a look below for more details +\# +\# note that we don't expect a space between the key and the equal sign +\# the code in seeks for `key=` when a key is needed. +\# therefore `key =` (with space) will fail +\# also, everything immidiatly after the equal sign (=) is +\# part of the value. the space before 11 in this line is part of the value: +bassid= 11:22:33:44:55:66 +key=value + +\### "vaps part - we expect at least one vap to be configured ## + +\# vap (bss and vap are interchangable) +bss=wlan0_0 + +\# this key (ssid) belongs to the previous vap (bss value which is wlan0_0) +ssid=test2 + +# another vap +bss=wlan0_1 + +# this key (bssid) belongs to the previous vap wlan0_1 +bssid=00:13:10:95:fe:0b + +///// END hostapd.conf /// + From 9738721dfdb56e4cc242263d44ea9641a0c3830c Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 8 Jul 2020 11:47:01 +0300 Subject: [PATCH 02/28] prplmesh-hostapd: added configuration class added hostapd Configuration class to be used by applications that wants to edit, add, delete, enable, disable, etc parametrs in hostapd configuration file https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 138 ++++++++++++++++++ .../beerocks/hostapd/include/configuration.h | 93 ++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 common/beerocks/hostapd/configuration.cpp create mode 100644 common/beerocks/hostapd/include/configuration.h diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp new file mode 100644 index 0000000000..adb50bb73e --- /dev/null +++ b/common/beerocks/hostapd/configuration.cpp @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * SPDX-FileCopyrightText: 2016-2020 the prplMesh contributors (see AUTHORS.md) + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include "configuration.h" +#include +#include +#include + +namespace prplmesh { +namespace hostapd { +namespace config { + +Configuration::Configuration(const std::string& file_name) : m_configuration_file(file_name) {} + +Configuration::operator bool() const { return m_ok; } + +bool Configuration::load() +{ + // please take a look at the end of this file for + // the expected format of hostapd configuration file + // loading the file relays on the expected format + // otherwise the load fails + + std::ifstream ifs(m_configuration_file); + std::string line; + + bool parsing_vaps = false; + std::string cur_vap; + + while (getline(ifs, line)) { + + // skip empty lines + if (std::all_of(line.begin(), line.end(), isspace)) { + continue; + } + // check if the string belongs to a vap config part and capture which one. + const std::string bss_eq("bss="); + if (line.compare(0, bss_eq.length(), bss_eq) == 0) { + + // from now on we are in the vaps area, all + // key/value pairs belongs to vaps + parsing_vaps = true; + + // copy the bss (vap) value + cur_vap.assign(line, bss_eq.length(), std::string::npos); + } + + // if not a vap line store it in the header part of the config, + // otherwise add to the currently being parsed vap storage. + if (!parsing_vaps) { + m_hostapd_config_head.push_back(line); + } else { + m_hostapd_config_vaps[cur_vap].push_back(line); + } + } + + m_error_description = strerror(errno); + + // if we've got to parsing vaps and no read errors, assume all is good + m_ok = parsing_vaps && !ifs.bad(); + return m_ok; +} + +std::ostream &operator<<(std::ostream &o, const Configuration &conf) +{ + o << "== configuration details ==\n" + << "= ok: " << std::boolalpha << conf.m_ok << '\n' + << "= erorr text: " << conf.m_error_description << '\n' + << "= file: " << conf.m_configuration_file << '\n' + << "= head: " << '\n'; + + for (const auto &line : conf.m_hostapd_config_head) { + o << line << '\n'; + } + + o << "== vaps (" << conf.m_hostapd_config_vaps.size() << ") ==\n"; + + for (const auto &vap : conf.m_hostapd_config_vaps) { + o << " = vap (" << vap.first << " ) =\n"; + for (const auto &line : vap.second) { + o << line << '\n'; + } + } + + return o; +} + +} // namespace config +} // namespace hostapd +} // namespace prplmesh + +/* +//////////////// hostapd configuration format //////////////// + +hostapd has a special format that does NOT have an ini like format. +We expect the following format of the file: +(between /// BEGIN hostapd.conf /// and /// END hostapd.conf ///) + +/// BEGIN hostapd.conf /// + +## "head" part ## + +# everything until the first `bss=` in the file +# is considered "head" +# after the first `bss=` "vaps" are presented. +# so we expect no parametrs that does not belong to vaps +# after the first `bss=` +# take a look below for more details + +# note that we don't expect a space between the key and the equal sign +# the code in this class seeks for `key=` when a key is needed. +# therefore `key =` (with space) will fail +# also, everything immidiatly after the equal sign (=) is +# part of the value. the space before 11 in this is part of the value: +# bassid= 11:22:33:44:55:66 +key=value + +## "vaps part - we expect at least one vap to be configured ## + +# vap (bss and vap are interchangable) +bss=wlan0_0 + +# this key (ssid) belongs to the previous vap (bss value which is wlan0_0) +ssid=test2 + +# another vap +bss=wlan0_1 + +# this key (bssid) belongs to the previous vap wlan0_1 +bssid=00:13:10:95:fe:0b + +///// END hostapd.conf /// +*/ diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h new file mode 100644 index 0000000000..cb01fbedbc --- /dev/null +++ b/common/beerocks/hostapd/include/configuration.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * SPDX-FileCopyrightText: 2016-2020 the prplMesh contributors (see AUTHORS.md) + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include +#include +#include + +namespace prplmesh { +namespace hostapd { +namespace config { + +class Configuration { + +public: + /** + * @brief construct a Configurationn object + * @param file_name the name of the configuration file. + */ + Configuration(const std::string& file_name); + + ~Configuration() = default; + Configuration(const Configuration &) = default; + Configuration(Configuration &&) = default; + + /** + * @brief for simple use in if statements + * @return true or false with the following meaning: + * true - configuration is non empty and valid + * false - otherwise + */ + operator bool() const; + + /** + * @brief load the configuration + * @details loads the file this object is constructed with + * @return *this (as bool, see above) + * e.g. + * Configuratio conf("hostapd.conf"); + * if (!conf.load()) { + * // handle error state + * } + * + */ + bool load(); + +private: + std::string m_configuration_file; + bool m_ok = false; + + // each string is a line in the original configuration file + // that belongs to the "head" part. read the explnation at + // the end of the cpp file for more details + std::vector m_hostapd_config_head; + + // a map between a vap (the key) to its key/value pairs. + // each string in the value part of the map is the line in the original + // configuration file with the original key=value + // e.g. the following lines in the configuration file: + // bss=wlan0.1 <------------------------------ the key in the map: "wlan0.1" + // ctrl_interface=/var/run/hostapd <---------- each line is an element in the array of the map's value + // ap_isolate=1 + // ap_max_inactivity=60 + // bss_transition=1 + // interworking=1 + // disassoc_low_ack=1 + // bss=wlan0.2 <------------------------------ the key in the map: "wlan0.2" + // ctrl_interface=/var/run/hostXXd + // ap_isolate=1 + // ap_max_inactivity=60 + // bss_transition=0 + // interworking=3 + // creates two entries in the map: + // { "wlan0.1" : ["ctrl_interface=/var/run/hostapd", "ap_isolate=1", "ap_max_inactivity=60", "bss_transition=1", "interworking=1", "disassoc_low_ack=1"] }, + // { "wlan0.2" : ["ctrl_interface=/var/run/hosXXpd", "ap_isolate=1", "ap_max_inactivity=60", "bss_transition=0", "interworking=3"] } + std::map> m_hostapd_config_vaps; + + std::string m_error_description = "no error"; + + // for logs + friend std::ostream &operator<<(std::ostream &, const Configuration &); +}; + +// for logs +std::ostream &operator<<(std::ostream &, const Configuration &); + +} // namespace config +} // namespace hostapd +} // namespace prplmesh From c7b62027f641707e1dc95f4f483b4452297bb71b Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 8 Jul 2020 11:48:28 +0300 Subject: [PATCH 03/28] prplmesh-hostapd: added a unit test file unit tests to test hostapd configuration functionality https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/CMakeLists.txt | 85 ++++ .../hostapd/unit_tests/configuration_test.cpp | 455 ++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 common/beerocks/hostapd/CMakeLists.txt create mode 100644 common/beerocks/hostapd/unit_tests/configuration_test.cpp diff --git a/common/beerocks/hostapd/CMakeLists.txt b/common/beerocks/hostapd/CMakeLists.txt new file mode 100644 index 0000000000..8287220dec --- /dev/null +++ b/common/beerocks/hostapd/CMakeLists.txt @@ -0,0 +1,85 @@ +project(prplmesh_hostapd VERSION ${prplmesh_VERSION}) + +# Set the base path for the current module +set(MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# Default defaults +set(PRPLMESH_HOSTAPD_TYPE "DUMMY" CACHE STRING "PRPLMESH_HOSTAPD type") +set_property(CACHE PRPLMESH_HOSTAPD_TYPE PROPERTY STRINGS "DUMMY" "NL80211" "DWPAL") +add_definitions(-DBEEROCKS_TMP_PATH="${TMP_PATH}") + +if (TARGET_PLATFORM STREQUAL "rdkb") + + set(PRPLMESH_HOSTAPD_TYPE "DWPAL") + # Extra libraries + list(APPEND PRPLMESH_HOSTAPD_LIBS rt dl) + +elseif(TARGET_PLATFORM STREQUAL "openwrt") + + if (TARGET_PLATFORM_TYPE STREQUAL "ugw") + set(PRPLMESH_HOSTAPD_TYPE "DWPAL") + else() + set(PRPLMESH_HOSTAPD_TYPE "NL80211") + endif() + + # hostapd directory + file(GLOB PRPLMESH_HOSTAPD_SEARCH_PATHS "${PLATFORM_BUILD_DIR}/hostapd*/hostapd-*") + find_path(PRPLMESH_HOSTAPD_INCLUDE_DIR NAMES "src/common/wpa_ctrl.h" PATHS ${PRPLMESH_HOSTAPD_SEARCH_PATHS} NO_CMAKE_FIND_ROOT_PATH) + set(PRPLMESH_HOSTAPD_DIR "${PRPLMESH_HOSTAPD_INCLUDE_DIR}") + +endif() + +set(PRPLMESH_HOSTAPD_TYPE ${PRPLMESH_HOSTAPD_TYPE} CACHE STRING "Which PRPLMESH_HOSTAPD backend to use") + +########################################################################## +########################################################################## +########################################################################## + +set( + hostapd_sources + configuration.cpp +) + + +# Build the library +add_library(${PROJECT_NAME} ${hostapd_sources}) +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${prplmesh_VERSION} SOVERSION ${prplmesh_VERSION_MAJOR}) +target_link_libraries(${PROJECT_NAME} elpp ${PRPLMESH_HOSTAPD_LIBS}) +target_include_directories(${PROJECT_NAME} + PRIVATE + ${PLATFORM_INCLUDE_DIR} + PUBLIC + $ +) + +install( + TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +if (BUILD_TESTS) + string(TOLOWER ${PRPLMESH_HOSTAPD_TYPE} PRPLMESH_HOSTAPD_TYPE_LOWERCASE) + set(TEST_PROJECT_NAME ${PROJECT_NAME}_${PRPLMESH_HOSTAPD_TYPE_LOWERCASE}_unit_tests) + set(unit_tests_sources + ${MODULE_PATH}/unit_tests/configuration_test.cpp + ) + add_executable(${TEST_PROJECT_NAME} + ${unit_tests_sources} + ) + if (COVERAGE) + set_target_properties(${TEST_PROJECT_NAME} PROPERTIES COMPILE_FLAGS "--coverage -fPIC -O0") + set_target_properties(${TEST_PROJECT_NAME} PROPERTIES LINK_FLAGS "--coverage") + endif() + target_include_directories(${TEST_PROJECT_NAME} + PRIVATE + ${PLATFORM_INCLUDE_DIR} + PUBLIC + $ + ) + target_link_libraries(${TEST_PROJECT_NAME} ${PROJECT_NAME} ${PRPLMESH_HOSTAPD_LIBS}) + target_link_libraries(${TEST_PROJECT_NAME} gtest_main) + install(TARGETS ${TEST_PROJECT_NAME} DESTINATION bin/tests) + add_test(NAME ${TEST_PROJECT_NAME} COMMAND $) +endif() diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp new file mode 100644 index 0000000000..653040a60a --- /dev/null +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: BSD-2-Clause-Patent + * + * SPDX-FileCopyrightText: 2020 the prplMesh contributors (see AUTHORS.md) + * + * This code is subject to the terms of the BSD+Patent license. + * See LICENSE file for more details. + */ + +#include "configuration.h" +#include + +#include + +namespace { + +const std::string configuration_path("/tmp/"); +const std::string configuration_file_name("hostapd.conf"); +const std::string configuration_content( + "driver=nl80211\n" + "logger_syslog=127\n" + "logger_syslog_level=2\n" + "logger_stdout=127\n" + "logger_stdout_level=2\n" + "sDisableMasterVap=1\n" + "atf_config_file=/var/run/hostapd-phy0-atf.conf\n" + "country_code=US\n" + "ieee80211d=1\n" + "hw_mode=g\n" + "beacon_int=100\n" + "channel=acs_smart\n" + "ieee80211n=1\n" + "ht_capab=[LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][MAX-AMSDU-7935][DSSS_CCK-40]\n" + "vht_capab=[RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][SU-BEAMFORMER][SU-BEAMFORMEE][MU-" + "BEAMFORMER][VHT-TXOP-PS][VHT160][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7][BF-ANTENNA-4][SOUNDING-" + "DIMENSION-2]\n" + "ieee80211ax=1\n" + "acs_num_scans=1\n" + "acs_smart_info_file=/var/run/acs_smart_info_wlan0.txt\n" + "acs_history_file=/var/run/acs_history_wlan0.txt\n" + "interface=wlan0\n" + "ctrl_interface=/var/run/hostapd\n" + "ap_isolate=1\n" + "rrm_neighbor_report=1\n" + "bss_transition=1\n" + "interworking=1\n" + "disassoc_low_ack=1\n" + "preamble=1\n" + "wmm_enabled=1\n" + "ignore_broadcast_ssid=1\n" + "uapsd_advertisement_enabled=1\n" + "mbo=1\n" + "vendor_elements=dd050009860100\n" + "vendor_vht=1\n" + "auth_algs=1\n" + "wpa=0\n" + "ssid=dummy_ssid_0\n" + "bridge=br-lan\n" + "bssid=02:9A:96:FB:59:0F\n" + + "bss=wlan0.0\n" + "ctrl_interface=/var/run/hostapd\n" + "ap_isolate=1\n" + "ap_max_inactivity=60\n" + "rrm_neighbor_report=1\n" + "bss_transition=1\n" + "interworking=1\n" + "disassoc_low_ack=1\n" + "preamble=1\n" + "wmm_enabled=1\n" + "mode=ap\n" + "ignore_broadcast_ssid=0\n" + "uapsd_advertisement_enabled=1\n" + "mbo=1\n" + "vendor_elements=dd050009860100\n" + "vendor_vht=1\n" + "auth_algs=1\n" + "eap_server=1\n" + "device_type=6-0050F204-1\n" + "device_name=WLAN-ROUTER\n" + "manufacturer=Intel Corporation\n" + "config_methods=push_button\n" + "wps_independent=1\n" + "wps_cred_processing=1\n" + "os_version=01020300\n" + "manufacturer_url=http://www.intel.com\n" + "model_description=TR069 Gateway\n" + "wps_rf_bands=a\n" + "bridge=br-lan\n" + "bssid=02:9A:96:FB:59:11\n" + "ssid=Multi-AP-24G-1\n" + "wpa=2\n" + "okc=0\n" + "wpa_key_mgmt=WPA-PSK\n" + "wpa_pairwise=CCMP\n" + "ieee80211w=0\n" + "wpa_passphrase=maprocks1\n" + "disable_pmksa_caching=1\n" + "wpa_disable_eapol_key_retries=0\n" + "wps_state=2\n" + "mesh_mode=fAP\n" + "multi_ap_backhaul_ssid=\"Multi-AP-24G-2\"\n" + "multi_ap_backhaul_wpa_passphrase=maprocks2\n" + + "bss=wlan0.1\n" + "mode=non-ap\n" + "ctrl_interface=/var/run/hostapd\n" + "ap_isolate=1\n" + "ap_max_inactivity=60\n" + "rrm_neighbor_report=1\n" + "bss_transition=1\n" + "interworking=1\n" + "disassoc_low_ack=1\n" + "preamble=1\n" + "wmm_enabled=1\n" + "ignore_broadcast_ssid=0\n" + "uapsd_advertisement_enabled=1\n" + "mbo=1\n" + "vendor_elements=dd050009860100\n" + "vendor_vht=1\n" + "auth_algs=1\n" + "bridge=br-lan\n" + "bssid=02:9A:96:FB:59:12\n" + "ssid=Multi-AP-24G-2\n" + "wpa=2\n" + "okc=0\n" + "wpa_key_mgmt=WPA-PSK\n" + "wpa_pairwise=CCMP\n" + "ieee80211w=0\n" + "wpa_passphrase=maprocks2\n" + "disable_pmksa_caching=1\n" + "wpa_disable_eapol_key_retries=0\n" + "mesh_mode=bAP\n" + "sFourAddrMode=1\n" + "max_num_sta=1\n" + + "#bss=wlan0.2\n" + "#ctrl_interface=/var/run/hostapd\n" + "#ap_isolate=1\n" + "#ap_max_inactivity=60\n" + "#rrm_neighbor_report=1\n" + "#mode=ap\n" + "#bss_transition=1\n" + "#interworking=1\n" + "#disassoc_low_ack=1\n" + "#preamble=1\n" + "#wmm_enabled=1\n" + "#ignore_broadcast_ssid=0\n" + "#uapsd_advertisement_enabled=1\n" + "#mbo=1\n" + "#vendor_elements=dd050009860100\n" + "#vendor_vht=1\n" + "#auth_algs=1\n" + "#wpa=0\n" + "#bridge=br-lan\n" + "#bssid=02:9A:96:FB:59:13\n" + "#start_disabled=1\n" + + "bss=wlan0.3\n" + "ctrl_interface=/var/run/hostapd\n" + "ap_isolate=1\n" + "ap_max_inactivity=60\n" + "rrm_neighbor_report=1\n" + "bss_transition=1\n" + "interworking=1\n" + "disassoc_low_ack=1\n" + "preamble=1\n" + "wmm_enabled=1\n" + "ignore_broadcast_ssid=0\n" + "uapsd_advertisement_enabled=1\n" + "mbo=1\n" + "vendor_elements=dd050009860100\n" + "vendor_vht=1\n" + "auth_algs=1\n" + "wpa=0\n" + "bridge=br-lan\n" + "bssid=02:9A:96:FB:59:14\n" + "start_disabled=1\n" + "mode=ap\n"); + +void clean_start() +{ + // save the content of the string (start clean) + std::ofstream tmp(configuration_path + configuration_file_name); + tmp << configuration_content; + tmp.flush(); +} + +TEST(configuration_test, load) +{ + //// start prerequsite //// + + clean_start(); + + //// end prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + EXPECT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + EXPECT_TRUE(conf) << conf; +} + +TEST(configuration_test, store) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + // add a value to vap + conf.set_create_vap_value("wlan0.3", "was_i_stroed", "yes_you_were"); + EXPECT_TRUE(conf) << conf; + + // store + conf.store(); + EXPECT_TRUE(conf) << conf; +} + +TEST(configuration_test, set_string_vap_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + // replace existing value for existing key for existing vap + conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); + EXPECT_TRUE(conf) << conf; + + // add a key/value to exising vap + conf.set_create_vap_value("wlan0.3", "unit_test_ok", "true"); + EXPECT_TRUE(conf) << conf; + + // remove key/value from existing vap + conf.set_create_vap_value("wlan0.0", "vendor_elements", ""); + EXPECT_TRUE(conf) << conf; + + // set key/value for NON existing vap + conf.set_create_vap_value("no_such", "how_do_you_do", "i_am_doing_fine"); + EXPECT_FALSE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end test //// +} + +TEST(configuration_test, set_int_vap_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + // replace existing value for existing key for existing vap + conf.set_create_vap_value("wlan0.1", "ignore_broadcast_ssid", 42); + EXPECT_TRUE(conf) << conf; + + // add a key/value to exising vap + conf.set_create_vap_value("wlan0.3", "i_am_negative", -24); + EXPECT_TRUE(conf) << conf; + + // try to replace existing value for existing key for existing vap + // with NON-int value. we expect the value to be trancated here + // at the caller site + conf.set_create_vap_value("wlan0.2", "wmm_enabled", 333.444); + EXPECT_TRUE(conf) << conf; + + //// end test //// +} + +TEST(configuration_test, get_vap_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + std::string value; + + // get existing value for existing vap + value = conf.get_vap_value("wlan0.1", "wpa_passphrase"); + EXPECT_EQ(value, "maprocks2") << conf; + + // another check - existing value for existing vap + value = conf.get_vap_value("wlan0.1", "bssid"); + EXPECT_EQ(value, "02:9A:96:FB:59:12"); + + // get NON existing value for existing vap + value = conf.get_vap_value("wlan0.1", "does_not_exist"); + EXPECT_EQ(value, "") << conf; + + // try to get for NON existing vap + value = conf.get_vap_value("no_vap", "key"); + EXPECT_EQ(value, "") << conf; + + //// end test //// +} + +TEST(configuration_test, disable_all_ap) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + // disable by adding a key/value + auto disable_func = [&conf](const std::string vap) { + conf.set_create_vap_value(vap, "start_disabled", 1); + }; + + conf.for_all_ap_vaps(disable_func); + EXPECT_TRUE(conf) << conf; + + auto comment_func = [&conf](const std::string vap) { + conf.comment_vap(vap); + }; + + conf.for_all_ap_vaps(comment_func); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end prerequsite //// +} + +TEST(configuration_test, comment_vap) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + conf.comment_vap("wlan0.1"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end test //// +} + +TEST(configuration_test, uncomment_vap) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + // comment twice! + conf.comment_vap("wlan0.1"); + conf.comment_vap("wlan0.1"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + conf.uncomment_vap("wlan0.1"); + EXPECT_TRUE(conf) << conf; + + conf.uncomment_vap("wlan0.2"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + std::cerr << conf; +} + +} // namespace From fffd8df9681184463a9d439bce4b01fe7141f107 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 8 Jul 2020 11:51:35 +0300 Subject: [PATCH 04/28] prplmesh-hostapd: added cmake for prplmesh hostapd created CMakeLists.txt to build prplmest_hostapd library and its unit tests https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/beerocks/CMakeLists.txt b/common/beerocks/CMakeLists.txt index 0479907511..6347f7a547 100644 --- a/common/beerocks/CMakeLists.txt +++ b/common/beerocks/CMakeLists.txt @@ -1,10 +1,11 @@ add_subdirectory(bcl) -add_subdirectory(bwl) -add_subdirectory(tlvf) add_subdirectory(btl) +add_subdirectory(bwl) +add_subdirectory(hostapd) add_subdirectory(scripts) +add_subdirectory(tlvf) # BWL depends on BCL, so make sure it's built first add_dependencies(bwl bcl) add_dependencies(btlvf bcl) -add_dependencies(btl btlvf) \ No newline at end of file +add_dependencies(btl btlvf) From da8fc18d99f8211e53c6e315271ab15d94a21793 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 9 Jul 2020 12:54:28 +0300 Subject: [PATCH 05/28] prplmesh-hostapd: added set_create_vap_value() introducing: set_create_vap_value functionality and unit test also: few minor general issues: * a variable for the test file * interface to get the last message (not implemented yet) * change the variable name from error to message and fix accordingly in the code https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 54 +++++++++++++++++-- .../beerocks/hostapd/include/configuration.h | 22 +++++++- .../hostapd/unit_tests/configuration_test.cpp | 23 ++++++++ 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index adb50bb73e..896aedbe4c 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -59,18 +59,66 @@ bool Configuration::load() } } - m_error_description = strerror(errno); + m_last_message = strerror(errno); // if we've got to parsing vaps and no read errors, assume all is good m_ok = parsing_vaps && !ifs.bad(); - return m_ok; + + // return this as bool + return *this; +} + +bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, + const std::string &value) +{ + auto existing_vap = m_hostapd_config_vaps.find(vap); + + if (existing_vap == m_hostapd_config_vaps.end()) { + m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap; + m_ok = false; + return *this; + } + + // array of values for the requested vap + auto &vaps_values = existing_vap->second; + + // search for the key + std::string key_eq(key + "="); + auto it_str = + std::find_if(vaps_values.begin(), vaps_values.end(), [&key_eq](std::string str) -> bool { + return (str.compare(0, key_eq.length(), key_eq) == 0); + }); + + // we first delete the key, and if the requested value is non empty + // we push it to the end of the array + + // delete the key-value if found + if (it_str != vaps_values.end()) { + it_str = vaps_values.erase(it_str); + } else { + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was not found"; + } + + // when the new value is provided add the key back with that new value + if (value.length() != 0) { + vaps_values.push_back(key_eq + value); + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was (re)added"; + } else { + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was deleted"; + } + + m_ok = true; + return *this; } std::ostream &operator<<(std::ostream &o, const Configuration &conf) { o << "== configuration details ==\n" << "= ok: " << std::boolalpha << conf.m_ok << '\n' - << "= erorr text: " << conf.m_error_description << '\n' + << "= last message: " << conf.m_last_message << '\n' << "= file: " << conf.m_configuration_file << '\n' << "= head: " << '\n'; diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index cb01fbedbc..d7005ae39c 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -48,6 +48,24 @@ class Configuration { */ bool load(); + /** + * @brief set key/value for the given vap + * @details set the key/vale for the given vap, either replace or create. + * @return + * true - the vap exists, values were set. + * false - the given vap was not found + */ + bool set_create_vap_value(const std::string &vap, const std::string &key, + const std::string &value); + + /** + * @brief for debug: return the last internal message + * @details each action on this class changes its internal + * message (similar to errno) - for debug usage + * @return s string describing the last message + */ + const std::string &get_last_message() const; + private: std::string m_configuration_file; bool m_ok = false; @@ -62,7 +80,7 @@ class Configuration { // configuration file with the original key=value // e.g. the following lines in the configuration file: // bss=wlan0.1 <------------------------------ the key in the map: "wlan0.1" - // ctrl_interface=/var/run/hostapd <---------- each line is an element in the array of the map's value + // ctrl_interface=/var/run/hostapd <---------- each line is an element in the array of the map's value // ap_isolate=1 // ap_max_inactivity=60 // bss_transition=1 @@ -79,7 +97,7 @@ class Configuration { // { "wlan0.2" : ["ctrl_interface=/var/run/hosXXpd", "ap_isolate=1", "ap_max_inactivity=60", "bss_transition=0", "interworking=3"] } std::map> m_hostapd_config_vaps; - std::string m_error_description = "no error"; + std::string m_last_message = "all good"; // for logs friend std::ostream &operator<<(std::ostream &, const Configuration &); diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 653040a60a..46c774f20a 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -449,6 +449,29 @@ TEST(configuration_test, uncomment_vap) conf.store(); EXPECT_TRUE(conf) << conf; + //// end prerequsite //// + + //// start test //// + + // replace existing value for existing key for existing vap + conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); + EXPECT_TRUE(conf) << conf; + + // add a key/value to exising vap + conf.set_create_vap_value("wlan0.3", "unit_test_ok", "true"); + EXPECT_TRUE(conf) << conf; + + // remove key/value from existing vap + conf.set_create_vap_value("wlan0.0", "vendor_elements", ""); + EXPECT_TRUE(conf) << conf; + + // set key/value for NON existing vap + conf.set_create_vap_value("no_such", "how_do_you_do", "i_am_doing_fine"); + EXPECT_FALSE(conf) << conf; + + //// end test //// + + // for humans std::cerr << conf; } From 39ac463436481dbfb8c4bcffc4c88f3be1e153df Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 9 Jul 2020 13:24:15 +0300 Subject: [PATCH 06/28] prplmesh-hostapd: added set_create_vap_value() overload with int introducing: set_create_vap_value, int overload implemented the function using the string version added functionality and tests https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 13 ++++++-- .../beerocks/hostapd/include/configuration.h | 16 +++++++++ .../hostapd/unit_tests/configuration_test.cpp | 33 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 896aedbe4c..b66116817d 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -74,8 +74,9 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri auto existing_vap = m_hostapd_config_vaps.find(vap); if (existing_vap == m_hostapd_config_vaps.end()) { - m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap; - m_ok = false; + m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap + + " (requested to set: " + key + "/" + value + ")"; + m_ok = false; return *this; } @@ -114,6 +115,14 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri return *this; } +bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, + const int value) +{ + return set_create_vap_value(vap, key, std::to_string(value)); +} + +const std::string &Configuration::get_last_message() const { return m_last_message; } + std::ostream &operator<<(std::ostream &o, const Configuration &conf) { o << "== configuration details ==\n" diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index d7005ae39c..8cb73673c8 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -51,6 +51,10 @@ class Configuration { /** * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. + * @param + * - the vap to set values for (string) + * - the key to set its value (string) + * - the value (string) * @return * true - the vap exists, values were set. * false - the given vap was not found @@ -58,6 +62,18 @@ class Configuration { bool set_create_vap_value(const std::string &vap, const std::string &key, const std::string &value); + /** + * @brief set key/value for the given vap + * @details set the key/vale for the given vap, either replace or create. + * @param + * - the vap to set values for (string) + * - the key to set its value (string) + * - the value (int) + * @return + * true - the vap exists, values were set. + * false - the given vap was not found + */ + bool set_create_vap_value(const std::string &vap, const std::string &key, const int value); /** * @brief for debug: return the last internal message * @details each action on this class changes its internal diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 46c774f20a..25bd4a4488 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -470,6 +470,39 @@ TEST(configuration_test, uncomment_vap) EXPECT_FALSE(conf) << conf; //// end test //// +} + +TEST(configuration_test, set_int_vap_values) +{ + //// start prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_file); + EXPECT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + EXPECT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + // replace existing value for existing key for existing vap + conf.set_create_vap_value("wlan0.1", "ignore_broadcast_ssid", 42); + EXPECT_TRUE(conf) << conf; + + // add a key/value to exising vap + conf.set_create_vap_value("wlan0.3", "i_am_negative", -24); + EXPECT_TRUE(conf) << conf; + + // try to replace existing value for existing key for existing vap + // with NON-int value. we expect the value to be trancated here + // at the caller site + conf.set_create_vap_value("wlan0.2", "wmm_enabled", 333.444); + EXPECT_TRUE(conf) << conf; + + //// end test //// // for humans std::cerr << conf; From e8d453ce288888d213ea2104b1c88547370741c7 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 9 Jul 2020 13:45:17 +0300 Subject: [PATCH 07/28] prplmesh-hostapd: added store() introducing: store stores internal configuration into the same file it was loaded from, effectively changing the configuration. added functionality and tests also: * chnaged prerequisite in tests from EXPECT to ASSERT - as we can't continue without it https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 30 +++++++++++++++++++ .../beerocks/hostapd/include/configuration.h | 14 +++++---- .../hostapd/unit_tests/configuration_test.cpp | 13 +++----- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index b66116817d..6a9319fdd8 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -68,6 +68,36 @@ bool Configuration::load() return *this; } +bool Configuration::store() +{ + std::ofstream out_file(m_configuration_file, std::ofstream::out | std::ofstream::trunc); + + // store the head + for (const auto &line : m_hostapd_config_head) { + out_file << line << "\n"; + } + + // store the vaps + for (auto &vap : m_hostapd_config_vaps) { + + // add empty line for readability + out_file << "\n"; + + for (auto &line : vap.second) { + out_file << line << "\n"; + } + } + + // close the file + out_file.close(); + if (out_file.fail()) { + m_last_message = strerror(errno); + m_ok = false; + } + + return *this; +} + bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, const std::string &value) { diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 8cb73673c8..d652ce0b5e 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -39,15 +39,17 @@ class Configuration { * @brief load the configuration * @details loads the file this object is constructed with * @return *this (as bool, see above) - * e.g. - * Configuratio conf("hostapd.conf"); - * if (!conf.load()) { - * // handle error state - * } - * */ bool load(); + /** + * @brief stores the configuration + * @details stores the internal representation of the configuration + * into the file it was loaded from, effectively changing the configuration + * @return *this (as bool, see above) + */ + bool store(); + /** * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 25bd4a4488..d654471018 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -210,15 +210,14 @@ TEST(configuration_test, store) // // clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file conf.load(); ASSERT_TRUE(conf) << conf; - + //// end prerequsite //// - + // add a value to vap conf.set_create_vap_value("wlan0.3", "was_i_stroed", "yes_you_were"); EXPECT_TRUE(conf) << conf; @@ -449,10 +448,6 @@ TEST(configuration_test, uncomment_vap) conf.store(); EXPECT_TRUE(conf) << conf; - //// end prerequsite //// - - //// start test //// - // replace existing value for existing key for existing vap conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); EXPECT_TRUE(conf) << conf; @@ -478,11 +473,11 @@ TEST(configuration_test, set_int_vap_values) // construct a configuration prplmesh::hostapd::config::Configuration conf(configuration_file); - EXPECT_FALSE(conf) << conf; + ASSERT_FALSE(conf) << conf; // load the dummy configuration file conf.load(); - EXPECT_TRUE(conf) << conf; + ASSERT_TRUE(conf) << conf; //// end prerequsite //// From 589695b7f7a4d0ab419478006e775f2c1dec3e84 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 9 Jul 2020 17:52:45 +0300 Subject: [PATCH 08/28] prplmesh-hostapd: added get_vap_value() introducing: get_vap_value added the ability to get a value for a specific vap with a given key. added functionality and tests https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 31 +++++++++++++ .../beerocks/hostapd/include/configuration.h | 14 +++++- .../hostapd/unit_tests/configuration_test.cpp | 46 +++++++++++++++++-- 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 6a9319fdd8..dda5addef4 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -151,6 +151,37 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri return set_create_vap_value(vap, key, std::to_string(value)); } +std::string Configuration::get_vap_value(const std::string &vap, const std::string &key) +{ + // search for the requested vap + auto existing_vap = m_hostapd_config_vaps.find(vap); + + if (existing_vap == m_hostapd_config_vaps.end()) { + m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap + + " (requested key: " + key + ")"; + m_ok = false; + return ""; + } + + // search for the key + std::string key_eq(key + "="); + auto it_str = std::find_if(existing_vap->second.begin(), existing_vap->second.end(), + [&key_eq](std::string str) -> bool { + return (str.compare(0, key_eq.length(), key_eq) == 0); + }); + + if (it_str == existing_vap->second.end()) { + m_last_message = std::string(__FUNCTION__) + + " couldn't find requested key for vap: " + vap + "; requested key: " + key; + m_ok = false; + return ""; + } + + m_ok = true; + // return from the just after the '=' sign to the end of the string + return it_str->substr(key_eq.length()); +} + const std::string &Configuration::get_last_message() const { return m_last_message; } std::ostream &operator<<(std::ostream &o, const Configuration &conf) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index d652ce0b5e..0ebb53b72e 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -54,7 +54,7 @@ class Configuration { * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. * @param - * - the vap to set values for (string) + * - the vap to set the value for (string) * - the key to set its value (string) * - the value (string) * @return @@ -68,7 +68,7 @@ class Configuration { * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. * @param - * - the vap to set values for (string) + * - the vap to set the value for (string) * - the key to set its value (string) * - the value (int) * @return @@ -76,6 +76,16 @@ class Configuration { * false - the given vap was not found */ bool set_create_vap_value(const std::string &vap, const std::string &key, const int value); + + /** + * @brief get the value of the given key for the given vap + * @param + * - the vap to get the value for (string) + * - the key to get its value (string) + * @return a string with the value or empty if not found + */ + std::string get_vap_value(const std::string &vap, const std::string &key); + /** * @brief for debug: return the last internal message * @details each action on this class changes its internal diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index d654471018..84b5c974c9 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -207,7 +207,7 @@ TEST(configuration_test, store) //// start prerequsite //// // save the content of the string (start clean) - // // clean_start(); + // clean_start(); // construct a configuration ASSERT_FALSE(conf) << conf; @@ -215,9 +215,9 @@ TEST(configuration_test, store) // load the dummy configuration file conf.load(); ASSERT_TRUE(conf) << conf; - + //// end prerequsite //// - + // add a value to vap conf.set_create_vap_value("wlan0.3", "was_i_stroed", "yes_you_were"); EXPECT_TRUE(conf) << conf; @@ -498,9 +498,45 @@ TEST(configuration_test, set_int_vap_values) EXPECT_TRUE(conf) << conf; //// end test //// +} + +TEST(configuration_test, get_vap_values) +{ + //// start prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_file); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + std::string value; + + // get existing value for existing vap + value = conf.get_vap_value("wlan0.1", "wpa_passphrase"); + EXPECT_EQ(value, "maprocks2") << conf; + + // another check - existing value for existing vap + value = conf.get_vap_value("wlan0.1", "bssid"); + EXPECT_EQ(value, "02:9A:96:FB:59:12"); + + // get NON existing value for existing vap + value = conf.get_vap_value("wlan0.1", "does_not_exist"); + EXPECT_EQ(value, "") << conf; + + // try to get for NON existing vap + value = conf.get_vap_value("no_vap", "key"); + EXPECT_EQ(value, "") << conf; + + //// end test //// // for humans - std::cerr << conf; + std::cerr << "received value: " << value << '\n'; } - } // namespace From 334ddef244c7929b49339624d0981107891d768c Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Sun, 12 Jul 2020 13:24:34 +0300 Subject: [PATCH 09/28] prplmesh-hostapd: added disable_all_ap_vaps() added functionality to disable all vaps that are access-points (as opposed to STA for example). the code mimics the existing functionality taken from ap_wlan_hal_dwpal.cpp. added funcvtionlity and unit test note: the example unit-test configuration file did not contain any vap with the mode= line, therefore it was added few of this. also: it was discovered that it is OK to look for non-exiting key within vap's configuration and the code was changed accordingly https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 16 ++++++++++- .../beerocks/hostapd/include/configuration.h | 7 +++++ .../hostapd/unit_tests/configuration_test.cpp | 27 +++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index dda5addef4..7117530fc0 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -159,6 +159,7 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri if (existing_vap == m_hostapd_config_vaps.end()) { m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap + " (requested key: " + key + ")"; + // it is an error to ask for non existing vap m_ok = false; return ""; } @@ -173,7 +174,9 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri if (it_str == existing_vap->second.end()) { m_last_message = std::string(__FUNCTION__) + " couldn't find requested key for vap: " + vap + "; requested key: " + key; - m_ok = false; + + // it ok not to find the requested key + m_ok = true; return ""; } @@ -182,6 +185,17 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri return it_str->substr(key_eq.length()); } +void Configuration::disable_all_ap_vaps() { + for_each( + m_hostapd_config_vaps.begin(), + m_hostapd_config_vaps.end(), + [this](const std::pair> &vap) { + if ( get_vap_value(vap.first, "mode") == "ap" ) { + set_create_vap_value(vap.first, "start_disabled", "1"); + } + }); +} + const std::string &Configuration::get_last_message() const { return m_last_message; } std::ostream &operator<<(std::ostream &o, const Configuration &conf) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 0ebb53b72e..d46cfd65e8 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -86,6 +86,13 @@ class Configuration { */ std::string get_vap_value(const std::string &vap, const std::string &key); + /** + * @brief disable all ap vaps + * @details disable all vaps that thier mode is "ap", + * (leaving STAs vaps untouched for example) + */ + void disable_all_ap_vaps(); + /** * @brief for debug: return the last internal message * @details each action on this class changes its internal diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 84b5c974c9..b7295b5d87 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -535,8 +535,31 @@ TEST(configuration_test, get_vap_values) EXPECT_EQ(value, "") << conf; //// end test //// +} + + +TEST(configuration_test, diable_all_ap) +{ + //// start prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_file); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// - // for humans - std::cerr << "received value: " << value << '\n'; + //// start test //// + + conf.disable_all_ap_vaps(); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end test //// } } // namespace From 90a228e85e863fa7c3bbc0e2bc48d8c7b1bb6dc7 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Sun, 12 Jul 2020 17:25:13 +0300 Subject: [PATCH 10/28] prplmesh-hostapd: added private help functions added private helper functions for functions changed all usages accordingly https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 108 ++++++++++++------ .../beerocks/hostapd/include/configuration.h | 27 ++++- 2 files changed, 97 insertions(+), 38 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 7117530fc0..21f55898c8 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -15,7 +15,7 @@ namespace prplmesh { namespace hostapd { namespace config { -Configuration::Configuration(const std::string& file_name) : m_configuration_file(file_name) {} +Configuration::Configuration(const std::string &file_name) : m_configuration_file(file_name) {} Configuration::operator bool() const { return m_ok; } @@ -26,6 +26,11 @@ bool Configuration::load() // loading the file relays on the expected format // otherwise the load fails + // for cases when load is called more than once, we + // first clear internal data + m_hostapd_config_head.clear(); + m_hostapd_config_vaps.clear(); + std::ifstream ifs(m_configuration_file); std::string line; @@ -95,37 +100,32 @@ bool Configuration::store() m_ok = false; } + m_ok = true; return *this; } bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, const std::string &value) { - auto existing_vap = m_hostapd_config_vaps.find(vap); - - if (existing_vap == m_hostapd_config_vaps.end()) { - m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap + - " (requested to set: " + key + "/" + value + ")"; - m_ok = false; - return *this; + // search for the requested vap + auto find_vap = get_vap(std::string(__FUNCTION__) + " key/value: " + key + '/' + value, vap); + if (!std::get<0>(find_vap)) { + return false; } + auto existing_vap = std::get<1>(find_vap); + bool existing_vap_commented = existing_vap->front()[0] == '#'; - // array of values for the requested vap - auto &vaps_values = existing_vap->second; - - // search for the key std::string key_eq(key + "="); - auto it_str = - std::find_if(vaps_values.begin(), vaps_values.end(), [&key_eq](std::string str) -> bool { - return (str.compare(0, key_eq.length(), key_eq) == 0); - }); + auto it_str = std::find_if( + existing_vap->begin(), existing_vap->end(), + [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); // we first delete the key, and if the requested value is non empty // we push it to the end of the array // delete the key-value if found - if (it_str != vaps_values.end()) { - it_str = vaps_values.erase(it_str); + if (it_str != existing_vap->end()) { + it_str = existing_vap->erase(it_str); } else { m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was not found"; @@ -133,7 +133,11 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri // when the new value is provided add the key back with that new value if (value.length() != 0) { - vaps_values.push_back(key_eq + value); + if (existing_vap_commented) { + existing_vap->push_back('#' + key_eq + value); + } else { + existing_vap->push_back(key_eq + value); + } m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was (re)added"; } else { @@ -154,35 +158,30 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri std::string Configuration::get_vap_value(const std::string &vap, const std::string &key) { // search for the requested vap - auto existing_vap = m_hostapd_config_vaps.find(vap); - - if (existing_vap == m_hostapd_config_vaps.end()) { - m_last_message = std::string(__FUNCTION__) + " couldn't find requested vap: " + vap + - " (requested key: " + key + ")"; - // it is an error to ask for non existing vap - m_ok = false; + auto find_vap = get_vap(std::string(__FUNCTION__), vap); + if (!std::get<0>(find_vap)) { return ""; } + const auto& existing_vap = std::get<1>(find_vap); + // + // from now on this function is ok with all situations + // (e.g. not finding the requested key) + m_ok = true; // search for the key std::string key_eq(key + "="); - auto it_str = std::find_if(existing_vap->second.begin(), existing_vap->second.end(), - [&key_eq](std::string str) -> bool { - return (str.compare(0, key_eq.length(), key_eq) == 0); - }); + auto it_str = std::find_if( + existing_vap->begin(), existing_vap->end(), + [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); - if (it_str == existing_vap->second.end()) { + if (it_str == existing_vap->end()) { m_last_message = std::string(__FUNCTION__) + " couldn't find requested key for vap: " + vap + "; requested key: " + key; - - // it ok not to find the requested key - m_ok = true; return ""; } - m_ok = true; // return from the just after the '=' sign to the end of the string - return it_str->substr(key_eq.length()); + return it_str->substr(it_str->find('=') + 1); } void Configuration::disable_all_ap_vaps() { @@ -198,6 +197,43 @@ void Configuration::disable_all_ap_vaps() { const std::string &Configuration::get_last_message() const { return m_last_message; } +std::tuple *> +Configuration::get_vap(const std::string &calling_function, const std::string &vap) +{ + // search for the requested vap + auto existing_vap = m_hostapd_config_vaps.find(vap); + + if (existing_vap == m_hostapd_config_vaps.end()) { + m_last_message = calling_function + " couldn't find requested vap: " + vap; + m_ok = false; + return std::make_tuple(false, nullptr); + } + + m_ok = true; + return std::make_tuple(true, &existing_vap->second); +} + +bool Configuration::is_key_in_line(const std::string &line, const std::string &key) const +{ + // we need to make sure when searching for example + // for "ssid", to ignore cases like: + // multi_ap_backhaul_ssid="Multi-AP-24G-2" + // ^^^^^ + // bssid=02:9A:96:FB:59:11 + // ^^^^^ + // and we need to take into consideration + // that the key might be or might not be commented. + // so the search algorithm is: + // - find the requested key and + // - make sure it is either on the first position + // or it has a comment sign just before it + auto found_pos = line.rfind(key); + bool ret = found_pos != std::string::npos && (found_pos == 0 || line.at(found_pos - 1) == '#'); + +// std::cerr << "line: " << line << "; key: " << key << "; ret: " << std::boolalpha << ret << '\n'; + return ret; +} + std::ostream &operator<<(std::ostream &o, const Configuration &conf) { o << "== configuration details ==\n" diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index d46cfd65e8..d210f47ffa 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -21,7 +21,7 @@ class Configuration { * @brief construct a Configurationn object * @param file_name the name of the configuration file. */ - Configuration(const std::string& file_name); + Configuration(const std::string &file_name); ~Configuration() = default; Configuration(const Configuration &) = default; @@ -97,10 +97,33 @@ class Configuration { * @brief for debug: return the last internal message * @details each action on this class changes its internal * message (similar to errno) - for debug usage - * @return s string describing the last message + * @return string describing the last message */ const std::string &get_last_message() const; +private: + /** + * @brief helper: get exiting vap + * @details any function that works on a specific vap does + * the same: search the vap, set a variable when found + * and set an error state when not found, this function + * does it instead of copy/paste the same code + * @param the calling function, string (to report error) + * @param the requested vap, string + * @return tuple*> (found/not found, a + * pointer to the vap's array) + */ + std::tuple *> get_vap(const std::string &calling_function, + const std::string &vap); + + /** + * @brief helper: check if the line contains the key + * @param line in the format key=value or commented ###key=value + * @param the requested key, string + * @return true/false if the line contains the key + */ + bool is_key_in_line(const std::string &line, const std::string &key) const; + private: std::string m_configuration_file; bool m_ok = false; From bdcf932c9722f5b384ba574f5927456e3157752b Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Sun, 12 Jul 2020 17:48:41 +0300 Subject: [PATCH 11/28] prplmesh-hostapd: added comment_vap() comment out (disable) the given vap by adding a comment (#) to it and to all keys related to this vap added functionality and unit test also: clang format chages https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 20 ++++++++++--------- .../beerocks/hostapd/include/configuration.h | 16 +++++++++++---- .../hostapd/unit_tests/configuration_test.cpp | 20 ++++++++++++++++--- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 21f55898c8..8ca1caee8e 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -184,15 +184,17 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri return it_str->substr(it_str->find('=') + 1); } -void Configuration::disable_all_ap_vaps() { - for_each( - m_hostapd_config_vaps.begin(), - m_hostapd_config_vaps.end(), - [this](const std::pair> &vap) { - if ( get_vap_value(vap.first, "mode") == "ap" ) { - set_create_vap_value(vap.first, "start_disabled", "1"); - } - }); +void Configuration::comment_vap(const std::string &vap) +{ + // search for the requested vap + auto find_vap = get_vap(std::string(__FUNCTION__), vap); + if (!std::get<0>(find_vap)) { + return; + } + const auto &existing_vap = std::get<1>(find_vap); + + std::for_each(existing_vap->begin(), existing_vap->end(), + [](std::string &line) { line.insert(0, 1, '#'); }); } const std::string &Configuration::get_last_message() const { return m_last_message; } diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index d210f47ffa..771c0e7708 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -87,11 +87,19 @@ class Configuration { std::string get_vap_value(const std::string &vap, const std::string &key); /** - * @brief disable all ap vaps - * @details disable all vaps that thier mode is "ap", - * (leaving STAs vaps untouched for example) + * @brief disables vap by adding a comment to it + * e.g: + * before: + * bss=wlan0_0 + * ssid=test2 + * bss=wlan0_1 + * after: + * #bss=wlan0_0 + * #ssid=test2 + * bss=wlan0_1 + * */ - void disable_all_ap_vaps(); + void comment_vap(const std::string &vap); /** * @brief for debug: return the last internal message diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index b7295b5d87..4b60826152 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -537,7 +537,6 @@ TEST(configuration_test, get_vap_values) //// end test //// } - TEST(configuration_test, diable_all_ap) { //// start prerequsite //// @@ -551,10 +550,25 @@ TEST(configuration_test, diable_all_ap) ASSERT_TRUE(conf) << conf; //// end prerequsite //// +} + +TEST(configuration_test, disable_vap) +{ + //// start prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_file); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// //// start test //// - - conf.disable_all_ap_vaps(); + + conf.comment_vap("wlan0.1"); EXPECT_TRUE(conf) << conf; conf.store(); From 3984d9d2354ddffbf49ba5688315852a0bee291f Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 13 Jul 2020 11:56:32 +0300 Subject: [PATCH 12/28] prplmesh-hostapd: added comment ability load, search and store comented lines in the file. this ability is needed for disabling vaps in the "up stream" version of hostapd. the intel version disables vaps by adding the line "start_disabled=1" https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 8ca1caee8e..9146f3ac46 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -25,18 +25,25 @@ bool Configuration::load() // the expected format of hostapd configuration file // loading the file relays on the expected format // otherwise the load fails + + // for cases when load is called more than once, we + // first clear internal data + m_hostapd_config_head.clear(); + m_hostapd_config_vaps.clear(); // for cases when load is called more than once, we // first clear internal data m_hostapd_config_head.clear(); m_hostapd_config_vaps.clear(); + // strat reading std::ifstream ifs(m_configuration_file); std::string line; bool parsing_vaps = false; std::string cur_vap; + // go over line by line in the file while (getline(ifs, line)) { // skip empty lines @@ -45,14 +52,15 @@ bool Configuration::load() } // check if the string belongs to a vap config part and capture which one. const std::string bss_eq("bss="); - if (line.compare(0, bss_eq.length(), bss_eq) == 0) { + auto end_comment = line.find_first_not_of('#'); + if (line.compare(end_comment, bss_eq.length(), bss_eq) == 0) { // from now on we are in the vaps area, all // key/value pairs belongs to vaps parsing_vaps = true; // copy the bss (vap) value - cur_vap.assign(line, bss_eq.length(), std::string::npos); + cur_vap.assign(line, end_comment + bss_eq.length(), std::string::npos); } // if not a vap line store it in the header part of the config, @@ -168,7 +176,10 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri // (e.g. not finding the requested key) m_ok = true; - // search for the key + // search for the key from the back of the string + // ignore comments this way + // e.g: + // ###bssid=11:22:ff:ee:aa std::string key_eq(key + "="); auto it_str = std::find_if( existing_vap->begin(), existing_vap->end(), @@ -202,8 +213,13 @@ const std::string &Configuration::get_last_message() const { return m_last_messa std::tuple *> Configuration::get_vap(const std::string &calling_function, const std::string &vap) { - // search for the requested vap - auto existing_vap = m_hostapd_config_vaps.find(vap); + // search for the requested vap - ignore comments + // by searching from the back of the saved vap (rfind) + auto existing_vap = + std::find_if(m_hostapd_config_vaps.begin(), m_hostapd_config_vaps.end(), + [&vap](const std::pair> ¤t_vap) { + return current_vap.first.rfind(vap) != std::string::npos; + }); if (existing_vap == m_hostapd_config_vaps.end()) { m_last_message = calling_function + " couldn't find requested vap: " + vap; From e58b302bc8fc102d31d877d7f5d82832b3eee427 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 13 Jul 2020 12:02:55 +0300 Subject: [PATCH 13/28] prplmesh-hostapd: added uncomment_vap() uncomment (enable) the given vap by removing all comment signes (###) from it and from all keys related to this vap added functionality and unit test https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 17 ++++++++ .../beerocks/hostapd/include/configuration.h | 15 +++++++ .../hostapd/unit_tests/configuration_test.cpp | 43 ++++++++++++++++++- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 9146f3ac46..bd39d6ffc4 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -208,6 +208,23 @@ void Configuration::comment_vap(const std::string &vap) [](std::string &line) { line.insert(0, 1, '#'); }); } +void Configuration::uncomment_vap(const std::string &vap) +{ + // search for the requested vap + auto find_vap = get_vap(std::string(__FUNCTION__), vap); + if (!std::get<0>(find_vap)) { + return; + } + const auto &existing_vap = std::get<1>(find_vap); + + std::for_each(existing_vap->begin(), existing_vap->end(), [](std::string &line) { + auto end_comment = line.find_first_not_of('#'); + if (std::string::npos != end_comment) { + line.erase(0, end_comment); + }; + }); +} + const std::string &Configuration::get_last_message() const { return m_last_message; } std::tuple *> diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 771c0e7708..1a5489e606 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -101,6 +101,21 @@ class Configuration { */ void comment_vap(const std::string &vap); + /** + * @brief enables vap by removing comments from it + * e.g: + * before: + * #bss=wlan0_0 + * ##ssid=test2 + * bss=wlan0_1 + * after: + * bss=wlan0_0 + * ssid=test2 + * bss=wlan0_1 + * + */ + void uncomment_vap(const std::string &vap); + /** * @brief for debug: return the last internal message * @details each action on this class changes its internal diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 4b60826152..b34ee792b2 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -552,7 +552,7 @@ TEST(configuration_test, diable_all_ap) //// end prerequsite //// } -TEST(configuration_test, disable_vap) +TEST(configuration_test, comment_vap) { //// start prerequsite //// @@ -576,4 +576,45 @@ TEST(configuration_test, disable_vap) //// end test //// } + +TEST(configuration_test, uncomment_vap) +{ + //// start prerequsite //// + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_file); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + // comment twice! + conf.comment_vap("wlan0.1"); + conf.comment_vap("wlan0.1"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + conf.uncomment_vap("wlan0.1"); + EXPECT_TRUE(conf) << conf; + + conf.uncomment_vap("wlan0.2"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end test //// +} + } // namespace From 8be8fa674fad830d62f2a9eae524a73a02db6d26 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 13 Jul 2020 13:01:17 +0300 Subject: [PATCH 14/28] prplmesh-hostapd: added for_all_ap_vaps() introducing: a function that goes all over all vaps which their mode is "ap" and apply the input function to them. may be used for example to disable all vaps. added functionality and tested by: - adding a parameter "start_disabled=1" to all. - commenting out https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/include/configuration.h | 17 +++++++++++++++++ .../hostapd/unit_tests/configuration_test.cpp | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 1a5489e606..703c972b44 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -116,6 +116,13 @@ class Configuration { */ void uncomment_vap(const std::string &vap); + /** + * @brief apply func to all ap vaps + * @details apply func to all vaps that thier mode is "ap" + * (leaving STAs vaps untouched for example). + */ + template void for_all_ap_vaps(func); + /** * @brief for debug: return the last internal message * @details each action on this class changes its internal @@ -184,6 +191,16 @@ class Configuration { friend std::ostream &operator<<(std::ostream &, const Configuration &); }; +template void Configuration::for_all_ap_vaps(func f) +{ + for_each(m_hostapd_config_vaps.begin(), m_hostapd_config_vaps.end(), + [this, &f](const std::pair> &vap) mutable { + if (get_vap_value(vap.first, "mode") == "ap") { + f(vap.first); + } + }); +} + // for logs std::ostream &operator<<(std::ostream &, const Configuration &); diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index b34ee792b2..81d62b2420 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -549,6 +549,23 @@ TEST(configuration_test, diable_all_ap) conf.load(); ASSERT_TRUE(conf) << conf; + auto disable_func = [&conf](const std::string vap) { + conf.set_create_vap_value(vap, "start_disabled", 1); + }; + + conf.for_all_ap_vaps(disable_func); + EXPECT_TRUE(conf) << conf; + + auto comment_func = [&conf](const std::string vap) { + conf.comment_vap(vap); + }; + + conf.for_all_ap_vaps(comment_func); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + //// end prerequsite //// } From 465cfbc5a2da0739733ac14d8d0c117eea1e257e Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 13 Jul 2020 13:55:24 +0300 Subject: [PATCH 15/28] prplmesh-hostapd: added for_all_ap_vaps() with user iterator There is a need to go on both the ap-vaps configured in hostapd and in paralell go over the given configuration (non-hostapd structure) The added functionality gives hostapd configuration class the responsibility of iterating over the vaps _and_ increment the user-iterator. the result is that the user's function is called with the next vap _and_ the next element in its given list. it is the user responsibility however to make sure that its container has at least as the number of vaps. otherwise for_all_ap_vaps() would increment the iterator passed the end of the user's container tested by implementing the non iterator version of for_all_ap_vaps() in term of the iterator version, with a dummy iterator https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 11 +++---- .../beerocks/hostapd/include/configuration.h | 29 +++++++++++++++++-- .../hostapd/unit_tests/configuration_test.cpp | 13 +++++++-- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index bd39d6ffc4..31e3e0e7f8 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -123,6 +123,10 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri auto existing_vap = std::get<1>(find_vap); bool existing_vap_commented = existing_vap->front()[0] == '#'; + // search for the key from the back of the string + // ignore comments this way + // e.g: + // ###bssid=11:22:ff:ee:aa std::string key_eq(key + "="); auto it_str = std::find_if( existing_vap->begin(), existing_vap->end(), @@ -176,10 +180,8 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri // (e.g. not finding the requested key) m_ok = true; - // search for the key from the back of the string - // ignore comments this way - // e.g: - // ###bssid=11:22:ff:ee:aa + const auto &existing_vap = std::get<1>(find_vap); + std::string key_eq(key + "="); auto it_str = std::find_if( existing_vap->begin(), existing_vap->end(), @@ -265,7 +267,6 @@ bool Configuration::is_key_in_line(const std::string &line, const std::string &k auto found_pos = line.rfind(key); bool ret = found_pos != std::string::npos && (found_pos == 0 || line.at(found_pos - 1) == '#'); -// std::cerr << "line: " << line << "; key: " << key << "; ret: " << std::boolalpha << ret << '\n'; return ret; } diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 703c972b44..319c65690b 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -6,6 +6,7 @@ * See LICENSE file for more details. */ +#include #include #include #include @@ -123,6 +124,16 @@ class Configuration { */ template void for_all_ap_vaps(func); + /** + * @brief apply func to all ap vaps, increment the given iterator + * with each call to func: f(current_vap, ++user_itertor) + * func interface: template f(const std::string& vap, iter user_iterator) + * @details apply func to all vaps that thier mode is "ap" while incrementing + * the user iterator. this functionality enables iterating over two containers + * in paralel: the ap-vaps and the user container. + */ + template void for_all_ap_vaps(func, iter current, const iter end); + /** * @brief for debug: return the last internal message * @details each action on this class changes its internal @@ -192,11 +203,25 @@ class Configuration { }; template void Configuration::for_all_ap_vaps(func f) +{ + auto f_with_iter = [&f](const std::string &vap, int iter) { f(vap); }; + + int dummy(0); + for_all_ap_vaps(f_with_iter, dummy, 10); +} + +template +void Configuration::for_all_ap_vaps(func f, iter current_iter, const iter end) { for_each(m_hostapd_config_vaps.begin(), m_hostapd_config_vaps.end(), - [this, &f](const std::pair> &vap) mutable { + [this, &f, ¤t_iter, + &end](const std::pair> &vap) { if (get_vap_value(vap.first, "mode") == "ap") { - f(vap.first); + if (end == current_iter) { + f(vap.first, end); + } else { + f(vap.first, current_iter++); + } } }); } diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 81d62b2420..067ece960b 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -367,13 +367,20 @@ TEST(configuration_test, disable_all_ap) conf.for_all_ap_vaps(disable_func); EXPECT_TRUE(conf) << conf; - auto comment_func = [&conf](const std::string vap) { - conf.comment_vap(vap); - }; + + // disable by commenting + auto comment_func = [&conf](const std::string vap) { conf.comment_vap(vap); }; conf.for_all_ap_vaps(comment_func); EXPECT_TRUE(conf) << conf; + // store + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end prerequsite //// +} + conf.store(); EXPECT_TRUE(conf) << conf; From 757a5c07862c89b4e9d5efa98367789a1f43f279 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 15 Jul 2020 12:33:42 +0300 Subject: [PATCH 16/28] prplmesh-hostapd: added test enable_all_vaps() a test that goes all over all vaps and enables them https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- .../hostapd/unit_tests/configuration_test.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 067ece960b..f903ec65ec 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -381,6 +381,36 @@ TEST(configuration_test, disable_all_ap) //// end prerequsite //// } +TEST(configuration_test, enable_all_ap) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + // enable by removing key/value + auto enable_func = [&conf](const std::string vap) { + conf.set_create_vap_value(vap, "start_disabled", ""); + }; + + conf.for_all_ap_vaps(enable_func); + EXPECT_TRUE(conf) << conf; + + // enable by uncommenting + auto uncomment_func = [&conf](const std::string vap) { conf.uncomment_vap(vap); }; + + conf.for_all_ap_vaps(uncomment_func); + EXPECT_TRUE(conf) << conf; + + // store conf.store(); EXPECT_TRUE(conf) << conf; From 93b265a9ee89cf35d7542b5cbdbb80112d409920 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 15 Jul 2020 12:35:15 +0300 Subject: [PATCH 17/28] prplmesh-hostapd: added test iterate-both-containers testing the ability to iterate over the user container and the hostapd's ap-vaps in paralel giving the user the ability to set parametrs based on its own container and the hostapd container https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- .../hostapd/unit_tests/configuration_test.cpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index f903ec65ec..bd7c234111 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -671,4 +671,46 @@ TEST(configuration_test, uncomment_vap) //// end test //// } +TEST(configuration_test, itererate_both_containers) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + //clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + //// start test //// + + std::vector ssid = {"00:11:22:33:44:55", "ab:cd:ef:01:02:03"}; + + auto set_ssid = [&conf, &ssid](const std::string vap, std::vector::iterator it) { + if (it != ssid.end()) { + // as long as we didn't finish our containr + // we set the given's vap ssid + conf.set_create_vap_value(vap, "ssid", *it); + } else { + // when we done with our container, we simply + // comment the rest of the vaps + conf.comment_vap(vap); + } + }; + + conf.for_all_ap_vaps(set_ssid, ssid.begin(), ssid.end()); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; + + //// end test //// +} + } // namespace From b2ec17554c33bbb9fd5efb1620cddb23d9634570 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 15 Jul 2020 13:42:56 +0300 Subject: [PATCH 18/28] prplmesh-hostapd: few cleanups after rebase rebase --autosquash -i origin/master introduced issues that I couldn't figure out how to fix with rebasing again. gave up and added this fix commit. https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 4 +- .../hostapd/unit_tests/configuration_test.cpp | 171 +----------------- 2 files changed, 5 insertions(+), 170 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index 31e3e0e7f8..c06e693709 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -175,13 +175,11 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri return ""; } const auto& existing_vap = std::get<1>(find_vap); - // + // from now on this function is ok with all situations // (e.g. not finding the requested key) m_ok = true; - const auto &existing_vap = std::get<1>(find_vap); - std::string key_eq(key + "="); auto it_str = std::find_if( existing_vap->begin(), existing_vap->end(), diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index bd7c234111..615d0121bf 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -209,6 +209,10 @@ TEST(configuration_test, store) // save the content of the string (start clean) // clean_start(); + // load the dummy configuration file + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + EXPECT_FALSE(conf) << conf; + // construct a configuration ASSERT_FALSE(conf) << conf; @@ -504,173 +508,6 @@ TEST(configuration_test, uncomment_vap) //// end test //// } -TEST(configuration_test, set_int_vap_values) -{ - //// start prerequsite //// - - // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_file); - ASSERT_FALSE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - //// end prerequsite //// - - //// start test //// - - // replace existing value for existing key for existing vap - conf.set_create_vap_value("wlan0.1", "ignore_broadcast_ssid", 42); - EXPECT_TRUE(conf) << conf; - - // add a key/value to exising vap - conf.set_create_vap_value("wlan0.3", "i_am_negative", -24); - EXPECT_TRUE(conf) << conf; - - // try to replace existing value for existing key for existing vap - // with NON-int value. we expect the value to be trancated here - // at the caller site - conf.set_create_vap_value("wlan0.2", "wmm_enabled", 333.444); - EXPECT_TRUE(conf) << conf; - - //// end test //// -} - -TEST(configuration_test, get_vap_values) -{ - //// start prerequsite //// - - // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_file); - ASSERT_FALSE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - //// end prerequsite //// - - //// start test //// - - std::string value; - - // get existing value for existing vap - value = conf.get_vap_value("wlan0.1", "wpa_passphrase"); - EXPECT_EQ(value, "maprocks2") << conf; - - // another check - existing value for existing vap - value = conf.get_vap_value("wlan0.1", "bssid"); - EXPECT_EQ(value, "02:9A:96:FB:59:12"); - - // get NON existing value for existing vap - value = conf.get_vap_value("wlan0.1", "does_not_exist"); - EXPECT_EQ(value, "") << conf; - - // try to get for NON existing vap - value = conf.get_vap_value("no_vap", "key"); - EXPECT_EQ(value, "") << conf; - - //// end test //// -} - -TEST(configuration_test, diable_all_ap) -{ - //// start prerequsite //// - - // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_file); - ASSERT_FALSE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - auto disable_func = [&conf](const std::string vap) { - conf.set_create_vap_value(vap, "start_disabled", 1); - }; - - conf.for_all_ap_vaps(disable_func); - EXPECT_TRUE(conf) << conf; - - auto comment_func = [&conf](const std::string vap) { - conf.comment_vap(vap); - }; - - conf.for_all_ap_vaps(comment_func); - EXPECT_TRUE(conf) << conf; - - conf.store(); - EXPECT_TRUE(conf) << conf; - - //// end prerequsite //// -} - -TEST(configuration_test, comment_vap) -{ - //// start prerequsite //// - - // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_file); - ASSERT_FALSE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - //// end prerequsite //// - - //// start test //// - - conf.comment_vap("wlan0.1"); - EXPECT_TRUE(conf) << conf; - - conf.store(); - EXPECT_TRUE(conf) << conf; - - //// end test //// -} - -TEST(configuration_test, uncomment_vap) -{ - //// start prerequsite //// - - // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_file); - ASSERT_FALSE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - // comment twice! - conf.comment_vap("wlan0.1"); - conf.comment_vap("wlan0.1"); - EXPECT_TRUE(conf) << conf; - - conf.store(); - EXPECT_TRUE(conf) << conf; - - // load the dummy configuration file - conf.load(); - ASSERT_TRUE(conf) << conf; - - //// end prerequsite //// - - //// start test //// - - conf.uncomment_vap("wlan0.1"); - EXPECT_TRUE(conf) << conf; - - conf.uncomment_vap("wlan0.2"); - EXPECT_TRUE(conf) << conf; - - conf.store(); - EXPECT_TRUE(conf) << conf; - - //// end test //// -} - TEST(configuration_test, itererate_both_containers) { //// start prerequsite //// From f81b5951339c9976c091af7ce9a8fed09d920ebf Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Wed, 15 Jul 2020 16:08:00 +0300 Subject: [PATCH 19/28] prplmesh-hostapd: added set_create_xx_head_value added a functionality to set or create key/value in the header part: either string or int added functionality and tests https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 42 ++++++++++++-- .../beerocks/hostapd/include/configuration.h | 28 ++++++++- .../hostapd/unit_tests/configuration_test.cpp | 58 +++++++++++++++++++ 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index c06e693709..d1a81b959b 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -112,6 +112,44 @@ bool Configuration::store() return *this; } +bool Configuration::set_create_head_value(const std::string &key, const std::string &value) +{ + // search for the key + std::string key_eq(key + "="); + auto it_str = std::find_if( + m_hostapd_config_head.begin(), m_hostapd_config_head.end(), + [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); + + // we first delete the key, and if the requested value is non empty + // we push it to the end of the array + + // delete the key-value if found + if (it_str != m_hostapd_config_head.end()) { + it_str = m_hostapd_config_head.erase(it_str); + } else { + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' for head was not found"; + } + + // when the new value is provided add the key back with that new value + if (value.length() != 0) { + m_hostapd_config_head.push_back(key_eq + value); + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' was (re)added to head"; + } else { + m_last_message = + std::string(__FUNCTION__) + " the key '" + key + "' was deleted from head"; + } + + m_ok = true; + return *this; +} + +bool Configuration::set_create_head_value(const std::string &key, const int value) +{ + return set_create_head_value(key, std::to_string(value)); +} + bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, const std::string &value) { @@ -123,10 +161,6 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri auto existing_vap = std::get<1>(find_vap); bool existing_vap_commented = existing_vap->front()[0] == '#'; - // search for the key from the back of the string - // ignore comments this way - // e.g: - // ###bssid=11:22:ff:ee:aa std::string key_eq(key + "="); auto it_str = std::find_if( existing_vap->begin(), existing_vap->end(), diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 319c65690b..203fcddc8d 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -51,6 +51,32 @@ class Configuration { */ bool store(); + /** + * @brief set key/value in the head section + * @details set the key/vale in the head section, + * either replace or create. + * @note comments in the head section are not supported + * for example, if the key/value line was commented before + * the call to this function, it would be uncommented afterwards + * @param + * - the key to set its value (string) + * - the value (string) + */ + bool set_create_head_value(const std::string &key, const std::string &value); + + /** + * @brief set key/value in the head section + * @details set the key/vale in the head section, + * either replace or create. + * @note comments in the head section are not supported + * for example, if the key/value line was commented before + * the call to this function, it would be uncommented afterwards + * @param + * - the key to set its value (string) + * - the value (int) + */ + bool set_create_head_value(const std::string &key, const int value); + /** * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. @@ -170,7 +196,7 @@ class Configuration { bool m_ok = false; // each string is a line in the original configuration file - // that belongs to the "head" part. read the explnation at + // that belongs to the "head" part. read the explenation at // the end of the cpp file for more details std::vector m_hostapd_config_head; diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 615d0121bf..5fab64f5e2 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -231,6 +231,64 @@ TEST(configuration_test, store) EXPECT_TRUE(conf) << conf; } +TEST(configuration_test, set_string_head_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + // clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + // replace existing key + conf.set_create_head_value("vht_capab", "this is shorter"); + EXPECT_TRUE(conf) << conf; + + // remove exiting key + conf.set_create_head_value("ht_capab", ""); + EXPECT_TRUE(conf) << conf; + + // add a new key/value + conf.set_create_head_value("new_head", "{pnew->next=phead; phead=pnew;}"); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; +} + +TEST(configuration_test, set_int_head_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + // replace existing key + conf.set_create_head_value("bss_transition", 451); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; +} + TEST(configuration_test, set_string_vap_values) { //// start prerequsite //// From b45fedd934453fbda52eca3fdb344b267c45f01d Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 16 Jul 2020 14:01:17 +0300 Subject: [PATCH 20/28] prplmesh-hostapd: added get_head_value() added a function to get a value from the head section added functionality and unit test also in this commit: the variable named it_str is now called line_iter (it is better describs its meaning) https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 35 ++++++++++++++----- .../beerocks/hostapd/include/configuration.h | 15 ++++++-- .../hostapd/unit_tests/configuration_test.cpp | 30 ++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index d1a81b959b..f73eb90847 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -116,7 +116,7 @@ bool Configuration::set_create_head_value(const std::string &key, const std::str { // search for the key std::string key_eq(key + "="); - auto it_str = std::find_if( + auto line_iter = std::find_if( m_hostapd_config_head.begin(), m_hostapd_config_head.end(), [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); @@ -124,8 +124,8 @@ bool Configuration::set_create_head_value(const std::string &key, const std::str // we push it to the end of the array // delete the key-value if found - if (it_str != m_hostapd_config_head.end()) { - it_str = m_hostapd_config_head.erase(it_str); + if (line_iter != m_hostapd_config_head.end()) { + line_iter = m_hostapd_config_head.erase(line_iter); } else { m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' for head was not found"; @@ -150,6 +150,23 @@ bool Configuration::set_create_head_value(const std::string &key, const int valu return set_create_head_value(key, std::to_string(value)); } +std::string Configuration::get_head_value(const std::string &key) +{ + std::string key_eq(key + "="); + auto line_iter = std::find_if( + m_hostapd_config_head.begin(), m_hostapd_config_head.end(), + [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); + + if (line_iter == m_hostapd_config_head.end()) { + m_last_message = std::string(__FUNCTION__) + + " couldn't find requested key in head: " + key; + return ""; + } + + // return from just after the '=' sign to the end of the string + return line_iter->substr(line_iter->find('=') + 1); +} + bool Configuration::set_create_vap_value(const std::string &vap, const std::string &key, const std::string &value) { @@ -162,7 +179,7 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri bool existing_vap_commented = existing_vap->front()[0] == '#'; std::string key_eq(key + "="); - auto it_str = std::find_if( + auto line_iter = std::find_if( existing_vap->begin(), existing_vap->end(), [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); @@ -170,8 +187,8 @@ bool Configuration::set_create_vap_value(const std::string &vap, const std::stri // we push it to the end of the array // delete the key-value if found - if (it_str != existing_vap->end()) { - it_str = existing_vap->erase(it_str); + if (line_iter != existing_vap->end()) { + line_iter = existing_vap->erase(line_iter); } else { m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' for vap " + vap + " was not found"; @@ -215,18 +232,18 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri m_ok = true; std::string key_eq(key + "="); - auto it_str = std::find_if( + auto line_iter = std::find_if( existing_vap->begin(), existing_vap->end(), [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); - if (it_str == existing_vap->end()) { + if (line_iter == existing_vap->end()) { m_last_message = std::string(__FUNCTION__) + " couldn't find requested key for vap: " + vap + "; requested key: " + key; return ""; } // return from the just after the '=' sign to the end of the string - return it_str->substr(it_str->find('=') + 1); + return line_iter->substr(line_iter->find('=') + 1); } void Configuration::comment_vap(const std::string &vap) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 203fcddc8d..dfd25cbaeb 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -77,6 +77,14 @@ class Configuration { */ bool set_create_head_value(const std::string &key, const int value); + /** + * @brief get the value of the given key from the head + * @param + * - the key to get its value (string) + * @return a string with the value or empty if not found + */ + std::string get_head_value(const std::string &key); + /** * @brief set key/value for the given vap * @details set the key/vale for the given vap, either replace or create. @@ -193,7 +201,9 @@ class Configuration { private: std::string m_configuration_file; - bool m_ok = false; + + // may be changed because of const functions, therefore mutable + mutable bool m_ok = false; // each string is a line in the original configuration file // that belongs to the "head" part. read the explenation at @@ -222,7 +232,8 @@ class Configuration { // { "wlan0.2" : ["ctrl_interface=/var/run/hosXXpd", "ap_isolate=1", "ap_max_inactivity=60", "bss_transition=0", "interworking=3"] } std::map> m_hostapd_config_vaps; - std::string m_last_message = "all good"; + // see m_ok's comment + mutable std::string m_last_message = "all good"; // for logs friend std::ostream &operator<<(std::ostream &, const Configuration &); diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 5fab64f5e2..787e5a4120 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -289,6 +289,36 @@ TEST(configuration_test, set_int_head_values) EXPECT_TRUE(conf) << conf; } +TEST(configuration_test, get_head_values) +{ + //// start prerequsite //// + + // save the content of the string (start clean) + clean_start(); + + // construct a configuration + prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + ASSERT_FALSE(conf) << conf; + + // load the dummy configuration file + conf.load(); + ASSERT_TRUE(conf) << conf; + + //// end prerequsite //// + + // get existing key + auto val = conf.get_head_value("ctrl_interface"); + EXPECT_EQ(val, "/var/run/hostapd"); + + // get non existing key + val = conf.get_head_value("out_of_office"); + EXPECT_EQ(val, ""); + EXPECT_TRUE(conf) << conf; + + conf.store(); + EXPECT_TRUE(conf) << conf; +} + TEST(configuration_test, set_string_vap_values) { //// start prerequsite //// From 163a663813bbda4654ecf699b1e1bfbf27c3efb8 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 20 Jul 2020 12:58:48 +0300 Subject: [PATCH 21/28] prplmesh-hostapd: added vap_indication to load() originally bss= was the only sign for vap configuration inside the hostapd file. this was based on intel's hostapd version. the hostapd upstream indicates vap configuration with interface= sign. load() now seeks for the vap sign as set by the user. also in this commit: - removed namespace config from prplmesh::hostapd. left with only the two and class Configuration. - clang format fixes https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/configuration.cpp | 58 ++++++------- .../beerocks/hostapd/include/configuration.h | 28 ++++-- .../hostapd/unit_tests/configuration_test.cpp | 86 ++++++++++--------- 3 files changed, 95 insertions(+), 77 deletions(-) diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/configuration.cpp index f73eb90847..aec6d9a8bb 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/configuration.cpp @@ -13,23 +13,18 @@ namespace prplmesh { namespace hostapd { -namespace config { Configuration::Configuration(const std::string &file_name) : m_configuration_file(file_name) {} Configuration::operator bool() const { return m_ok; } -bool Configuration::load() +bool Configuration::load(const std::set &vap_indications) { // please take a look at the end of this file for // the expected format of hostapd configuration file // loading the file relays on the expected format // otherwise the load fails - - // for cases when load is called more than once, we - // first clear internal data - m_hostapd_config_head.clear(); - m_hostapd_config_vaps.clear(); + // for cases when load is called more than once, we // first clear internal data @@ -50,17 +45,21 @@ bool Configuration::load() if (std::all_of(line.begin(), line.end(), isspace)) { continue; } + // check if the string belongs to a vap config part and capture which one. - const std::string bss_eq("bss="); auto end_comment = line.find_first_not_of('#'); - if (line.compare(end_comment, bss_eq.length(), bss_eq) == 0) { - - // from now on we are in the vaps area, all - // key/value pairs belongs to vaps - parsing_vaps = true; - - // copy the bss (vap) value - cur_vap.assign(line, end_comment + bss_eq.length(), std::string::npos); + auto end_key = line.find_first_of('='); + + std::string current_key(line,end_comment,end_key+1); + + auto vap_iterator = vap_indications.find(current_key); + if (vap_iterator != vap_indications.end()) { + // from now on we are in the vaps area, all + // key/value pairs belongs to vaps + parsing_vaps = true; + + // copy the vap value + cur_vap = std::string(line, end_key + 1); } // if not a vap line store it in the header part of the config, @@ -72,7 +71,10 @@ bool Configuration::load() } } - m_last_message = strerror(errno); + std::stringstream load_message; + load_message << "load() final message: os - " << strerror(errno) << "; existing vaps - " + << std::boolalpha << parsing_vaps; + m_last_message = load_message.str(); // if we've got to parsing vaps and no read errors, assume all is good m_ok = parsing_vaps && !ifs.bad(); @@ -134,11 +136,9 @@ bool Configuration::set_create_head_value(const std::string &key, const std::str // when the new value is provided add the key back with that new value if (value.length() != 0) { m_hostapd_config_head.push_back(key_eq + value); - m_last_message = - std::string(__FUNCTION__) + " the key '" + key + "' was (re)added to head"; + m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' was (re)added to head"; } else { - m_last_message = - std::string(__FUNCTION__) + " the key '" + key + "' was deleted from head"; + m_last_message = std::string(__FUNCTION__) + " the key '" + key + "' was deleted from head"; } m_ok = true; @@ -158,8 +158,7 @@ std::string Configuration::get_head_value(const std::string &key) [&key_eq, this](const std::string &line) -> bool { return is_key_in_line(line, key_eq); }); if (line_iter == m_hostapd_config_head.end()) { - m_last_message = std::string(__FUNCTION__) + - " couldn't find requested key in head: " + key; + m_last_message = std::string(__FUNCTION__) + " couldn't find requested key in head: " + key; return ""; } @@ -225,7 +224,7 @@ std::string Configuration::get_vap_value(const std::string &vap, const std::stri if (!std::get<0>(find_vap)) { return ""; } - const auto& existing_vap = std::get<1>(find_vap); + const auto &existing_vap = std::get<1>(find_vap); // from now on this function is ok with all situations // (e.g. not finding the requested key) @@ -331,10 +330,10 @@ std::ostream &operator<<(std::ostream &o, const Configuration &conf) o << line << '\n'; } - o << "== vaps (" << conf.m_hostapd_config_vaps.size() << ") ==\n"; + o << "== vaps (total of: " << conf.m_hostapd_config_vaps.size() << " vaps) ==\n"; for (const auto &vap : conf.m_hostapd_config_vaps) { - o << " = vap (" << vap.first << " ) =\n"; + o << " vap: " << vap.first << "\n"; for (const auto &line : vap.second) { o << line << '\n'; } @@ -343,7 +342,6 @@ std::ostream &operator<<(std::ostream &o, const Configuration &conf) return o; } -} // namespace config } // namespace hostapd } // namespace prplmesh @@ -351,8 +349,10 @@ std::ostream &operator<<(std::ostream &o, const Configuration &conf) //////////////// hostapd configuration format //////////////// hostapd has a special format that does NOT have an ini like format. -We expect the following format of the file: -(between /// BEGIN hostapd.conf /// and /// END hostapd.conf ///) +between /// BEGIN hostapd.conf /// and /// END hostapd.conf /// is the +format of the file. +note: the string "bss=" may be replaced by the +user in the call to load() with another vap-indicator (e.g. "interface=") /// BEGIN hostapd.conf /// diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index dfd25cbaeb..1101038099 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -8,12 +8,12 @@ #include #include +#include #include #include namespace prplmesh { namespace hostapd { -namespace config { class Configuration { @@ -39,9 +39,18 @@ class Configuration { /** * @brief load the configuration * @details loads the file this object is constructed with + * @param vap_indications - set the indications that a vap + * configuration section begins. e.g. "bss=" and/or "interface=" + * note that the equal sign is expected to be part of vap_indication + * std::string vap_indication("interface="); + * vap indication can be any of the parameters supplied * @return *this (as bool, see above) */ - bool load(); + template + bool load(const StringIndications... vap_indications) + { + return load(std::set{vap_indications...}); + } /** * @brief stores the configuration @@ -177,6 +186,14 @@ class Configuration { const std::string &get_last_message() const; private: + /** + * @brief helper: load configuration based on array of strings + * indicating vap separation. + * @param array of separations + * @return *this as bool + */ + bool load(const std::set &vap_indicators); + /** * @brief helper: get exiting vap * @details any function that works on a specific vap does @@ -203,7 +220,7 @@ class Configuration { std::string m_configuration_file; // may be changed because of const functions, therefore mutable - mutable bool m_ok = false; + mutable bool m_ok = false; // each string is a line in the original configuration file // that belongs to the "head" part. read the explenation at @@ -232,8 +249,8 @@ class Configuration { // { "wlan0.2" : ["ctrl_interface=/var/run/hosXXpd", "ap_isolate=1", "ap_max_inactivity=60", "bss_transition=0", "interworking=3"] } std::map> m_hostapd_config_vaps; - // see m_ok's comment - mutable std::string m_last_message = "all good"; + // see m_ok's comment + mutable std::string m_last_message = "initial state, yet nothing was done"; // for logs friend std::ostream &operator<<(std::ostream &, const Configuration &); @@ -266,6 +283,5 @@ void Configuration::for_all_ap_vaps(func f, iter current_iter, const iter end) // for logs std::ostream &operator<<(std::ostream &, const Configuration &); -} // namespace config } // namespace hostapd } // namespace prplmesh diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 787e5a4120..66cad3bb95 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -13,8 +13,9 @@ namespace { +const std::string vap_indication("interface="); const std::string configuration_path("/tmp/"); -const std::string configuration_file_name("hostapd.conf"); +const std::string configuration_file_name("omnia.conf"); const std::string configuration_content( "driver=nl80211\n" "logger_syslog=127\n" @@ -177,6 +178,7 @@ const std::string configuration_content( "start_disabled=1\n" "mode=ap\n"); +/* void clean_start() { // save the content of the string (start clean) @@ -184,21 +186,22 @@ void clean_start() tmp << configuration_content; tmp.flush(); } +*/ TEST(configuration_test, load) { //// start prerequsite //// - clean_start(); + ////clean_start(); //// end prerequsite //// // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); EXPECT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication); EXPECT_TRUE(conf) << conf; } @@ -207,17 +210,17 @@ TEST(configuration_test, store) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // load the dummy configuration file - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); EXPECT_FALSE(conf) << conf; // construct a configuration ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -236,14 +239,14 @@ TEST(configuration_test, set_string_head_values) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -269,14 +272,14 @@ TEST(configuration_test, set_int_head_values) //// start prerequsite //// // save the content of the string (start clean) - clean_start(); + //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -294,14 +297,14 @@ TEST(configuration_test, get_head_values) //// start prerequsite //// // save the content of the string (start clean) - clean_start(); + //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -324,14 +327,14 @@ TEST(configuration_test, set_string_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // // clean_start(); + // // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -365,14 +368,14 @@ TEST(configuration_test, set_int_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -401,14 +404,14 @@ TEST(configuration_test, get_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -441,14 +444,14 @@ TEST(configuration_test, disable_all_ap) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; // disable by adding a key/value @@ -459,7 +462,6 @@ TEST(configuration_test, disable_all_ap) conf.for_all_ap_vaps(disable_func); EXPECT_TRUE(conf) << conf; - // disable by commenting auto comment_func = [&conf](const std::string vap) { conf.comment_vap(vap); }; @@ -478,14 +480,14 @@ TEST(configuration_test, enable_all_ap) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; // enable by removing key/value @@ -514,14 +516,14 @@ TEST(configuration_test, comment_vap) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -542,14 +544,14 @@ TEST(configuration_test, uncomment_vap) //// start prerequsite //// // save the content of the string (start clean) - // clean_start(); + // //clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; // comment twice! @@ -561,7 +563,7 @@ TEST(configuration_test, uncomment_vap) EXPECT_TRUE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -601,14 +603,14 @@ TEST(configuration_test, itererate_both_containers) //// start prerequsite //// // save the content of the string (start clean) - //clean_start(); + ////clean_start(); // construct a configuration - prplmesh::hostapd::config::Configuration conf(configuration_path + configuration_file_name); + prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(); + conf.load(vap_indication);; ASSERT_TRUE(conf) << conf; //// end prerequsite //// From a76b023c1de6ab554ecaf5a4be9af6572ebb6cf5 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 20 Jul 2020 14:56:09 +0300 Subject: [PATCH 22/28] prplmesh-hostapd: added ap predicate to for_all_ap_vaps there is a need to let the usr the ability to decide if a given vap is an ap vap or not. e.g. for intel the predicate is: mode=ap for ap stream hostapd: all vaps are ap vaps https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- .../beerocks/hostapd/include/configuration.h | 33 +++++---- .../hostapd/unit_tests/configuration_test.cpp | 74 ++++++++++++++----- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/configuration.h index 1101038099..c73a19f531 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/configuration.h @@ -162,20 +162,26 @@ class Configuration { /** * @brief apply func to all ap vaps - * @details apply func to all vaps that thier mode is "ap" - * (leaving STAs vaps untouched for example). + * @details apply func to all vaps that ap_predicate returns + * true for them. this enables leaving STAs vaps untouched for example. + * @param func has the following interface: void f(const std::string &vap); + * @param ap_predicate has the following interface: bool f(const std::string &vap); */ - template void for_all_ap_vaps(func); + template void for_all_ap_vaps(func, ap_predicate); /** * @brief apply func to all ap vaps, increment the given iterator * with each call to func: f(current_vap, ++user_itertor) - * func interface: template f(const std::string& vap, iter user_iterator) - * @details apply func to all vaps that thier mode is "ap" while incrementing - * the user iterator. this functionality enables iterating over two containers + * @details apply func to all vaps that ap_predicate returns true for + * while incrementing the user iterator. + * this functionality enables iterating over two containers * in paralel: the ap-vaps and the user container. + * @param func has the following interface: template f(const std::string& vap, iter user_iterator) + * @param iter - iterator to the begining of the user sequence + * @param ap_predicate has the following interface: bool f(const std::string &vap); */ - template void for_all_ap_vaps(func, iter current, const iter end); + template + void for_all_ap_vaps(func, iter current, const iter end, ap_predicate); /** * @brief for debug: return the last internal message @@ -256,21 +262,22 @@ class Configuration { friend std::ostream &operator<<(std::ostream &, const Configuration &); }; -template void Configuration::for_all_ap_vaps(func f) +template +void Configuration::for_all_ap_vaps(func f, ap_predicate pred) { auto f_with_iter = [&f](const std::string &vap, int iter) { f(vap); }; int dummy(0); - for_all_ap_vaps(f_with_iter, dummy, 10); + for_all_ap_vaps(f_with_iter, dummy, 10, pred); } -template -void Configuration::for_all_ap_vaps(func f, iter current_iter, const iter end) +template +void Configuration::for_all_ap_vaps(func f, iter current_iter, const iter end, ap_predicate pred) { for_each(m_hostapd_config_vaps.begin(), m_hostapd_config_vaps.end(), [this, &f, ¤t_iter, - &end](const std::pair> &vap) { - if (get_vap_value(vap.first, "mode") == "ap") { + &end, &pred](const std::pair> &vap) { + if (pred(vap.first)) { if (end == current_iter) { f(vap.first, end); } else { diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 66cad3bb95..4a746e617d 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -178,6 +178,7 @@ const std::string configuration_content( "start_disabled=1\n" "mode=ap\n"); + /* void clean_start() { @@ -220,13 +221,14 @@ TEST(configuration_test, store) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// // add a value to vap - conf.set_create_vap_value("wlan0.3", "was_i_stroed", "yes_you_were"); + conf.set_create_vap_value("wlan1", "was_i_stroed", "yes_you_were"); EXPECT_TRUE(conf) << conf; // store @@ -246,7 +248,8 @@ TEST(configuration_test, set_string_head_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -279,7 +282,8 @@ TEST(configuration_test, set_int_head_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -304,7 +308,8 @@ TEST(configuration_test, get_head_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -334,7 +339,8 @@ TEST(configuration_test, set_string_vap_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -375,7 +381,8 @@ TEST(configuration_test, set_int_vap_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -411,7 +418,8 @@ TEST(configuration_test, get_vap_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -451,21 +459,34 @@ TEST(configuration_test, disable_all_ap) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; + // identify vap by mode=ap + /* + auto mode_predicate = [&conf](const std::string &vap) { + return conf.get_vap_value(vap, "mode") == "ap"; + }; + */ + + // all vaps are ap vaps + auto all_predicate = [&conf](const std::string &vap) { + return true; + }; + // disable by adding a key/value auto disable_func = [&conf](const std::string vap) { conf.set_create_vap_value(vap, "start_disabled", 1); }; - conf.for_all_ap_vaps(disable_func); + conf.for_all_ap_vaps(disable_func, all_predicate); EXPECT_TRUE(conf) << conf; // disable by commenting auto comment_func = [&conf](const std::string vap) { conf.comment_vap(vap); }; - conf.for_all_ap_vaps(comment_func); + conf.for_all_ap_vaps(comment_func, all_predicate); EXPECT_TRUE(conf) << conf; // store @@ -487,7 +508,8 @@ TEST(configuration_test, enable_all_ap) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; // enable by removing key/value @@ -495,13 +517,17 @@ TEST(configuration_test, enable_all_ap) conf.set_create_vap_value(vap, "start_disabled", ""); }; - conf.for_all_ap_vaps(enable_func); + auto ap_predicate = [&conf](const std::string &vap) { + return conf.get_vap_value(vap, "mode") == "ap"; + }; + + conf.for_all_ap_vaps(enable_func, ap_predicate); EXPECT_TRUE(conf) << conf; // enable by uncommenting - auto uncomment_func = [&conf](const std::string vap) { conf.uncomment_vap(vap); }; + auto uncomment_func = [&conf](const std::string &vap) { conf.uncomment_vap(vap); }; - conf.for_all_ap_vaps(uncomment_func); + conf.for_all_ap_vaps(uncomment_func, ap_predicate); EXPECT_TRUE(conf) << conf; // store @@ -523,7 +549,8 @@ TEST(configuration_test, comment_vap) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -551,7 +578,8 @@ TEST(configuration_test, uncomment_vap) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; // comment twice! @@ -563,7 +591,8 @@ TEST(configuration_test, uncomment_vap) EXPECT_TRUE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -610,7 +639,8 @@ TEST(configuration_test, itererate_both_containers) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication);; + conf.load(vap_indication); + ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -631,7 +661,11 @@ TEST(configuration_test, itererate_both_containers) } }; - conf.for_all_ap_vaps(set_ssid, ssid.begin(), ssid.end()); + auto ap_predicate = [&conf](const std::string &vap) { + return conf.get_vap_value(vap, "mode") == "ap"; + }; + + conf.for_all_ap_vaps(set_ssid, ssid.begin(), ssid.end(), ap_predicate); EXPECT_TRUE(conf) << conf; conf.store(); From 6a6fa395f47129282912defc5ba89b250da7561e Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 30 Jul 2020 14:26:54 +0300 Subject: [PATCH 23/28] prplmesh-hostapd: PR 1514 review fixups this commnit fixes all relevant comments for https://github.com/prplfoundation/prplMesh/pull/1550 https://jira.prplfoundation.org/browse/PPM-249 Signed-off-by: Ran Regev --- common/beerocks/hostapd/CMakeLists.txt | 2 +- .../include/{ => hostapd}/configuration.h | 11 ++- .../hostapd/{ => source}/configuration.cpp | 75 ++++--------------- .../hostapd/unit_tests/configuration_test.cpp | 37 +++++---- 4 files changed, 45 insertions(+), 80 deletions(-) rename common/beerocks/hostapd/include/{ => hostapd}/configuration.h (95%) rename common/beerocks/hostapd/{ => source}/configuration.cpp (83%) diff --git a/common/beerocks/hostapd/CMakeLists.txt b/common/beerocks/hostapd/CMakeLists.txt index 8287220dec..f69c15a220 100644 --- a/common/beerocks/hostapd/CMakeLists.txt +++ b/common/beerocks/hostapd/CMakeLists.txt @@ -37,7 +37,7 @@ set(PRPLMESH_HOSTAPD_TYPE ${PRPLMESH_HOSTAPD_TYPE} CACHE STRING "Which PRPLMESH_ set( hostapd_sources - configuration.cpp + source/configuration.cpp ) diff --git a/common/beerocks/hostapd/include/configuration.h b/common/beerocks/hostapd/include/hostapd/configuration.h similarity index 95% rename from common/beerocks/hostapd/include/configuration.h rename to common/beerocks/hostapd/include/hostapd/configuration.h index c73a19f531..ede822d8f5 100644 --- a/common/beerocks/hostapd/include/configuration.h +++ b/common/beerocks/hostapd/include/hostapd/configuration.h @@ -225,7 +225,12 @@ class Configuration { private: std::string m_configuration_file; - // may be changed because of const functions, therefore mutable + // m_ok hoslds the internal state of the configuration + // the user may query the success/fail state of the last command + // simply by reading the value of this variable. + // the access to it is via operator bool() + // m_ok itslef may be changed because of a call to + // const function, therefore it is mutable mutable bool m_ok = false; // each string is a line in the original configuration file @@ -275,8 +280,8 @@ template void Configuration::for_all_ap_vaps(func f, iter current_iter, const iter end, ap_predicate pred) { for_each(m_hostapd_config_vaps.begin(), m_hostapd_config_vaps.end(), - [this, &f, ¤t_iter, - &end, &pred](const std::pair> &vap) { + [this, &f, ¤t_iter, &end, + &pred](const std::pair> &vap) { if (pred(vap.first)) { if (end == current_iter) { f(vap.first, end); diff --git a/common/beerocks/hostapd/configuration.cpp b/common/beerocks/hostapd/source/configuration.cpp similarity index 83% rename from common/beerocks/hostapd/configuration.cpp rename to common/beerocks/hostapd/source/configuration.cpp index aec6d9a8bb..223d8db5e9 100644 --- a/common/beerocks/hostapd/configuration.cpp +++ b/common/beerocks/hostapd/source/configuration.cpp @@ -6,10 +6,10 @@ * See LICENSE file for more details. */ -#include "configuration.h" #include #include #include +#include namespace prplmesh { namespace hostapd { @@ -20,9 +20,9 @@ Configuration::operator bool() const { return m_ok; } bool Configuration::load(const std::set &vap_indications) { - // please take a look at the end of this file for - // the expected format of hostapd configuration file - // loading the file relays on the expected format + // please take a look at README.md (common/beerocks/hostapd/README.md) for + // the expected format of hostapd configuration file. + // loading the file relies on the expected format // otherwise the load fails @@ -318,74 +318,29 @@ bool Configuration::is_key_in_line(const std::string &line, const std::string &k return ret; } -std::ostream &operator<<(std::ostream &o, const Configuration &conf) +std::ostream &operator<<(std::ostream &os, const Configuration &conf) { - o << "== configuration details ==\n" - << "= ok: " << std::boolalpha << conf.m_ok << '\n' - << "= last message: " << conf.m_last_message << '\n' - << "= file: " << conf.m_configuration_file << '\n' - << "= head: " << '\n'; + os << "== configuration details ==\n" + << "= ok: " << std::boolalpha << conf.m_ok << '\n' + << "= last message: " << conf.m_last_message << '\n' + << "= file: " << conf.m_configuration_file << '\n' + << "= head: " << '\n'; for (const auto &line : conf.m_hostapd_config_head) { - o << line << '\n'; + os << line << '\n'; } - o << "== vaps (total of: " << conf.m_hostapd_config_vaps.size() << " vaps) ==\n"; + os << "== vaps (total of: " << conf.m_hostapd_config_vaps.size() << " vaps) ==\n"; for (const auto &vap : conf.m_hostapd_config_vaps) { - o << " vap: " << vap.first << "\n"; + os << " vap: " << vap.first << "\n"; for (const auto &line : vap.second) { - o << line << '\n'; + os << line << '\n'; } } - return o; + return os; } } // namespace hostapd } // namespace prplmesh - -/* -//////////////// hostapd configuration format //////////////// - -hostapd has a special format that does NOT have an ini like format. -between /// BEGIN hostapd.conf /// and /// END hostapd.conf /// is the -format of the file. -note: the string "bss=" may be replaced by the -user in the call to load() with another vap-indicator (e.g. "interface=") - -/// BEGIN hostapd.conf /// - -## "head" part ## - -# everything until the first `bss=` in the file -# is considered "head" -# after the first `bss=` "vaps" are presented. -# so we expect no parametrs that does not belong to vaps -# after the first `bss=` -# take a look below for more details - -# note that we don't expect a space between the key and the equal sign -# the code in this class seeks for `key=` when a key is needed. -# therefore `key =` (with space) will fail -# also, everything immidiatly after the equal sign (=) is -# part of the value. the space before 11 in this is part of the value: -# bassid= 11:22:33:44:55:66 -key=value - -## "vaps part - we expect at least one vap to be configured ## - -# vap (bss and vap are interchangable) -bss=wlan0_0 - -# this key (ssid) belongs to the previous vap (bss value which is wlan0_0) -ssid=test2 - -# another vap -bss=wlan0_1 - -# this key (bssid) belongs to the previous vap wlan0_1 -bssid=00:13:10:95:fe:0b - -///// END hostapd.conf /// -*/ diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 4a746e617d..04774eea30 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -6,13 +6,14 @@ * See LICENSE file for more details. */ -#include "configuration.h" #include +#include #include namespace { +//const std::string vap_indication("bss="); const std::string vap_indication("interface="); const std::string configuration_path("/tmp/"); const std::string configuration_file_name("omnia.conf"); @@ -56,9 +57,10 @@ const std::string configuration_content( "wpa=0\n" "ssid=dummy_ssid_0\n" "bridge=br-lan\n" - "bssid=02:9A:96:FB:59:0F\n" + "bssid=02:9A:96:FB:59:0F\n" + - "bss=wlan0.0\n" + vap_indication + + "wlan0.0\n" "ctrl_interface=/var/run/hostapd\n" "ap_isolate=1\n" "ap_max_inactivity=60\n" @@ -100,9 +102,10 @@ const std::string configuration_content( "wps_state=2\n" "mesh_mode=fAP\n" "multi_ap_backhaul_ssid=\"Multi-AP-24G-2\"\n" - "multi_ap_backhaul_wpa_passphrase=maprocks2\n" + "multi_ap_backhaul_wpa_passphrase=maprocks2\n" + - "bss=wlan0.1\n" + vap_indication + + "wlan0.1\n" "mode=non-ap\n" "ctrl_interface=/var/run/hostapd\n" "ap_isolate=1\n" @@ -134,7 +137,9 @@ const std::string configuration_content( "sFourAddrMode=1\n" "max_num_sta=1\n" - "#bss=wlan0.2\n" + "#" + + vap_indication + + "wlan0.2\n" "#ctrl_interface=/var/run/hostapd\n" "#ap_isolate=1\n" "#ap_max_inactivity=60\n" @@ -154,9 +159,10 @@ const std::string configuration_content( "#wpa=0\n" "#bridge=br-lan\n" "#bssid=02:9A:96:FB:59:13\n" - "#start_disabled=1\n" + "#start_disabled=1\n" + - "bss=wlan0.3\n" + vap_indication + + "wlan0.3\n" "ctrl_interface=/var/run/hostapd\n" "ap_isolate=1\n" "ap_max_inactivity=60\n" @@ -178,8 +184,6 @@ const std::string configuration_content( "start_disabled=1\n" "mode=ap\n"); - -/* void clean_start() { // save the content of the string (start clean) @@ -187,7 +191,6 @@ void clean_start() tmp << configuration_content; tmp.flush(); } -*/ TEST(configuration_test, load) { @@ -228,7 +231,7 @@ TEST(configuration_test, store) //// end prerequsite //// // add a value to vap - conf.set_create_vap_value("wlan1", "was_i_stroed", "yes_you_were"); + conf.set_create_vap_value("wlan0.1", "was_i_stroed", "yes_you_were"); EXPECT_TRUE(conf) << conf; // store @@ -315,8 +318,8 @@ TEST(configuration_test, get_head_values) //// end prerequsite //// // get existing key - auto val = conf.get_head_value("ctrl_interface"); - EXPECT_EQ(val, "/var/run/hostapd"); + auto val = conf.get_head_value("acs_num_scans"); + EXPECT_EQ(val, "1"); // get non existing key val = conf.get_head_value("out_of_office"); @@ -332,7 +335,7 @@ TEST(configuration_test, set_string_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); @@ -340,13 +343,15 @@ TEST(configuration_test, set_string_vap_values) // load the dummy configuration file conf.load(vap_indication); - ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// //// start test //// + conf.set_create_vap_value("wlan0", "ssid", "ran_home"); + EXPECT_TRUE(conf) << conf; + // replace existing value for existing key for existing vap conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); EXPECT_TRUE(conf) << conf; From 930a2c0ec94eb8ec1a581105fd15b7acb760c495 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Mon, 20 Jul 2020 14:56:09 +0300 Subject: [PATCH 24/28] prplmesh-hostapd: added ap predicate to for_all_ap_vaps there is a need to let the usr the ability to decide if a given vap is an ap vap or not. e.g. for intel the predicate is: mode=ap for up stream's hostapd: all vaps are ap vaps Signed-off-by: Ran Regev --- common/beerocks/hostapd/unit_tests/configuration_test.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 04774eea30..890e7703a7 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -476,9 +476,7 @@ TEST(configuration_test, disable_all_ap) */ // all vaps are ap vaps - auto all_predicate = [&conf](const std::string &vap) { - return true; - }; + auto all_predicate = [&conf](const std::string &vap) { return true; }; // disable by adding a key/value auto disable_func = [&conf](const std::string vap) { From 56a5adf8c47c37fa5f434a65869a54f9924dd524 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 23 Jul 2020 13:51:45 +0300 Subject: [PATCH 25/28] nl80211: hostapd configuration: cmake added prplmesh_hostapd dependency to bwl and btl in cmake Signed-off-by: Ran Regev --- common/beerocks/bwl/CMakeLists.txt | 4 ++- common/beerocks/hostapd/CMakeLists.txt | 43 ++------------------------ 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/common/beerocks/bwl/CMakeLists.txt b/common/beerocks/bwl/CMakeLists.txt index 7ec702efad..39ef976aa2 100644 --- a/common/beerocks/bwl/CMakeLists.txt +++ b/common/beerocks/bwl/CMakeLists.txt @@ -109,6 +109,8 @@ elseif(BWL_TYPE STREQUAL "NL80211") ${PLATFORM_STAGING_DIR}/usr/lib ) + list(APPEND BWL_LIBS prplmesh_hostapd) + elseif(BWL_TYPE STREQUAL "DUMMY") set(bwl_platform_sources @@ -179,7 +181,7 @@ if (BUILD_TESTS) PUBLIC $ ) - target_link_libraries(${TEST_PROJECT_NAME} bcl elpp ${BWL_LIBS}) + target_link_libraries(${TEST_PROJECT_NAME} prplmesh_hostapd bcl elpp ${BWL_LIBS}) target_link_libraries(${TEST_PROJECT_NAME} gtest_main) install(TARGETS ${TEST_PROJECT_NAME} DESTINATION bin/tests) add_test(NAME ${TEST_PROJECT_NAME} COMMAND $) diff --git a/common/beerocks/hostapd/CMakeLists.txt b/common/beerocks/hostapd/CMakeLists.txt index f69c15a220..f7b3d5afc3 100644 --- a/common/beerocks/hostapd/CMakeLists.txt +++ b/common/beerocks/hostapd/CMakeLists.txt @@ -1,47 +1,11 @@ + project(prplmesh_hostapd VERSION ${prplmesh_VERSION}) # Set the base path for the current module set(MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) -# Default defaults -set(PRPLMESH_HOSTAPD_TYPE "DUMMY" CACHE STRING "PRPLMESH_HOSTAPD type") -set_property(CACHE PRPLMESH_HOSTAPD_TYPE PROPERTY STRINGS "DUMMY" "NL80211" "DWPAL") -add_definitions(-DBEEROCKS_TMP_PATH="${TMP_PATH}") - -if (TARGET_PLATFORM STREQUAL "rdkb") - - set(PRPLMESH_HOSTAPD_TYPE "DWPAL") - # Extra libraries - list(APPEND PRPLMESH_HOSTAPD_LIBS rt dl) - -elseif(TARGET_PLATFORM STREQUAL "openwrt") - - if (TARGET_PLATFORM_TYPE STREQUAL "ugw") - set(PRPLMESH_HOSTAPD_TYPE "DWPAL") - else() - set(PRPLMESH_HOSTAPD_TYPE "NL80211") - endif() - - # hostapd directory - file(GLOB PRPLMESH_HOSTAPD_SEARCH_PATHS "${PLATFORM_BUILD_DIR}/hostapd*/hostapd-*") - find_path(PRPLMESH_HOSTAPD_INCLUDE_DIR NAMES "src/common/wpa_ctrl.h" PATHS ${PRPLMESH_HOSTAPD_SEARCH_PATHS} NO_CMAKE_FIND_ROOT_PATH) - set(PRPLMESH_HOSTAPD_DIR "${PRPLMESH_HOSTAPD_INCLUDE_DIR}") - -endif() - -set(PRPLMESH_HOSTAPD_TYPE ${PRPLMESH_HOSTAPD_TYPE} CACHE STRING "Which PRPLMESH_HOSTAPD backend to use") - -########################################################################## -########################################################################## -########################################################################## - -set( - hostapd_sources - source/configuration.cpp -) - - # Build the library +set(hostapd_sources source/configuration.cpp) add_library(${PROJECT_NAME} ${hostapd_sources}) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${prplmesh_VERSION} SOVERSION ${prplmesh_VERSION_MAJOR}) target_link_libraries(${PROJECT_NAME} elpp ${PRPLMESH_HOSTAPD_LIBS}) @@ -60,8 +24,7 @@ install( ) if (BUILD_TESTS) - string(TOLOWER ${PRPLMESH_HOSTAPD_TYPE} PRPLMESH_HOSTAPD_TYPE_LOWERCASE) - set(TEST_PROJECT_NAME ${PROJECT_NAME}_${PRPLMESH_HOSTAPD_TYPE_LOWERCASE}_unit_tests) + set(TEST_PROJECT_NAME ${PROJECT_NAME}_unit_tests) set(unit_tests_sources ${MODULE_PATH}/unit_tests/configuration_test.cpp ) From 0fac46d305fac839ae437d84c790919c6164c400 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 23 Jul 2020 13:52:54 +0300 Subject: [PATCH 26/28] nl80211: hostapd configuration update_vap_credentials() added hostapd configuration to update_vap_credentials() the implementation was copied and then adopted from dwpal implementation (common/beerocks/bwl/dwpal/ap_wlan_hal_dwpal.cpp) tested on Turris Omnia Signed-off-by: Ran Regev --- .../bwl/nl80211/ap_wlan_hal_nl80211.cpp | 297 +++++++++++++++++- 1 file changed, 296 insertions(+), 1 deletion(-) diff --git a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp index aa54592dc2..ff12ac0856 100644 --- a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp +++ b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -106,6 +107,46 @@ static uint8_t wpa_bw_to_beerocks_bw(const std::string &chan_width) return (chan_width == "80+80") ? 160 : beerocks::string_utils::stoi(chan_width); } +/// @brief figures out hostapd config file name by the +// interface name and loads its content +static prplmesh::hostapd::Configuration load_hostapd_config(const std::string &radio_iface_name) +{ + std::vector hostapd_cfg_names = { + "/tmp/run/hostapd-phy0.conf", "/tmp/run/hostapd-phy1.conf", "/var/run/hostapd-phy0.conf", + "/var/run/hostapd-phy1.conf", "/var/run/hostapd-phy2.conf", "/var/run/hostapd-phy3.conf"}; + + for (const auto &try_fname : hostapd_cfg_names) { + LOG(DEBUG) << "Trying to load " << try_fname << "..."; + + if (!beerocks::os_utils::file_exists(try_fname)) { + continue; + } + + prplmesh::hostapd::Configuration hostapd_conf(try_fname); + + // try loading + if (!hostapd_conf.load("interface=")) { + LOG(ERROR) << "Failed to load hostapd config file: " << hostapd_conf; + continue; + } + + // check if it is the right one: + // we are looking for the line in the vap that declares this vap: interface=radio_iface_name + // we could equaly ask hostapd_conf if it has this vap, but there is no such interface + // to do this. it should be something like: hostapd_conf.is_vap_exists(radio_iface_name); + if (hostapd_conf.get_vap_value(radio_iface_name, "interface") != radio_iface_name) { + LOG(DEBUG) << radio_iface_name << " does not exists in " << try_fname; + continue; + } + + // all is good, return the conf + return hostapd_conf; + } + + // return an empty one since we couldn't find the + return prplmesh::hostapd::Configuration("file not found"); +} + ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// Implementation /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -268,7 +309,261 @@ bool ap_wlan_hal_nl80211::update_vap_credentials( std::list &bss_info_conf_list, const std::string &backhaul_wps_ssid, const std::string &backhaul_wps_passphrase) { - //TODO Implement #346 + // The bss_info_conf_list contains list of all VAPs and STAs that have to be created + // on the radio. If the list is empty, there should be no VAPs left. + // At the moment we are taking a shortcut to make this work for certification setup. + // We are updating the hostapd.conf files with the info from the bss_info_conf_list + // and sending RELOAD command to hostapd making it update the VAPs. + // With that approach there are following limitations: + // - all the VAPs are expected to be always present in the config, but have + // start_disabled=1 option if not active + // - any configuration change causing the hostapd config files to be re-generated + // and the hostapd processes restarted will override the prplMesh configuration + + // The hostapd config is stored here. The items order in the header and VAPs sections + // is mostly preserved (with the exception of the items prplMesh modifies). This helps + // to keep the configuration the same and easily see what prplMesh is changing. + + // Load hostapd config for the radio + + LOG(DEBUG) << std::string(__FUNCTION__) << " was called with list size: " << bss_info_conf_list.size(); + + + prplmesh::hostapd::Configuration conf = load_hostapd_config(m_radio_info.iface_name); + if (!conf) { + LOG(ERROR) << "Autoconfiguration: no hostapd config to apply configuration!"; + return false; + } + + // If a Multi-AP Agent receives an AP-Autoconfiguration WSC message containing one or + // more M2, it shall validate each M2 (based on its 1905 AL MAC address) and configure + // a BSS on the corresponding radio for each of the M2. If the Multi-AP Agent is currently + // operating a BSS with operating parameters that do not completely match any of the M2 in + // the received AP-Autoconfiguration WSC message, it shall tear down that BSS. + + // decalre a function for iterating over bss-conf and ap-vaps + bool abort = false; + auto configure_func = [&abort, &conf, &bss_info_conf_list, &backhaul_wps_ssid, + &backhaul_wps_passphrase]( + const std::string vap, + std::list::iterator bss_it) { + if (abort) { + return; + } + + if (bss_it != bss_info_conf_list.end()) { + + // we still have data on the input bss-conf list + + // escape I + auto auth_type = + son::wireless_utils::wsc_to_bwl_authentication(bss_it->authentication_type); + if (auth_type == "INVALID") { + LOG(ERROR) << "Autoconfiguration: auth type is 'INVALID'; number: " + << (uint16_t)bss_it->authentication_type; + abort = true; + return; + } + + // escape II + auto enc_type = son::wireless_utils::wsc_to_bwl_encryption(bss_it->encryption_type); + if (enc_type == "INVALID") { + LOG(ERROR) << "Autoconfiguration: enc_type is 'INVALID'; number: " + << int(bss_it->encryption_type); + abort = true; + return; + } + + // escape III + if (conf.get_vap_value(vap, "bssid").empty()) { + LOG(ERROR) << "Failed to get BSSID for vap: " << vap; + abort = true; + return; + } + + // escape IV + // BSS type (backhaul, fronthaul or both) + // Use Intel Mesh-Mode (upstream Multi-AP functionality not supported by Intel): + // Not supporting hybrid mode for in mesh mode (TODO - move to hybrid mode after + // https://github.com/prplfoundation/prplMesh/issues/889) + if (bss_it->fronthaul && bss_it->backhaul) { + LOG(ERROR) << "Not supporting hybrid VAP"; + abort = true; + return; + } + + // settings + + // ssid + conf.set_create_vap_value(vap, "ssid", bss_it->ssid); + + // Hostapd "wpa" field. + // This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) + // and/or WPA2 (full IEEE 802.11i/RSN): + // bit0 = WPA + // bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) + int wpa = 0; + + // Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The + // entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be + // added to enable SHA256-based stronger algorithms. + // WPA-PSK = WPA-Personal / WPA2-Personal + std::string wpa_key_mgmt; // default to empty -> delete from hostapd config + + // (dot11RSNAConfigPairwiseCiphersTable) + // Pairwise cipher for WPA (v1) (default: TKIP) + // wpa_pairwise=TKIP CCMP + // Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) + // rsn_pairwise=CCMP + std::string wpa_pairwise; // default to empty -> delete from hostapd config + + // WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit + // secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase + // (8..63 characters), wpa_passphrase. + std::string wpa_passphrase; + std::string wpa_psk; + + // ieee80211w: Whether management frame protection (MFP) is enabled + // 0 = disabled (default) + // 1 = optional + // 2 = required + std::string ieee80211w; + + // This parameter can be used to disable caching of PMKSA created through EAP + // authentication. RSN preauthentication may still end up using PMKSA caching if + // it is enabled (rsn_preauth=1). + // 0 = PMKSA caching enabled (default) + // 1 = PMKSA caching disabled + std::string disable_pmksa_caching; + + // Opportunistic Key Caching (aka Proactive Key Caching) + // Allow PMK cache to be shared opportunistically among configured interfaces + // and BSSes (i.e., all configurations within a single hostapd process). + // 0 = disabled (default) + // 1 = enabled + std::string okc; + + // This parameter can be used to disable retransmission of EAPOL-Key frames that + // are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This + // is similar to setting wpa_group_update_count=1 and + std::string wpa_disable_eapol_key_retries; + + // EasyMesh R1 only allows Open and WPA2 PSK auth&encryption methods. + // Quote: A Multi-AP Controller shall set the Authentication Type attribute + // in M2 to indicate WPA2-Personal or Open System Authentication. + // bss_it->authentication_type is a bitfield, but we are not going + // to accept any combinations due to the above limitation. + if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_OPEN) { + wpa = 0x0; + if (bss_it->encryption_type != WSC::eWscEncr::WSC_ENCR_NONE) { + LOG(ERROR) << "Autoconfiguration: " << vap << " encryption set on open VAP"; + abort = true; + return; + } + if (bss_it->network_key.length() > 0) { + LOG(ERROR) << "Autoconfiguration: " << vap << " network key set for open VAP"; + abort = true; + return; + } + } else if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_WPA2PSK) { + wpa = 0x2; + wpa_key_mgmt.assign("WPA-PSK"); + // Cipher must include AES for WPA2, TKIP is optional + if ((static_cast(bss_it->encryption_type) & static_cast(WSC::eWscEncr::WSC_ENCR_AES)) == + 0) { + LOG(ERROR) << "Autoconfiguration: " << vap + << " CCMP(AES) is required for WPA2"; + abort = true; + return; + } + if ((uint16_t(bss_it->encryption_type) & uint16_t(WSC::eWscEncr::WSC_ENCR_TKIP)) != + 0) { + wpa_pairwise.assign("TKIP CCMP"); + } else { + wpa_pairwise.assign("CCMP"); + } + if (bss_it->network_key.length() < 8 || bss_it->network_key.length() > 64) { + LOG(ERROR) << "Autoconfiguration: " << vap << " invalid network key length " + << bss_it->network_key.length(); + abort = true; + return; + } + if (bss_it->network_key.length() < 64) { + wpa_passphrase.assign(bss_it->network_key); + } else { + wpa_psk.assign(bss_it->network_key); + } + ieee80211w.assign("0"); + disable_pmksa_caching.assign("1"); + okc.assign("0"); + wpa_disable_eapol_key_retries.assign("0"); + } else { + LOG(ERROR) << "Autoconfiguration: " << vap << " invalid authentication type"; + abort = true; + return; + } + + LOG(DEBUG) << "Autoconfiguration for ssid: " << bss_it->ssid + << " auth_type: " << auth_type << " encr_type: " << enc_type + << " network_key: " << bss_it->network_key + << " fronthaul=" << beerocks::string_utils::bool_str(bss_it->fronthaul) + << " backhaul=" << beerocks::string_utils::bool_str(bss_it->backhaul); + + conf.set_create_vap_value(vap, "wps_state", bss_it->fronthaul ? "2" : ""); + conf.set_create_vap_value(vap, "wps_independent", "0"); + conf.set_create_vap_value(vap, "sFourAddrMode", bss_it->backhaul ? "1" : ""); + conf.set_create_vap_value(vap, "max_num_sta", bss_it->backhaul ? "1" : ""); + + // oddly enough, multi_ap_backhaul_wpa_passphrase has to be + // quoted, while wpa_passphrase does not... + if (bss_it->fronthaul && !backhaul_wps_ssid.empty()) { + conf.set_create_vap_value(vap, "multi_ap_backhaul_ssid", + "\"" + backhaul_wps_ssid + "\""); + conf.set_create_vap_value(vap, "multi_ap_backhaul_wpa_passphrase", + backhaul_wps_passphrase); + } + + conf.set_create_vap_value(vap, "wpa", wpa); + conf.set_create_vap_value(vap, "okc", okc); + conf.set_create_vap_value(vap, "wpa_key_mgmt", wpa_key_mgmt); + conf.set_create_vap_value(vap, "wpa_pairwise", wpa_pairwise); + conf.set_create_vap_value(vap, "wpa_psk", wpa_psk); + conf.set_create_vap_value(vap, "ieee80211w", ieee80211w); + conf.set_create_vap_value(vap, "wpa_passphrase", wpa_passphrase); + conf.set_create_vap_value(vap, "disable_pmksa_caching", disable_pmksa_caching); + conf.set_create_vap_value(vap, "wpa_disable_eapol_key_retries", + wpa_disable_eapol_key_retries); + + // finally enable the vap (remove any previously set start_disabled) + conf.set_create_vap_value(vap, "start_disabled", ""); + + } else { + // no more data in the input bss-conf list + // disable the rest of the vaps + conf.set_create_vap_value(vap, "start_disabled", 1); + conf.set_create_vap_value(vap, "ssid", ""); + } + }; + + conf.for_all_ap_vaps(configure_func, bss_info_conf_list.begin(), bss_info_conf_list.end(), + [](const std::string &) { return true; }); + + if (abort) { + return false; + } + + if (!conf.store()) { + LOG(ERROR) << "Autoconfiguration: cannot save hostapd config!"; + return false; + } + + const std::string cmd("UPDATE "); + if (!wpa_ctrl_send_msg(cmd)) { + LOG(ERROR) << "Autoconfiguration: \"" << cmd << "\" command to hostapd has failed"; + return false; + } + + LOG(DEBUG) << "Autoconfiguration: done:\n" << conf; return true; } From c515db9563c5b01b01b3c3e179ec65ac6615c632 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 30 Jul 2020 15:04:10 +0300 Subject: [PATCH 27/28] nl80211: hostapd configuration: close issues found in PR 1550 this commit closes issues found in https://github.com/prplfoundation/prplMesh/pull/1550 https://jira.prplfoundation.org/browse/PPM-4 MAP-4.2.1:turris-omnia Signed-off-by: Ran Regev --- .../bwl/nl80211/ap_wlan_hal_nl80211.cpp | 387 ++++++++---------- .../hostapd/include/hostapd/configuration.h | 4 +- .../beerocks/hostapd/source/configuration.cpp | 4 +- .../hostapd/unit_tests/configuration_test.cpp | 2 + 4 files changed, 189 insertions(+), 208 deletions(-) diff --git a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp index ff12ac0856..e5e780f6d7 100644 --- a/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp +++ b/common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp @@ -7,24 +7,21 @@ */ #include "ap_wlan_hal_nl80211.h" - #include #include #include #include #include -#include - -#include - #include - +#include +#include #include #include #include #include #include #include +#include ////////////////////////////////////////////////////////////////////////////// ////////////////////////// Local Module Definitions ////////////////////////// @@ -309,26 +306,13 @@ bool ap_wlan_hal_nl80211::update_vap_credentials( std::list &bss_info_conf_list, const std::string &backhaul_wps_ssid, const std::string &backhaul_wps_passphrase) { - // The bss_info_conf_list contains list of all VAPs and STAs that have to be created - // on the radio. If the list is empty, there should be no VAPs left. - // At the moment we are taking a shortcut to make this work for certification setup. - // We are updating the hostapd.conf files with the info from the bss_info_conf_list - // and sending RELOAD command to hostapd making it update the VAPs. - // With that approach there are following limitations: - // - all the VAPs are expected to be always present in the config, but have - // start_disabled=1 option if not active - // - any configuration change causing the hostapd config files to be re-generated - // and the hostapd processes restarted will override the prplMesh configuration - - // The hostapd config is stored here. The items order in the header and VAPs sections - // is mostly preserved (with the exception of the items prplMesh modifies). This helps - // to keep the configuration the same and easily see what prplMesh is changing. + if (0 == bss_info_conf_list.size()) { + LOG(DEBUG) << "given bss conf list size is zero, no changes to existing hostapd " + "configuration are applied"; + return true; + } // Load hostapd config for the radio - - LOG(DEBUG) << std::string(__FUNCTION__) << " was called with list size: " << bss_info_conf_list.size(); - - prplmesh::hostapd::Configuration conf = load_hostapd_config(m_radio_info.iface_name); if (!conf) { LOG(ERROR) << "Autoconfiguration: no hostapd config to apply configuration!"; @@ -342,208 +326,201 @@ bool ap_wlan_hal_nl80211::update_vap_credentials( // the received AP-Autoconfiguration WSC message, it shall tear down that BSS. // decalre a function for iterating over bss-conf and ap-vaps - bool abort = false; - auto configure_func = [&abort, &conf, &bss_info_conf_list, &backhaul_wps_ssid, - &backhaul_wps_passphrase]( - const std::string vap, - std::list::iterator bss_it) { - if (abort) { - return; - } - - if (bss_it != bss_info_conf_list.end()) { - - // we still have data on the input bss-conf list - - // escape I - auto auth_type = - son::wireless_utils::wsc_to_bwl_authentication(bss_it->authentication_type); - if (auth_type == "INVALID") { - LOG(ERROR) << "Autoconfiguration: auth type is 'INVALID'; number: " - << (uint16_t)bss_it->authentication_type; - abort = true; + bool abort = false; + auto configure_func = + [&abort, &conf, &bss_info_conf_list, &backhaul_wps_ssid, &backhaul_wps_passphrase]( + const std::string &vap, + std::remove_reference::type::iterator bss_it) { + if (abort) { return; } - // escape II - auto enc_type = son::wireless_utils::wsc_to_bwl_encryption(bss_it->encryption_type); - if (enc_type == "INVALID") { - LOG(ERROR) << "Autoconfiguration: enc_type is 'INVALID'; number: " - << int(bss_it->encryption_type); - abort = true; - return; - } + if (bss_it != bss_info_conf_list.end()) { - // escape III - if (conf.get_vap_value(vap, "bssid").empty()) { - LOG(ERROR) << "Failed to get BSSID for vap: " << vap; - abort = true; - return; - } + // we still have data on the input bss-conf list - // escape IV - // BSS type (backhaul, fronthaul or both) - // Use Intel Mesh-Mode (upstream Multi-AP functionality not supported by Intel): - // Not supporting hybrid mode for in mesh mode (TODO - move to hybrid mode after - // https://github.com/prplfoundation/prplMesh/issues/889) - if (bss_it->fronthaul && bss_it->backhaul) { - LOG(ERROR) << "Not supporting hybrid VAP"; - abort = true; - return; - } - - // settings - - // ssid - conf.set_create_vap_value(vap, "ssid", bss_it->ssid); - - // Hostapd "wpa" field. - // This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) - // and/or WPA2 (full IEEE 802.11i/RSN): - // bit0 = WPA - // bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) - int wpa = 0; - - // Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The - // entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be - // added to enable SHA256-based stronger algorithms. - // WPA-PSK = WPA-Personal / WPA2-Personal - std::string wpa_key_mgmt; // default to empty -> delete from hostapd config - - // (dot11RSNAConfigPairwiseCiphersTable) - // Pairwise cipher for WPA (v1) (default: TKIP) - // wpa_pairwise=TKIP CCMP - // Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) - // rsn_pairwise=CCMP - std::string wpa_pairwise; // default to empty -> delete from hostapd config - - // WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit - // secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase - // (8..63 characters), wpa_passphrase. - std::string wpa_passphrase; - std::string wpa_psk; - - // ieee80211w: Whether management frame protection (MFP) is enabled - // 0 = disabled (default) - // 1 = optional - // 2 = required - std::string ieee80211w; - - // This parameter can be used to disable caching of PMKSA created through EAP - // authentication. RSN preauthentication may still end up using PMKSA caching if - // it is enabled (rsn_preauth=1). - // 0 = PMKSA caching enabled (default) - // 1 = PMKSA caching disabled - std::string disable_pmksa_caching; - - // Opportunistic Key Caching (aka Proactive Key Caching) - // Allow PMK cache to be shared opportunistically among configured interfaces - // and BSSes (i.e., all configurations within a single hostapd process). - // 0 = disabled (default) - // 1 = enabled - std::string okc; - - // This parameter can be used to disable retransmission of EAPOL-Key frames that - // are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This - // is similar to setting wpa_group_update_count=1 and - std::string wpa_disable_eapol_key_retries; - - // EasyMesh R1 only allows Open and WPA2 PSK auth&encryption methods. - // Quote: A Multi-AP Controller shall set the Authentication Type attribute - // in M2 to indicate WPA2-Personal or Open System Authentication. - // bss_it->authentication_type is a bitfield, but we are not going - // to accept any combinations due to the above limitation. - if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_OPEN) { - wpa = 0x0; - if (bss_it->encryption_type != WSC::eWscEncr::WSC_ENCR_NONE) { - LOG(ERROR) << "Autoconfiguration: " << vap << " encryption set on open VAP"; + // escape I + auto auth_type = + son::wireless_utils::wsc_to_bwl_authentication(bss_it->authentication_type); + if (auth_type == "INVALID") { + LOG(ERROR) << "Autoconfiguration: auth type is 'INVALID'; number: " + << (uint16_t)bss_it->authentication_type; abort = true; return; } - if (bss_it->network_key.length() > 0) { - LOG(ERROR) << "Autoconfiguration: " << vap << " network key set for open VAP"; + + // escape II + auto enc_type = son::wireless_utils::wsc_to_bwl_encryption(bss_it->encryption_type); + if (enc_type == "INVALID") { + LOG(ERROR) << "Autoconfiguration: enc_type is 'INVALID'; number: " + << int(bss_it->encryption_type); abort = true; return; } - } else if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_WPA2PSK) { - wpa = 0x2; - wpa_key_mgmt.assign("WPA-PSK"); - // Cipher must include AES for WPA2, TKIP is optional - if ((static_cast(bss_it->encryption_type) & static_cast(WSC::eWscEncr::WSC_ENCR_AES)) == - 0) { - LOG(ERROR) << "Autoconfiguration: " << vap - << " CCMP(AES) is required for WPA2"; + + // escape III + if (conf.get_vap_value(vap, "bssid").empty()) { + LOG(ERROR) << "Failed to get BSSID for vap: " << vap; abort = true; return; } - if ((uint16_t(bss_it->encryption_type) & uint16_t(WSC::eWscEncr::WSC_ENCR_TKIP)) != - 0) { - wpa_pairwise.assign("TKIP CCMP"); + + // settings + + // Hostapd "wpa" field. + // This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) + // and/or WPA2 (full IEEE 802.11i/RSN): + // bit0 = WPA + // bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) + int wpa = 0; + + // Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The + // entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be + // added to enable SHA256-based stronger algorithms. + // WPA-PSK = WPA-Personal / WPA2-Personal + std::string wpa_key_mgmt; // default to empty -> delete from hostapd config + + // (dot11RSNAConfigPairwiseCiphersTable) + // Pairwise cipher for WPA (v1) (default: TKIP) + // wpa_pairwise=TKIP CCMP + // Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) + // rsn_pairwise=CCMP + std::string wpa_pairwise; // default to empty -> delete from hostapd config + + // WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit + // secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase + // (8..63 characters), wpa_passphrase. + std::string wpa_passphrase; + std::string wpa_psk; + + // ieee80211w: Whether management frame protection (MFP) is enabled + // 0 = disabled (default) + // 1 = optional + // 2 = required + std::string ieee80211w; + + // This parameter can be used to disable caching of PMKSA created through EAP + // authentication. RSN preauthentication may still end up using PMKSA caching if + // it is enabled (rsn_preauth=1). + // 0 = PMKSA caching enabled (default) + // 1 = PMKSA caching disabled + std::string disable_pmksa_caching; + + // Opportunistic Key Caching (aka Proactive Key Caching) + // Allow PMK cache to be shared opportunistically among configured interfaces + // and BSSes (i.e., all configurations within a single hostapd process). + // 0 = disabled (default) + // 1 = enabled + std::string okc; + + // This parameter can be used to disable retransmission of EAPOL-Key frames that + // are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This + // is similar to setting wpa_group_update_count=1 and + std::string wpa_disable_eapol_key_retries; + + // EasyMesh R1 only allows Open and WPA2 PSK auth&encryption methods. + // Quote: A Multi-AP Controller shall set the Authentication Type attribute + // in M2 to indicate WPA2-Personal or Open System Authentication. + // bss_it->authentication_type is a bitfield, but we are not going + // to accept any combinations due to the above limitation. + if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_OPEN) { + wpa = 0x0; + if (bss_it->encryption_type != WSC::eWscEncr::WSC_ENCR_NONE) { + LOG(ERROR) << "Autoconfiguration: " << vap << " encryption set on open VAP"; + abort = true; + return; + } + if (bss_it->network_key.length() > 0) { + LOG(ERROR) + << "Autoconfiguration: " << vap << " network key set for open VAP"; + abort = true; + return; + } + } else if (bss_it->authentication_type == WSC::eWscAuth::WSC_AUTH_WPA2PSK) { + wpa = 0x2; + wpa_key_mgmt.assign("WPA-PSK"); + // Cipher must include AES for WPA2, TKIP is optional + if ((static_cast(bss_it->encryption_type) & + static_cast(WSC::eWscEncr::WSC_ENCR_AES)) == 0) { + LOG(ERROR) + << "Autoconfiguration: " << vap << " CCMP(AES) is required for WPA2"; + abort = true; + return; + } + if ((uint16_t(bss_it->encryption_type) & + uint16_t(WSC::eWscEncr::WSC_ENCR_TKIP)) != 0) { + wpa_pairwise.assign("TKIP CCMP"); + } else { + wpa_pairwise.assign("CCMP"); + } + if (bss_it->network_key.length() < 8 || bss_it->network_key.length() > 64) { + LOG(ERROR) << "Autoconfiguration: " << vap << " invalid network key length " + << bss_it->network_key.length(); + abort = true; + return; + } + if (bss_it->network_key.length() < 64) { + wpa_passphrase.assign(bss_it->network_key); + } else { + wpa_psk.assign(bss_it->network_key); + } + ieee80211w.assign("0"); + disable_pmksa_caching.assign("1"); + okc.assign("0"); + wpa_disable_eapol_key_retries.assign("0"); } else { - wpa_pairwise.assign("CCMP"); - } - if (bss_it->network_key.length() < 8 || bss_it->network_key.length() > 64) { - LOG(ERROR) << "Autoconfiguration: " << vap << " invalid network key length " - << bss_it->network_key.length(); + LOG(ERROR) << "Autoconfiguration: " << vap << " invalid authentication type"; abort = true; return; } - if (bss_it->network_key.length() < 64) { - wpa_passphrase.assign(bss_it->network_key); - } else { - wpa_psk.assign(bss_it->network_key); - } - ieee80211w.assign("0"); - disable_pmksa_caching.assign("1"); - okc.assign("0"); - wpa_disable_eapol_key_retries.assign("0"); - } else { - LOG(ERROR) << "Autoconfiguration: " << vap << " invalid authentication type"; - abort = true; - return; - } - LOG(DEBUG) << "Autoconfiguration for ssid: " << bss_it->ssid - << " auth_type: " << auth_type << " encr_type: " << enc_type - << " network_key: " << bss_it->network_key - << " fronthaul=" << beerocks::string_utils::bool_str(bss_it->fronthaul) - << " backhaul=" << beerocks::string_utils::bool_str(bss_it->backhaul); - - conf.set_create_vap_value(vap, "wps_state", bss_it->fronthaul ? "2" : ""); - conf.set_create_vap_value(vap, "wps_independent", "0"); - conf.set_create_vap_value(vap, "sFourAddrMode", bss_it->backhaul ? "1" : ""); - conf.set_create_vap_value(vap, "max_num_sta", bss_it->backhaul ? "1" : ""); - - // oddly enough, multi_ap_backhaul_wpa_passphrase has to be - // quoted, while wpa_passphrase does not... - if (bss_it->fronthaul && !backhaul_wps_ssid.empty()) { - conf.set_create_vap_value(vap, "multi_ap_backhaul_ssid", - "\"" + backhaul_wps_ssid + "\""); - conf.set_create_vap_value(vap, "multi_ap_backhaul_wpa_passphrase", - backhaul_wps_passphrase); - } + LOG(DEBUG) << "Autoconfiguration for ssid: " << bss_it->ssid + << " auth_type: " << auth_type << " encr_type: " << enc_type + << " network_key: " << bss_it->network_key + << " fronthaul=" << beerocks::string_utils::bool_str(bss_it->fronthaul) + << " backhaul=" << beerocks::string_utils::bool_str(bss_it->backhaul); + + conf.set_create_vap_value(vap, "ssid", bss_it->ssid); + conf.set_create_vap_value(vap, "wps_state", bss_it->fronthaul ? "2" : ""); + conf.set_create_vap_value(vap, "wps_independent", "0"); + conf.set_create_vap_value(vap, "max_num_sta", bss_it->backhaul ? "1" : ""); + + // oddly enough, multi_ap_backhaul_wpa_passphrase has to be + // quoted, while wpa_passphrase does not... + if (bss_it->fronthaul && !backhaul_wps_ssid.empty()) { + conf.set_create_vap_value(vap, "multi_ap_backhaul_ssid", + "\"" + backhaul_wps_ssid + "\""); + conf.set_create_vap_value(vap, "multi_ap_backhaul_wpa_passphrase", + backhaul_wps_passphrase); + } - conf.set_create_vap_value(vap, "wpa", wpa); - conf.set_create_vap_value(vap, "okc", okc); - conf.set_create_vap_value(vap, "wpa_key_mgmt", wpa_key_mgmt); - conf.set_create_vap_value(vap, "wpa_pairwise", wpa_pairwise); - conf.set_create_vap_value(vap, "wpa_psk", wpa_psk); - conf.set_create_vap_value(vap, "ieee80211w", ieee80211w); - conf.set_create_vap_value(vap, "wpa_passphrase", wpa_passphrase); - conf.set_create_vap_value(vap, "disable_pmksa_caching", disable_pmksa_caching); - conf.set_create_vap_value(vap, "wpa_disable_eapol_key_retries", - wpa_disable_eapol_key_retries); + // remove when not needed + if (!bss_it->fronthaul && backhaul_wps_ssid.empty()) { + conf.set_create_vap_value(vap, "multi_ap_backhaul_ssid", ""); + conf.set_create_vap_value(vap, "multi_ap_backhaul_wpa_passphrase", ""); + } - // finally enable the vap (remove any previously set start_disabled) - conf.set_create_vap_value(vap, "start_disabled", ""); + conf.set_create_vap_value(vap, "wpa", wpa); + conf.set_create_vap_value(vap, "okc", okc); + conf.set_create_vap_value(vap, "wpa_key_mgmt", wpa_key_mgmt); + conf.set_create_vap_value(vap, "wpa_pairwise", wpa_pairwise); + conf.set_create_vap_value(vap, "wpa_psk", wpa_psk); + conf.set_create_vap_value(vap, "ieee80211w", ieee80211w); + conf.set_create_vap_value(vap, "wpa_passphrase", wpa_passphrase); + conf.set_create_vap_value(vap, "disable_pmksa_caching", disable_pmksa_caching); + conf.set_create_vap_value(vap, "wpa_disable_eapol_key_retries", + wpa_disable_eapol_key_retries); + + // finally enable the vap (remove any previously set start_disabled and uncomment) + conf.set_create_vap_value(vap, "start_disabled", ""); + conf.uncomment_vap(vap); - } else { - // no more data in the input bss-conf list - // disable the rest of the vaps - conf.set_create_vap_value(vap, "start_disabled", 1); - conf.set_create_vap_value(vap, "ssid", ""); - } - }; + } else { + // no more data in the input bss-conf list + // disable the rest of the vaps + conf.comment_vap(vap); + } + }; conf.for_all_ap_vaps(configure_func, bss_info_conf_list.begin(), bss_info_conf_list.end(), [](const std::string &) { return true; }); @@ -561,7 +538,7 @@ bool ap_wlan_hal_nl80211::update_vap_credentials( if (!wpa_ctrl_send_msg(cmd)) { LOG(ERROR) << "Autoconfiguration: \"" << cmd << "\" command to hostapd has failed"; return false; - } + } LOG(DEBUG) << "Autoconfiguration: done:\n" << conf; return true; diff --git a/common/beerocks/hostapd/include/hostapd/configuration.h b/common/beerocks/hostapd/include/hostapd/configuration.h index ede822d8f5..9a29be01bf 100644 --- a/common/beerocks/hostapd/include/hostapd/configuration.h +++ b/common/beerocks/hostapd/include/hostapd/configuration.h @@ -22,7 +22,7 @@ class Configuration { * @brief construct a Configurationn object * @param file_name the name of the configuration file. */ - Configuration(const std::string &file_name); + explicit Configuration(const std::string &file_name); ~Configuration() = default; Configuration(const Configuration &) = default; @@ -230,7 +230,7 @@ class Configuration { // simply by reading the value of this variable. // the access to it is via operator bool() // m_ok itslef may be changed because of a call to - // const function, therefore it is mutable + // may be changed because of const functions, therefore mutable mutable bool m_ok = false; // each string is a line in the original configuration file diff --git a/common/beerocks/hostapd/source/configuration.cpp b/common/beerocks/hostapd/source/configuration.cpp index 223d8db5e9..99f96f1782 100644 --- a/common/beerocks/hostapd/source/configuration.cpp +++ b/common/beerocks/hostapd/source/configuration.cpp @@ -103,6 +103,9 @@ bool Configuration::store() } } + m_ok = true; + m_last_message = m_configuration_file + " was stored"; + // close the file out_file.close(); if (out_file.fail()) { @@ -110,7 +113,6 @@ bool Configuration::store() m_ok = false; } - m_ok = true; return *this; } diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index 890e7703a7..ec97b4b296 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -192,6 +192,8 @@ void clean_start() tmp.flush(); } +// Suppress cppcheck syntax error for gtest TEST macro +// cppcheck-suppress syntaxError TEST(configuration_test, load) { //// start prerequsite //// From 659da87287ba3571435290d777a6aa96991adb25 Mon Sep 17 00:00:00 2001 From: Ran Regev Date: Thu, 13 Aug 2020 15:36:56 +0300 Subject: [PATCH 28/28] hostapd: configuration: tests: turris omnia Set the unit tests file to completely match turris omnia configuration as found in the CI. this trail-and-error approach for working with hostapd configurartion is the best that we can do now. Signed-off-by: Ran Regev --- .../hostapd/unit_tests/configuration_test.cpp | 285 ++++++------------ 1 file changed, 96 insertions(+), 189 deletions(-) diff --git a/common/beerocks/hostapd/unit_tests/configuration_test.cpp b/common/beerocks/hostapd/unit_tests/configuration_test.cpp index ec97b4b296..a85e5ded56 100644 --- a/common/beerocks/hostapd/unit_tests/configuration_test.cpp +++ b/common/beerocks/hostapd/unit_tests/configuration_test.cpp @@ -13,176 +13,85 @@ namespace { -//const std::string vap_indication("bss="); -const std::string vap_indication("interface="); +const std::string vap_indication_1("bss="); +const std::string vap_indication_2("interface="); const std::string configuration_path("/tmp/"); const std::string configuration_file_name("omnia.conf"); const std::string configuration_content( "driver=nl80211\n" "logger_syslog=127\n" - "logger_syslog_level=2\n" + "logger_syslog_level=1\n" "logger_stdout=127\n" - "logger_stdout_level=2\n" - "sDisableMasterVap=1\n" - "atf_config_file=/var/run/hostapd-phy0-atf.conf\n" - "country_code=US\n" - "ieee80211d=1\n" - "hw_mode=g\n" + "logger_stdout_level=7\n" + "hw_mode=a\n" "beacon_int=100\n" - "channel=acs_smart\n" + "channel=44\n" + "tx_queue_data2_burst=2.0\n" "ieee80211n=1\n" - "ht_capab=[LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][MAX-AMSDU-7935][DSSS_CCK-40]\n" - "vht_capab=[RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][SU-BEAMFORMER][SU-BEAMFORMEE][MU-" - "BEAMFORMER][VHT-TXOP-PS][VHT160][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7][BF-ANTENNA-4][SOUNDING-" - "DIMENSION-2]\n" - "ieee80211ax=1\n" - "acs_num_scans=1\n" - "acs_smart_info_file=/var/run/acs_smart_info_wlan0.txt\n" - "acs_history_file=/var/run/acs_history_wlan0.txt\n" - "interface=wlan0\n" - "ctrl_interface=/var/run/hostapd\n" - "ap_isolate=1\n" - "rrm_neighbor_report=1\n" - "bss_transition=1\n" - "interworking=1\n" - "disassoc_low_ack=1\n" - "preamble=1\n" - "wmm_enabled=1\n" - "ignore_broadcast_ssid=1\n" - "uapsd_advertisement_enabled=1\n" - "mbo=1\n" - "vendor_elements=dd050009860100\n" - "vendor_vht=1\n" - "auth_algs=1\n" - "wpa=0\n" - "ssid=dummy_ssid_0\n" - "bridge=br-lan\n" - "bssid=02:9A:96:FB:59:0F\n" + + "ht_coex=0\n" + "ht_capab=[HT40+][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935][DSSS_CCK-40]\n" + "vht_oper_chwidth=1\n" + "vht_oper_centr_freq_seg0_idx=42\n" + "ieee80211ac=1\n" + "vht_capab=[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][RX-STBC-1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]\n" - vap_indication + - "wlan0.0\n" + "interface=wlan0\n" "ctrl_interface=/var/run/hostapd\n" "ap_isolate=1\n" - "ap_max_inactivity=60\n" - "rrm_neighbor_report=1\n" - "bss_transition=1\n" - "interworking=1\n" + "bss_load_update_period=60\n" + "chan_util_avg_period=600\n" "disassoc_low_ack=1\n" "preamble=1\n" "wmm_enabled=1\n" - "mode=ap\n" "ignore_broadcast_ssid=0\n" "uapsd_advertisement_enabled=1\n" - "mbo=1\n" - "vendor_elements=dd050009860100\n" - "vendor_vht=1\n" + "utf8_ssid=1\n" + "multi_ap=0\n" + "wpa_passphrase=prplBEE$\n" + "wpa_psk_file=/var/run/hostapd-wlan0.psk\n" "auth_algs=1\n" - "eap_server=1\n" - "device_type=6-0050F204-1\n" - "device_name=WLAN-ROUTER\n" - "manufacturer=Intel Corporation\n" - "config_methods=push_button\n" - "wps_independent=1\n" - "wps_cred_processing=1\n" - "os_version=01020300\n" - "manufacturer_url=http://www.intel.com\n" - "model_description=TR069 Gateway\n" - "wps_rf_bands=a\n" - "bridge=br-lan\n" - "bssid=02:9A:96:FB:59:11\n" - "ssid=Multi-AP-24G-1\n" "wpa=2\n" - "okc=0\n" - "wpa_key_mgmt=WPA-PSK\n" "wpa_pairwise=CCMP\n" - "ieee80211w=0\n" - "wpa_passphrase=maprocks1\n" - "disable_pmksa_caching=1\n" - "wpa_disable_eapol_key_retries=0\n" - "wps_state=2\n" - "mesh_mode=fAP\n" - "multi_ap_backhaul_ssid=\"Multi-AP-24G-2\"\n" - "multi_ap_backhaul_wpa_passphrase=maprocks2\n" + - - vap_indication + - "wlan0.1\n" - "mode=non-ap\n" - "ctrl_interface=/var/run/hostapd\n" - "ap_isolate=1\n" - "ap_max_inactivity=60\n" - "rrm_neighbor_report=1\n" - "bss_transition=1\n" - "interworking=1\n" - "disassoc_low_ack=1\n" - "preamble=1\n" - "wmm_enabled=1\n" - "ignore_broadcast_ssid=0\n" - "uapsd_advertisement_enabled=1\n" - "mbo=1\n" - "vendor_elements=dd050009860100\n" - "vendor_vht=1\n" - "auth_algs=1\n" + "ssid=prplmesh-front\n" "bridge=br-lan\n" - "bssid=02:9A:96:FB:59:12\n" - "ssid=Multi-AP-24G-2\n" - "wpa=2\n" - "okc=0\n" + "wpa_disable_eapol_key_retries=0\n" "wpa_key_mgmt=WPA-PSK\n" - "wpa_pairwise=CCMP\n" - "ieee80211w=0\n" - "wpa_passphrase=maprocks2\n" + "okc=0\n" "disable_pmksa_caching=1\n" - "wpa_disable_eapol_key_retries=0\n" - "mesh_mode=bAP\n" - "sFourAddrMode=1\n" - "max_num_sta=1\n" - - "#" + - vap_indication + - "wlan0.2\n" - "#ctrl_interface=/var/run/hostapd\n" - "#ap_isolate=1\n" - "#ap_max_inactivity=60\n" - "#rrm_neighbor_report=1\n" - "#mode=ap\n" - "#bss_transition=1\n" - "#interworking=1\n" - "#disassoc_low_ack=1\n" - "#preamble=1\n" - "#wmm_enabled=1\n" - "#ignore_broadcast_ssid=0\n" - "#uapsd_advertisement_enabled=1\n" - "#mbo=1\n" - "#vendor_elements=dd050009860100\n" - "#vendor_vht=1\n" - "#auth_algs=1\n" - "#wpa=0\n" - "#bridge=br-lan\n" - "#bssid=02:9A:96:FB:59:13\n" - "#start_disabled=1\n" + - - vap_indication + - "wlan0.3\n" + "dynamic_vlan=0\n" + "vlan_naming=1\n" + "vlan_file=/var/run/hostapd-wlan0.vlan\n" + "bssid=04:f0:21:24:24:17\n" + + "bss=wlan0-1\n" "ctrl_interface=/var/run/hostapd\n" "ap_isolate=1\n" - "ap_max_inactivity=60\n" - "rrm_neighbor_report=1\n" - "bss_transition=1\n" - "interworking=1\n" + "bss_load_update_period=60\n" + "chan_util_avg_period=600\n" "disassoc_low_ack=1\n" "preamble=1\n" "wmm_enabled=1\n" "ignore_broadcast_ssid=0\n" "uapsd_advertisement_enabled=1\n" - "mbo=1\n" - "vendor_elements=dd050009860100\n" - "vendor_vht=1\n" + "utf8_ssid=1\n" + "multi_ap=0\n" + "wpa_passphrase=prplBEE$\n" + "wpa_psk_file=/var/run/hostapd-wlan0-1.psk\n" "auth_algs=1\n" - "wpa=0\n" + "wpa=2\n" + "wpa_pairwise=CCMP\n" + "ssid=prplmesh-back\n" "bridge=br-lan\n" - "bssid=02:9A:96:FB:59:14\n" - "start_disabled=1\n" - "mode=ap\n"); + "wpa_disable_eapol_key_retries=0\n" + "wpa_key_mgmt=WPA-PSK\n" + "okc=0\n" + "disable_pmksa_caching=1\n" + "dynamic_vlan=0\n" + "vlan_naming=1\n" + "vlan_file=/var/run/hostapd-wlan0-1.vlan\n" + "wds_sta=1\n" + "bssid=06:f0:21:24:24:17\n" +); void clean_start() { @@ -198,7 +107,7 @@ TEST(configuration_test, load) { //// start prerequsite //// - ////clean_start(); + clean_start(); //// end prerequsite //// @@ -207,7 +116,7 @@ TEST(configuration_test, load) EXPECT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); EXPECT_TRUE(conf) << conf; } @@ -216,24 +125,22 @@ TEST(configuration_test, store) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // load the dummy configuration file prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); - EXPECT_FALSE(conf) << conf; // construct a configuration ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); - ; + conf.load(vap_indication_1,vap_indication_2); ASSERT_TRUE(conf) << conf; //// end prerequsite //// // add a value to vap - conf.set_create_vap_value("wlan0.1", "was_i_stroed", "yes_you_were"); + conf.set_create_vap_value("wlan0-1", "was_i_stroed", "yes_you_were"); EXPECT_TRUE(conf) << conf; // store @@ -246,14 +153,14 @@ TEST(configuration_test, set_string_head_values) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -280,14 +187,14 @@ TEST(configuration_test, set_int_head_values) //// start prerequsite //// // save the content of the string (start clean) - //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -306,22 +213,22 @@ TEST(configuration_test, get_head_values) //// start prerequsite //// // save the content of the string (start clean) - //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; //// end prerequsite //// // get existing key - auto val = conf.get_head_value("acs_num_scans"); - EXPECT_EQ(val, "1"); + auto val = conf.get_head_value("logger_syslog"); + EXPECT_EQ(val, "127"); // get non existing key val = conf.get_head_value("out_of_office"); @@ -344,7 +251,7 @@ TEST(configuration_test, set_string_vap_values) ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ASSERT_TRUE(conf) << conf; //// end prerequsite //// @@ -355,15 +262,15 @@ TEST(configuration_test, set_string_vap_values) EXPECT_TRUE(conf) << conf; // replace existing value for existing key for existing vap - conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); + conf.set_create_vap_value("wlan0", "disassoc_low_ack", "734"); EXPECT_TRUE(conf) << conf; // add a key/value to exising vap - conf.set_create_vap_value("wlan0.3", "unit_test_ok", "true"); + conf.set_create_vap_value("wlan0", "unit_test_ok", "true"); EXPECT_TRUE(conf) << conf; // remove key/value from existing vap - conf.set_create_vap_value("wlan0.0", "vendor_elements", ""); + conf.set_create_vap_value("wlan0-1", "vendor_elements", ""); EXPECT_TRUE(conf) << conf; // set key/value for NON existing vap @@ -381,14 +288,14 @@ TEST(configuration_test, set_int_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -397,17 +304,17 @@ TEST(configuration_test, set_int_vap_values) //// start test //// // replace existing value for existing key for existing vap - conf.set_create_vap_value("wlan0.1", "ignore_broadcast_ssid", 42); + conf.set_create_vap_value("wlan0-1", "ignore_broadcast_ssid", 42); EXPECT_TRUE(conf) << conf; // add a key/value to exising vap - conf.set_create_vap_value("wlan0.3", "i_am_negative", -24); + conf.set_create_vap_value("wlan0", "i_am_negative", -24); EXPECT_TRUE(conf) << conf; // try to replace existing value for existing key for existing vap // with NON-int value. we expect the value to be trancated here // at the caller site - conf.set_create_vap_value("wlan0.2", "wmm_enabled", 333.444); + conf.set_create_vap_value("wlan0", "wmm_enabled", 333.444); EXPECT_TRUE(conf) << conf; //// end test //// @@ -418,14 +325,14 @@ TEST(configuration_test, get_vap_values) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -436,12 +343,12 @@ TEST(configuration_test, get_vap_values) std::string value; // get existing value for existing vap - value = conf.get_vap_value("wlan0.1", "wpa_passphrase"); - EXPECT_EQ(value, "maprocks2") << conf; + value = conf.get_vap_value("wlan0", "wpa_passphrase"); + EXPECT_EQ(value, "prplBEE$") << conf; // another check - existing value for existing vap - value = conf.get_vap_value("wlan0.1", "bssid"); - EXPECT_EQ(value, "02:9A:96:FB:59:12"); + value = conf.get_vap_value("wlan0", "bssid"); + EXPECT_EQ(value, "04:f0:21:24:24:17"); // get NON existing value for existing vap value = conf.get_vap_value("wlan0.1", "does_not_exist"); @@ -459,14 +366,14 @@ TEST(configuration_test, disable_all_ap) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -506,14 +413,14 @@ TEST(configuration_test, enable_all_ap) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -547,14 +454,14 @@ TEST(configuration_test, comment_vap) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -562,7 +469,7 @@ TEST(configuration_test, comment_vap) //// start test //// - conf.comment_vap("wlan0.1"); + conf.comment_vap("wlan0"); EXPECT_TRUE(conf) << conf; conf.store(); @@ -576,27 +483,27 @@ TEST(configuration_test, uncomment_vap) //// start prerequsite //// // save the content of the string (start clean) - // //clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; // comment twice! - conf.comment_vap("wlan0.1"); - conf.comment_vap("wlan0.1"); + conf.comment_vap("wlan0"); + conf.comment_vap("wlan0"); EXPECT_TRUE(conf) << conf; conf.store(); EXPECT_TRUE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); + conf.load(vap_indication_1,vap_indication_2); ; ASSERT_TRUE(conf) << conf; @@ -604,25 +511,25 @@ TEST(configuration_test, uncomment_vap) //// start test //// - conf.uncomment_vap("wlan0.1"); + conf.uncomment_vap("wlan0"); EXPECT_TRUE(conf) << conf; - conf.uncomment_vap("wlan0.2"); + conf.uncomment_vap("wlan0"); EXPECT_TRUE(conf) << conf; conf.store(); EXPECT_TRUE(conf) << conf; // replace existing value for existing key for existing vap - conf.set_create_vap_value("wlan0.2", "disassoc_low_ack", "734"); + conf.set_create_vap_value("wlan0", "disassoc_low_ack", "734"); EXPECT_TRUE(conf) << conf; // add a key/value to exising vap - conf.set_create_vap_value("wlan0.3", "unit_test_ok", "true"); + conf.set_create_vap_value("wlan0", "unit_test_ok", "true"); EXPECT_TRUE(conf) << conf; // remove key/value from existing vap - conf.set_create_vap_value("wlan0.0", "vendor_elements", ""); + conf.set_create_vap_value("wlan0-1", "preamble", ""); EXPECT_TRUE(conf) << conf; // set key/value for NON existing vap @@ -637,15 +544,15 @@ TEST(configuration_test, itererate_both_containers) //// start prerequsite //// // save the content of the string (start clean) - ////clean_start(); + clean_start(); // construct a configuration prplmesh::hostapd::Configuration conf(configuration_path + configuration_file_name); ASSERT_FALSE(conf) << conf; // load the dummy configuration file - conf.load(vap_indication); - ; + conf.load(vap_indication_1,vap_indication_2); + ASSERT_TRUE(conf) << conf; //// end prerequsite ////