Skip to content
This repository was archived by the owner on Sep 7, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
51cc4b8
prplmesh-hostapd: info: added README.md file
RanRegev Jul 7, 2020
9738721
prplmesh-hostapd: added configuration class
RanRegev Jul 8, 2020
c7b6202
prplmesh-hostapd: added a unit test file
RanRegev Jul 8, 2020
fffd8df
prplmesh-hostapd: added cmake for prplmesh hostapd
RanRegev Jul 8, 2020
da8fc18
prplmesh-hostapd: added set_create_vap_value()
RanRegev Jul 9, 2020
39ac463
prplmesh-hostapd: added set_create_vap_value() overload with int
RanRegev Jul 9, 2020
e8d453c
prplmesh-hostapd: added store()
RanRegev Jul 9, 2020
589695b
prplmesh-hostapd: added get_vap_value()
RanRegev Jul 9, 2020
334ddef
prplmesh-hostapd: added disable_all_ap_vaps()
RanRegev Jul 12, 2020
90a228e
prplmesh-hostapd: added private help functions
RanRegev Jul 12, 2020
bdcf932
prplmesh-hostapd: added comment_vap()
RanRegev Jul 12, 2020
3984d9d
prplmesh-hostapd: added comment ability
RanRegev Jul 13, 2020
e58b302
prplmesh-hostapd: added uncomment_vap()
RanRegev Jul 13, 2020
8be8fa6
prplmesh-hostapd: added for_all_ap_vaps()
RanRegev Jul 13, 2020
465cfbc
prplmesh-hostapd: added for_all_ap_vaps() with user iterator
RanRegev Jul 13, 2020
757a5c0
prplmesh-hostapd: added test enable_all_vaps()
RanRegev Jul 15, 2020
93b265a
prplmesh-hostapd: added test iterate-both-containers
RanRegev Jul 15, 2020
b2ec175
prplmesh-hostapd: few cleanups after rebase
RanRegev Jul 15, 2020
f81b595
prplmesh-hostapd: added set_create_xx_head_value
RanRegev Jul 15, 2020
b45fedd
prplmesh-hostapd: added get_head_value()
RanRegev Jul 16, 2020
163a663
prplmesh-hostapd: added vap_indication to load()
RanRegev Jul 20, 2020
a76b023
prplmesh-hostapd: added ap predicate to for_all_ap_vaps
RanRegev Jul 20, 2020
6a6fa39
prplmesh-hostapd: PR 1514 review fixups
RanRegev Jul 30, 2020
930a2c0
prplmesh-hostapd: added ap predicate to for_all_ap_vaps
RanRegev Jul 20, 2020
56a5adf
nl80211: hostapd configuration: cmake
RanRegev Jul 23, 2020
0fac46d
nl80211: hostapd configuration update_vap_credentials()
RanRegev Jul 23, 2020
c515db9
nl80211: hostapd configuration: close issues found in PR 1550
RanRegev Jul 30, 2020
659da87
hostapd: configuration: tests: turris omnia
RanRegev Aug 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions common/beerocks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
add_dependencies(btl btlvf)
4 changes: 3 additions & 1 deletion common/beerocks/bwl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -179,7 +181,7 @@ if (BUILD_TESTS)
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
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 $<TARGET_FILE:${TEST_PROJECT_NAME}>)
Expand Down
284 changes: 278 additions & 6 deletions common/beerocks/bwl/nl80211/ap_wlan_hal_nl80211.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,21 @@
*/

#include "ap_wlan_hal_nl80211.h"

#include <bcl/beerocks_os_utils.h>
#include <bcl/beerocks_utils.h>
#include <bcl/beerocks_version.h>
#include <bcl/network/network_utils.h>
#include <bcl/son/son_wireless_utils.h>

#include <easylogging++.h>

#include <cmath>

#include <easylogging++.h>
#include <hostapd/configuration.h>
#include <linux/nl80211.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/genl.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <type_traits>

//////////////////////////////////////////////////////////////////////////////
////////////////////////// Local Module Definitions //////////////////////////
Expand Down Expand Up @@ -106,6 +104,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<std::string> 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 ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -268,7 +306,241 @@ bool ap_wlan_hal_nl80211::update_vap_credentials(
std::list<son::wireless_utils::sBssInfoConf> &bss_info_conf_list,
const std::string &backhaul_wps_ssid, const std::string &backhaul_wps_passphrase)
{
//TODO Implement #346
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
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::remove_reference<decltype(bss_info_conf_list)>::type::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;
}

// 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<uint16_t>(bss_it->encryption_type) &
static_cast<uint16_t>(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, "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);
}

// 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", "");
}

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.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; });

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;
}

Expand Down
48 changes: 48 additions & 0 deletions common/beerocks/hostapd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

project(prplmesh_hostapd VERSION ${prplmesh_VERSION})

# Set the base path for the current module
set(MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})

# 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})
target_include_directories(${PROJECT_NAME}
PRIVATE
${PLATFORM_INCLUDE_DIR}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if (BUILD_TESTS)
set(TEST_PROJECT_NAME ${PROJECT_NAME}_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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
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 $<TARGET_FILE:${TEST_PROJECT_NAME}>)
endif()
Loading