Skip to content

Commit

Permalink
Merge pull request #308 from microsoft/fixwpacontrollertests
Browse files Browse the repository at this point in the history
Make wpa-controller tests deterministic
  • Loading branch information
abeltrano authored Jul 2, 2024
2 parents 33dfe17 + 9417de0 commit 5b8a329
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/linux/external/hostap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})

Expand Down
23 changes: 23 additions & 0 deletions src/linux/wpa-controller/include/Wpa/ProtocolWpaConfig.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#error "CONFIG_WPA_CONTROL_SOCKET_PATH_BASE must be defined."
#endif

#include <Wpa/WpaCore.hxx>

namespace Wpa
{
/**
Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/linux/wpa-controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ target_link_libraries(wpa-controller-test-unit
)

configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/detail/config/hostapd.conf.format.hxx.in
${CMAKE_CURRENT_BINARY_DIR}/hostapd.conf.format.hxx
${CMAKE_CURRENT_SOURCE_DIR}/detail/config/HostapdBinaryInfo.hxx.in
${CMAKE_CURRENT_BINARY_DIR}/HostapdBinaryInfo.hxx
@ONLY
)

Expand Down
96 changes: 58 additions & 38 deletions tests/unit/linux/wpa-controller/detail/WpaDaemonManager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,72 +10,89 @@
#include <stdexcept>
#include <string_view>

#include "WpaDaemonManager.hxx"
#include <Wpa/ProtocolWpaConfig.hxx>
#include <Wpa/WpaCore.hxx>
#include <magic_enum.hpp>
#include <plog/Log.h>

#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
* daemon type to the specified file stream.
*
* @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

/* 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)
{
// 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();

Expand All @@ -92,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" : "";
Expand Down Expand Up @@ -147,13 +173,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 */
Expand Down
9 changes: 0 additions & 9 deletions tests/unit/linux/wpa-controller/detail/WpaDaemonManager.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <filesystem>

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

This file was deleted.

0 comments on commit 5b8a329

Please sign in to comment.