From 590e987a3f9a627b9ad2881d01009065e2db18db Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 21:40:57 +0000 Subject: [PATCH 1/4] Specify hostapd daemon binary deterministrically. --- src/linux/external/hostap/CMakeLists.txt | 2 +- .../unit/linux/wpa-controller/CMakeLists.txt | 6 ++++ .../detail/WpaDaemonManager.cxx | 31 ++----------------- .../detail/WpaDaemonManager.hxx | 9 ------ .../detail/config/HostapdBinaryInfo.hxx.in | 23 ++++++++++++++ 5 files changed, 32 insertions(+), 39 deletions(-) create mode 100644 tests/unit/linux/wpa-controller/detail/config/HostapdBinaryInfo.hxx.in diff --git a/src/linux/external/hostap/CMakeLists.txt b/src/linux/external/hostap/CMakeLists.txt index 1e34e1bc..4090fc9f 100644 --- a/src/linux/external/hostap/CMakeLists.txt +++ b/src/linux/external/hostap/CMakeLists.txt @@ -22,7 +22,7 @@ set(HOSTAP_TMP_DIR ${HOSTAP_PREFIX}/tmp) set(HOSTAP_STAMP_DIR ${HOSTAP_PREFIX}/src/${HOSTAP_EP_NAME}-stamp) set(HOSTAP_DOWNLOAD_DIR ${HOSTAP_PREFIX}/src) set(HOSTAP_SOURCE_DIR ${HOSTAP_PREFIX}/src/${HOSTAP_EP_NAME}) -set(HOSTAP_BINARY_DIR ${HOSTAP_PREFIX}/src/${HOSTAP_EP_NAME}-build) +set(HOSTAP_BINARY_DIR ${HOSTAP_SOURCE_DIR}/hostapd CACHE FILEPATH "hostapd binary directory" FORCE) set(HOSTAP_INSTALL_DIR ${HOSTAP_PREFIX}) set(HOSTAP_LOG_DIR ${HOSTAP_STAMP_DIR}) diff --git a/tests/unit/linux/wpa-controller/CMakeLists.txt b/tests/unit/linux/wpa-controller/CMakeLists.txt index f3d4c3da..3f5d37e1 100644 --- a/tests/unit/linux/wpa-controller/CMakeLists.txt +++ b/tests/unit/linux/wpa-controller/CMakeLists.txt @@ -32,4 +32,10 @@ configure_file( @ONLY ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/detail/config/HostapdBinaryInfo.hxx.in + ${CMAKE_CURRENT_BINARY_DIR}/HostapdBinaryInfo.hxx + @ONLY +) + catch_discover_tests(wpa-controller-test-unit) diff --git a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx index b730ae7e..6088137f 100644 --- a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx +++ b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx @@ -16,6 +16,7 @@ #include #include "hostapd.conf.format.hxx" +#include "HostapdBinaryInfo.hxx" namespace detail { @@ -43,28 +44,6 @@ WriteDefaultConfigurationFileContents(Wpa::WpaType wpaType, std::string_view int } } // namespace detail -/* static */ -std::filesystem::path -WpaDaemonManager::FindDaemonBinary(Wpa::WpaType wpaType, const std::filesystem::path& searchPath) -{ - using std::filesystem::perms; - - const auto daemon = Wpa::GetWpaTypeDaemonBinaryName(wpaType); - - LOGI << std::format("Searching for hostapd daemon binary '{}' in '{}'\n", daemon, searchPath.c_str()); - - for (const auto& directoryEntry : std::filesystem::recursive_directory_iterator(searchPath)) { - if (directoryEntry.is_regular_file() && directoryEntry.path().filename() == daemon) { - const auto permissions = directoryEntry.status().permissions(); - if ((permissions & (perms::owner_exec | perms::group_exec | perms::others_exec)) != perms::none) { - return directoryEntry.path(); - } - } - } - - return {}; -} - /* static */ std::filesystem::path WpaDaemonManager::CreateAndWriteDefaultConfigurationFile(Wpa::WpaType wpaType, std::string_view interfaceName) @@ -147,13 +126,7 @@ WpaDaemonManager::StartDefault(Wpa::WpaType wpaType, std::string_view interfaceN return std::nullopt; } - auto daemonFilePath = FindDaemonBinary(wpaType); - if (daemonFilePath.empty()) { - LOGE << std::format("Failed to find wpa '{}' daemon binary\n", magic_enum::enum_name(wpaType)); - return std::nullopt; - } - - return Start(wpaType, interfaceName, daemonFilePath, configurationFilePath); + return Start(wpaType, interfaceName, detail::HostapdBinaryPath, configurationFilePath); } /* static */ diff --git a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.hxx b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.hxx index 03240b31..62abe784 100644 --- a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.hxx +++ b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.hxx @@ -36,15 +36,6 @@ struct WpaDaemonManager */ static constexpr auto ControlSocketPathBase{ "/run/" }; - /** - * @brief Attempts to find the binary for the specified wpa daemon type. - * - * @param wpaType The type of wpa daemon to find the binary for. - * @return std::filesystem::path The path to the daemon binary, if found. Otherwise, an empty path. - */ - static std::filesystem::path - FindDaemonBinary(Wpa::WpaType wpaType, const std::filesystem::path& searchPath = std::filesystem::current_path()); - /** * @brief Create and write a default configuration file to disk for the * specified wpa daemon type. The configuration file will be written to the diff --git a/tests/unit/linux/wpa-controller/detail/config/HostapdBinaryInfo.hxx.in b/tests/unit/linux/wpa-controller/detail/config/HostapdBinaryInfo.hxx.in new file mode 100644 index 00000000..ee7bbeb2 --- /dev/null +++ b/tests/unit/linux/wpa-controller/detail/config/HostapdBinaryInfo.hxx.in @@ -0,0 +1,23 @@ + +#ifndef HOSTAPD_BINARY_INFO_HXX +#define HOSTAPD_BINARY_INFO_HXX + +/** + * Note: this is a generated file. Do not attempt to change it as any changes will be lost. + */ +#include + +namespace detail +{ +/** + * @brief The path to hostapd binaries. + */ +static const std::filesystem::path HostapdBinaryDirectory{ "@HOSTAP_BINARY_DIR@" }; + +/** + * @brief The path to the hostapd daemon binary. + */ +static const std::filesystem::path HostapdBinaryPath{ HostapdBinaryDirectory / "hostapd" }; +} // namespace detail + +#endif // HOSTAPD_BINARY_INFO_HXX From 64ab2c49013912f483d86111d91395c2a7c8056d Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 22:41:11 +0000 Subject: [PATCH 2/4] Add helper to obtain fixed wpa control socket path. --- .../include/Wpa/ProtocolWpaConfig.hxx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/linux/wpa-controller/include/Wpa/ProtocolWpaConfig.hxx b/src/linux/wpa-controller/include/Wpa/ProtocolWpaConfig.hxx index daacd647..aa59fe42 100644 --- a/src/linux/wpa-controller/include/Wpa/ProtocolWpaConfig.hxx +++ b/src/linux/wpa-controller/include/Wpa/ProtocolWpaConfig.hxx @@ -6,6 +6,8 @@ #error "CONFIG_WPA_CONTROL_SOCKET_PATH_BASE must be defined." #endif +#include + namespace Wpa { /** @@ -29,6 +31,27 @@ struct ProtocolWpaConfig * @brief The path to the control sockets used by hostapd. */ static constexpr auto ControlSocketPathHostapd{ CONFIG_WPA_CONTROL_SOCKET_PATH_BASE "/hostapd" }; + + /** + * @brief Get the control socket path for the specified WPA type. + * + * @param wpaType The type of WPA daemon to get the control socket path for. + * @return constexpr auto The control socket path for the specified WPA type. + */ + static constexpr auto + GetControlSocketPath(WpaType wpaType) + { + switch (wpaType) { + case WpaType::Hostapd: + return ControlSocketPathHostapd; + case WpaType::WpaSupplicant: + return ControlSocketPathWpaSupplicant; + case WpaType::Unknown: + [[fallthrough]]; + default: + return ControlSocketPathBase; + } + } }; } // namespace Wpa From 97d24b3ab187ba89f855447fb7d5fc14bcdc501b Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 22:41:25 +0000 Subject: [PATCH 3/4] Remove now unnecessary configure file. --- .../unit/linux/wpa-controller/CMakeLists.txt | 6 ----- .../detail/config/hostapd.conf.format.hxx.in | 24 ------------------- 2 files changed, 30 deletions(-) delete mode 100644 tests/unit/linux/wpa-controller/detail/config/hostapd.conf.format.hxx.in diff --git a/tests/unit/linux/wpa-controller/CMakeLists.txt b/tests/unit/linux/wpa-controller/CMakeLists.txt index 3f5d37e1..a439a21a 100644 --- a/tests/unit/linux/wpa-controller/CMakeLists.txt +++ b/tests/unit/linux/wpa-controller/CMakeLists.txt @@ -26,12 +26,6 @@ target_link_libraries(wpa-controller-test-unit wpa-controller ) -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/detail/config/hostapd.conf.format.hxx.in - ${CMAKE_CURRENT_BINARY_DIR}/hostapd.conf.format.hxx - @ONLY -) - configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/detail/config/HostapdBinaryInfo.hxx.in ${CMAKE_CURRENT_BINARY_DIR}/HostapdBinaryInfo.hxx diff --git a/tests/unit/linux/wpa-controller/detail/config/hostapd.conf.format.hxx.in b/tests/unit/linux/wpa-controller/detail/config/hostapd.conf.format.hxx.in deleted file mode 100644 index ca9cb8f8..00000000 --- a/tests/unit/linux/wpa-controller/detail/config/hostapd.conf.format.hxx.in +++ /dev/null @@ -1,24 +0,0 @@ - -#ifndef HOSTAPD_CONF_FORMAT_HXX -#define HOSTAPD_CONF_FORMAT_HXX - -namespace detail -{ -static constexpr auto WpaDaemonHostapdConfigurationFileContentsFormat = R"CONFIG( -interface={} -driver=nl80211 -ctrl_interface=@CMAKE_INSTALL_FULL_RUNSTATEDIR@/hostapd -ssid=wificontrollertest -hw_mode=g -channel=1 -auth_algs=3 -wpa=2 -wpa_passphrase=password -wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE -wpa_pairwise=TKIP CCMP -rsn_pairwise=CCMP -)CONFIG"; - -} // namespace detail - -#endif // HOSTAPD_CONF_FORMAT_HXX From 9417de0146e910eec16990957f85e8336218b6e3 Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 22:41:50 +0000 Subject: [PATCH 4/4] Create control socket directory if not exists. --- .../detail/WpaDaemonManager.cxx | 65 ++++++++++++++++--- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx index 6088137f..fc57b8b5 100644 --- a/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx +++ b/tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx @@ -10,16 +10,49 @@ #include #include -#include "WpaDaemonManager.hxx" +#include #include #include #include -#include "hostapd.conf.format.hxx" #include "HostapdBinaryInfo.hxx" +#include "WpaDaemonManager.hxx" namespace detail { +/** + * @brief Format string for the default wpa_supplicant configuration file contents. + * + * There are 1 arguments expected to be substituted into the format string: + * + * 1. The control interface path. + */ +static constexpr auto WpaDaemonWpaSupplicantConfigurationFileContentsFormat = R"CONFIG( +ctrl_interface={} +)CONFIG"; + +/** + * @brief Format string for the default hostapd configuration file contents. + * + * There are 2 argumentd expected to be substituted into the format string: + * + * 1. The wlan interface name. + * 2. The control interface path. + */ +static constexpr auto WpaDaemonHostapdConfigurationFileContentsFormat = R"CONFIG( +interface={} +driver=nl80211 +ctrl_interface={} +ssid=wificontrollertest +hw_mode=g +channel=1 +auth_algs=3 +wpa=2 +wpa_passphrase=password +wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE +wpa_pairwise=TKIP CCMP +rsn_pairwise=CCMP +)CONFIG"; /** * @brief Write the default configuration file contents for the specified wpa @@ -27,20 +60,24 @@ namespace detail * * @param wpaType The type of wpa daemon to write the configuration file for. * @param interfaceName The wlan interface the daemon will be managing. + * @param controlSocketPath The path to the control socket for the daemon. * @param configurationFile The file stream to write the configuration file to. */ void -WriteDefaultConfigurationFileContents(Wpa::WpaType wpaType, std::string_view interfaceName, std::ofstream& configurationFile) +WriteDefaultConfigurationFileContents(Wpa::WpaType wpaType, std::string_view interfaceName, std::string_view controlSocketPath, std::ofstream& configurationFile) { switch (wpaType) { - case Wpa::WpaType::Hostapd: { - configurationFile << std::format(WpaDaemonHostapdConfigurationFileContentsFormat, interfaceName); + case Wpa::WpaType::Hostapd: + configurationFile << std::format(WpaDaemonHostapdConfigurationFileContentsFormat, interfaceName, controlSocketPath); break; - } - default: { + case Wpa::WpaType::WpaSupplicant: + configurationFile << std::format(WpaDaemonWpaSupplicantConfigurationFileContentsFormat, controlSocketPath); + break; + case Wpa::WpaType::Unknown: + [[fallthrough]]; + default: throw std::runtime_error(std::format("Unsupported wpa daemon type '{}'", magic_enum::enum_name(wpaType))); } - } } } // namespace detail @@ -51,10 +88,11 @@ WpaDaemonManager::CreateAndWriteDefaultConfigurationFile(Wpa::WpaType wpaType, s // Determine which daemon to create the configuration file for. const auto daemon = Wpa::GetWpaTypeDaemonBinaryName(wpaType); const auto daemonConfigurationFilePath = std::filesystem::temp_directory_path() / std::format("{}.conf", daemon); + const auto daemonControlSocketPath{ Wpa::ProtocolWpaConfig::GetControlSocketPath(wpaType) }; // Create and write default configuration file contents. std::ofstream daemonConfigurationFile{ daemonConfigurationFilePath, std::ios::out | std::ios::trunc }; - detail::WriteDefaultConfigurationFileContents(wpaType, interfaceName, daemonConfigurationFile); + detail::WriteDefaultConfigurationFileContents(wpaType, interfaceName, daemonControlSocketPath, daemonConfigurationFile); daemonConfigurationFile.flush(); daemonConfigurationFile.close(); @@ -71,6 +109,15 @@ WpaDaemonManager::Start(Wpa::WpaType wpaType, std::string_view interfaceName, co interfaceName = WpaDaemonManager::InterfaceNameDefault; } + // Create the control socket path if it doesn't exist. + const std::filesystem::path controlSocketPath{ Wpa::ProtocolWpaConfig::GetControlSocketPath(wpaType) }; + if (!std::filesystem::exists(controlSocketPath)) { + if (!std::filesystem::create_directories(controlSocketPath)) { + LOGE << std::format("Failed to create control socket path '{}'\n", controlSocketPath.c_str()); + return std::nullopt; + } + } + // Determine which daemon to start and formulate daemon binary arguments. const auto daemon = Wpa::GetWpaTypeDaemonBinaryName(wpaType); const auto* configurationFileArgumentPrefix = (wpaType == Wpa::WpaType::WpaSupplicant) ? "-c" : "";