diff --git a/src/common/wifi/core/AccessPointOperationStatus.cxx b/src/common/wifi/core/AccessPointOperationStatus.cxx new file mode 100644 index 00000000..a0bdc2b9 --- /dev/null +++ b/src/common/wifi/core/AccessPointOperationStatus.cxx @@ -0,0 +1,16 @@ + +#include + +using namespace Microsoft::Net::Wifi; + +/* static */ +AccessPointOperationStatus +AccessPointOperationStatus::MakeSucceeded() noexcept +{ + return AccessPointOperationStatus{ AccessPointOperationStatusCode::Succeeded }; +} + +AccessPointOperationStatus::operator bool() const noexcept +{ + return (Code == AccessPointOperationStatusCode::Succeeded); +} diff --git a/src/common/wifi/core/CMakeLists.txt b/src/common/wifi/core/CMakeLists.txt index cd0d2a97..bbf872ba 100644 --- a/src/common/wifi/core/CMakeLists.txt +++ b/src/common/wifi/core/CMakeLists.txt @@ -10,12 +10,14 @@ target_sources(wifi-core AccessPoint.cxx AccessPointController.cxx AccessPointControllerException.cxx + AccessPointOperationStatus.cxx PUBLIC FILE_SET HEADERS BASE_DIRS ${WIFI_CORE_PUBLIC_INCLUDE} FILES ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPoint.hxx ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointController.hxx + ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointOperationStatus.hxx ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/IAccessPoint.hxx ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/IAccessPointController.hxx ${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/Ieee80211.hxx diff --git a/src/common/wifi/core/include/microsoft/net/wifi/AccessPointOperationStatus.hxx b/src/common/wifi/core/include/microsoft/net/wifi/AccessPointOperationStatus.hxx new file mode 100644 index 00000000..ec424b66 --- /dev/null +++ b/src/common/wifi/core/include/microsoft/net/wifi/AccessPointOperationStatus.hxx @@ -0,0 +1,66 @@ + +#ifndef ACCESS_POINT_OPERATION_STATUS_HXX +#define ACCESS_POINT_OPERATION_STATUS_HXX + +#include + +namespace Microsoft::Net::Wifi +{ +/** + * @brief High-level status reported by various access point operations. + */ +enum class AccessPointOperationStatusCode { + Unknown, + Succeeded, + InvalidParameter, + AccessPointInvalid, + AccessPointNotEnabled, + OperationNotSupported, + InternalError, +}; + +/** + * @brief Coarse status of an access point operation. + */ +struct AccessPointOperationStatus +{ + AccessPointOperationStatusCode Code{ AccessPointOperationStatusCode::Unknown }; + std::string Message; + + AccessPointOperationStatus() = default; + virtual ~AccessPointOperationStatus() = default; + + /** + * @brief Create an AccessPointOperationStatus with the given status code. + */ + constexpr explicit AccessPointOperationStatus(AccessPointOperationStatusCode code) noexcept : + Code{ code } + {} + + AccessPointOperationStatus(const AccessPointOperationStatus &) = default; + AccessPointOperationStatus(AccessPointOperationStatus &&) = default; + AccessPointOperationStatus & + operator=(const AccessPointOperationStatus &) = default; + AccessPointOperationStatus & + operator=(AccessPointOperationStatus &&) = default; + + /** + * @brief Create an AccessPointOperationStatus describing an operation that succeeded. + * + * @return AccessPointOperationStatus + */ + static AccessPointOperationStatus + MakeSucceeded() noexcept; + + /** + * @brief Implicit bool operator allowing AccessPointOperationStatus to be used directly in condition statements + * (eg. if (status) // ...). + * + * @return true + * @return false + */ + operator bool() const noexcept; // NOLINT(hicpp-explicit-conversions) +}; +} // namespace Microsoft::Net::Wifi + +#endif // ACCESS_POINT_OPERATION_STATUS_HXX diff --git a/src/common/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx b/src/common/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx index 0b388b30..15a7eda4 100644 --- a/src/common/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx +++ b/src/common/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -41,12 +42,14 @@ struct IAccessPointController virtual ~IAccessPointController() = default; /** - * Prevent copying and moving of IAccessPointController objects. + * Prevent copying and moving of IAccessPointController objects. */ IAccessPointController(const IAccessPointController&) = delete; - IAccessPointController& operator=(const IAccessPointController&) = delete; + IAccessPointController& + operator=(const IAccessPointController&) = delete; IAccessPointController(IAccessPointController&&) = delete; - IAccessPointController& operator=(IAccessPointController&&) = delete; + IAccessPointController& + operator=(IAccessPointController&&) = delete; /** * @brief Get the interface name associated with this controller. @@ -93,6 +96,15 @@ struct IAccessPointController */ virtual bool SetFrequencyBands(std::vector frequencyBands) = 0; + + /** + * @brief Set the SSID of the access point. + * + * @param ssid The SSID to be set. + * @return AccessPointOperationStatus + */ + virtual AccessPointOperationStatus + SetSssid(std::string_view ssid) = 0; }; /** @@ -105,12 +117,14 @@ struct IAccessPointControllerFactory virtual ~IAccessPointControllerFactory() = default; /** - * Prevent copying and moving of IAccessPointControllerFactory objects. + * Prevent copying and moving of IAccessPointControllerFactory objects. */ IAccessPointControllerFactory(const IAccessPointControllerFactory&) = delete; - IAccessPointControllerFactory& operator=(const IAccessPointControllerFactory&) = delete; + IAccessPointControllerFactory& + operator=(const IAccessPointControllerFactory&) = delete; IAccessPointControllerFactory(IAccessPointControllerFactory&&) = delete; - IAccessPointControllerFactory& operator=(IAccessPointControllerFactory&&) = delete; + IAccessPointControllerFactory& + operator=(IAccessPointControllerFactory&&) = delete; /** * @brief Create a new IAccessPointController object. diff --git a/src/common/wifi/core/include/microsoft/net/wifi/Ieee80211.hxx b/src/common/wifi/core/include/microsoft/net/wifi/Ieee80211.hxx index 0e725082..b53dae78 100644 --- a/src/common/wifi/core/include/microsoft/net/wifi/Ieee80211.hxx +++ b/src/common/wifi/core/include/microsoft/net/wifi/Ieee80211.hxx @@ -2,9 +2,11 @@ #ifndef IEEE_80211_HXX #define IEEE_80211_HXX +#include #include #include #include +#include #include @@ -268,6 +270,39 @@ enum class Ieee80211CipherSuite : uint32_t { Wep40 = MakeIeee80211Suite(Ieee80211CipherSuiteIdWep40), }; +/** + * @brief IEEE 802.11 BSS Types. + * + * Defined in IEEE 802.11-2020, Section 4.3. + */ +enum class Ieee80211BssType { + Unknown, + Infrastructure, // ESS + Independent, // IBSS + Personal, // PBSS + Mesh, // MBSS +}; + +/** + * @brief Number of octets per BSSID. + */ +static constexpr auto BssidNumOctets = 6; + +/** + * @brief BSSID type. + */ +using Ieee80211Bssid = std::array; + +/** + * @brief Information about a BSS. + */ +struct Ieee80211Bss +{ + Ieee80211BssType Type; + Ieee80211Bssid Bssid; + std::string Ssid; +}; + } // namespace Microsoft::Net::Wifi #endif // IEEE_80211_HXX diff --git a/src/common/wifi/dot11/adapter/Ieee80211Dot11Adapters.cxx b/src/common/wifi/dot11/adapter/Ieee80211Dot11Adapters.cxx index 54aa5bcf..0c7d9a17 100644 --- a/src/common/wifi/dot11/adapter/Ieee80211Dot11Adapters.cxx +++ b/src/common/wifi/dot11/adapter/Ieee80211Dot11Adapters.cxx @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,53 @@ namespace Microsoft::Net::Wifi { +using Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatusCode; +using Microsoft::Net::Wifi::AccessPointOperationStatusCode; + +WifiAccessPointOperationStatusCode +ToDot11AccessPointOperationStatusCode(AccessPointOperationStatusCode& accessPointOperationStatusCode) noexcept +{ + switch (accessPointOperationStatusCode) { + case AccessPointOperationStatusCode::Succeeded: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded; + case AccessPointOperationStatusCode::InvalidParameter: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeInvalidParameter; + case AccessPointOperationStatusCode::AccessPointInvalid: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeAccessPointInvalid; + case AccessPointOperationStatusCode::AccessPointNotEnabled: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeAccessPointNotEnabled; + case AccessPointOperationStatusCode::OperationNotSupported: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeOperationNotSupported; + case AccessPointOperationStatusCode::InternalError: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeInternalError; + case AccessPointOperationStatusCode::Unknown: + default: + return WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeUnknown; + } +} + +AccessPointOperationStatusCode +FromDot11AccessPointOperationStatusCode(WifiAccessPointOperationStatusCode wifiAccessPointOperationStatusCode) noexcept +{ + switch (wifiAccessPointOperationStatusCode) { + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded: + return AccessPointOperationStatusCode::Succeeded; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeInvalidParameter: + return AccessPointOperationStatusCode::InvalidParameter; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeAccessPointInvalid: + return AccessPointOperationStatusCode::AccessPointInvalid; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeAccessPointNotEnabled: + return AccessPointOperationStatusCode::AccessPointNotEnabled; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeOperationNotSupported: + return AccessPointOperationStatusCode::OperationNotSupported; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeInternalError: + return AccessPointOperationStatusCode::InternalError; + case WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeUnknown: + default: + return AccessPointOperationStatusCode::Unknown; + } +} + using Microsoft::Net::Wifi::Dot11PhyType; using Microsoft::Net::Wifi::Ieee80211Protocol; diff --git a/src/common/wifi/dot11/adapter/include/microsoft/net/wifi/Ieee80211Dot11Adapters.hxx b/src/common/wifi/dot11/adapter/include/microsoft/net/wifi/Ieee80211Dot11Adapters.hxx index 0722d813..275b7fc0 100644 --- a/src/common/wifi/dot11/adapter/include/microsoft/net/wifi/Ieee80211Dot11Adapters.hxx +++ b/src/common/wifi/dot11/adapter/include/microsoft/net/wifi/Ieee80211Dot11Adapters.hxx @@ -4,12 +4,32 @@ #include +#include #include +#include #include #include namespace Microsoft::Net::Wifi { +/** + * @brief Convert the specified WifiAccessPointOperationStatusCode to the equivalent AccessPointOperationStatus. + * + * @param accessPointOperationStatusCode The WifiAccessPointOperationStatusCode to convert. + * @return Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatusCode + */ +Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatusCode +ToDot11AccessPointOperationStatusCode(Microsoft::Net::Wifi::AccessPointOperationStatusCode& accessPointOperationStatusCode) noexcept; + +/** + * @brief Convert the specified AccessPointOperationStatus to the equivalent WifiAccessPointOperationStatusCode. + * + * @param wifiAccessPointOperationStatusCode The AccessPointOperationStatus to convert. + * @return Microsoft::Net::Wifi::AccessPointOperationStatusCode + */ +Microsoft::Net::Wifi::AccessPointOperationStatusCode +FromDot11AccessPointOperationStatusCode(Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatusCode wifiAccessPointOperationStatusCode) noexcept; + /** * @brief Convert the specified Dot11PhyType to the equivalent IEEE 802.11 protocol. * diff --git a/src/linux/wifi/core/AccessPointControllerLinux.cxx b/src/linux/wifi/core/AccessPointControllerLinux.cxx index d03c95fa..62de7a80 100644 --- a/src/linux/wifi/core/AccessPointControllerLinux.cxx +++ b/src/linux/wifi/core/AccessPointControllerLinux.cxx @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -193,7 +194,7 @@ bool AccessPointControllerLinux::SetProtocol(Microsoft::Net::Wifi::Ieee80211Protocol ieeeProtocol) { bool isOk = false; - Wpa::HostapdHwMode hwMode = detail::IeeeProtocolToHostapdHwMode(ieeeProtocol); + const Wpa::HostapdHwMode hwMode = detail::IeeeProtocolToHostapdHwMode(ieeeProtocol); try { // Set the hostapd hw_mode property. @@ -249,7 +250,7 @@ AccessPointControllerLinux::SetFrequencyBands(std::vector AccessPointControllerLinuxFactory::Create(std::string_view interfaceName) { diff --git a/src/linux/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx b/src/linux/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx index 65930d68..3ea6ea44 100644 --- a/src/linux/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx +++ b/src/linux/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx @@ -2,12 +2,16 @@ #ifndef ACCESS_POINT_CONTROLLER_LINUX_HXX #define ACCESS_POINT_CONTROLLER_LINUX_HXX +#include #include #include #include #include +#include +#include #include +#include namespace Microsoft::Net::Wifi { @@ -29,12 +33,14 @@ struct AccessPointControllerLinux : explicit AccessPointControllerLinux(std::string_view interfaceName); /** - * Prevent copying and moving of this object. + * Prevent copying and moving of this object. */ AccessPointControllerLinux(const AccessPointControllerLinux&) = delete; - AccessPointControllerLinux& operator=(const AccessPointControllerLinux&) = delete; + AccessPointControllerLinux& + operator=(const AccessPointControllerLinux&) = delete; AccessPointControllerLinux(AccessPointControllerLinux&&) = delete; - AccessPointControllerLinux& operator=(AccessPointControllerLinux&&) = delete; + AccessPointControllerLinux& + operator=(AccessPointControllerLinux&&) = delete; /** * @brief Get whether the access point is enabled. @@ -73,6 +79,15 @@ struct AccessPointControllerLinux : bool SetFrequencyBands(std::vector frequencyBands) override; + /** + * @brief Set the SSID of the access point. + * + * @param ssid The SSID to be set. + * @return AccessPointOperationStatus + */ + AccessPointOperationStatus + SetSssid(std::string_view ssid) override; + private: Wpa::Hostapd m_hostapd; }; @@ -88,12 +103,14 @@ struct AccessPointControllerLinuxFactory : ~AccessPointControllerLinuxFactory() override = default; /** - * Prevent copying and moving of this object. + * Prevent copying and moving of this object. */ AccessPointControllerLinuxFactory(const AccessPointControllerLinuxFactory&) = delete; - AccessPointControllerLinuxFactory& operator=(const AccessPointControllerLinuxFactory&) = delete; + AccessPointControllerLinuxFactory& + operator=(const AccessPointControllerLinuxFactory&) = delete; AccessPointControllerLinuxFactory(AccessPointControllerLinuxFactory&&) = delete; - AccessPointControllerLinuxFactory& operator=(AccessPointControllerLinuxFactory&&) = delete; + AccessPointControllerLinuxFactory& + operator=(AccessPointControllerLinuxFactory&&) = delete; /** * @brief Create a new IAccessPointController object. diff --git a/src/linux/wpa-controller/Hostapd.cxx b/src/linux/wpa-controller/Hostapd.cxx index 676492f5..599d7922 100644 --- a/src/linux/wpa-controller/Hostapd.cxx +++ b/src/linux/wpa-controller/Hostapd.cxx @@ -1,4 +1,5 @@ +#include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include using namespace Wpa; @@ -52,6 +54,27 @@ Hostapd::GetStatus() return response->Status; } +bool +Hostapd::SetSsid(std::string_view ssid, bool reload) +{ + const bool ssidWasSet = SetProperty(ProtocolHostapd::PropertyNameSsid, ssid); + if (!ssidWasSet) { + throw HostapdException("Failed to set hostapd 'ssid' property"); + } + + if (!reload) { + LOGW << "Skipping hostapd reload after setting 'ssid' (requested)"; + return true; + } + + const bool configurationReloadSucceeded = Reload(); + if (!configurationReloadSucceeded) { + throw HostapdException("Failed to reload hostapd configuration"); + } + + return true; +} + bool Hostapd::GetProperty(std::string_view propertyName, std::string& propertyValue) { @@ -61,7 +84,7 @@ Hostapd::GetProperty(std::string_view propertyName, std::string& propertyValue) throw HostapdException("Failed to send hostapd 'get' command"); } - // Check Failed() and not IsOk() since the response will indicate failure + // Check Failed() instead of IsOk() since the response will indicate failure // with "FAIL", otherwise, the payload is the property value (not "OK"). if (response->Failed()) { return false; @@ -74,6 +97,8 @@ Hostapd::GetProperty(std::string_view propertyName, std::string& propertyValue) bool Hostapd::SetProperty(std::string_view propertyName, std::string_view propertyValue) { + LOGD << std::format("Attempting to set hostapd property '{}'({}) to '{}'({})", propertyName, std::size(propertyName), propertyValue, std::size(propertyValue)); + const WpaCommandSet setCommand(propertyName, propertyValue); const auto response = m_controller.SendCommand(setCommand); if (!response) { diff --git a/src/linux/wpa-controller/WpaCommandStatus.cxx b/src/linux/wpa-controller/WpaCommandStatus.cxx index f451f414..b5dfb1be 100644 --- a/src/linux/wpa-controller/WpaCommandStatus.cxx +++ b/src/linux/wpa-controller/WpaCommandStatus.cxx @@ -1,13 +1,16 @@ #include #include +#include #include +#include #include #include #include #include #include +#include #include "WpaParsingUtilities.hxx" @@ -26,6 +29,10 @@ WpaStatusResponseParser::WpaStatusResponseParser(const WpaCommand* command, std: { ProtocolHostapd::ResponseStatusPropertyKeyIeee80211N, WpaValuePresence::Required }, { ProtocolHostapd::ResponseStatusPropertyKeyIeee80211AC, WpaValuePresence::Required }, { ProtocolHostapd::ResponseStatusPropertyKeyIeee80211AX, WpaValuePresence::Required }, + { ProtocolHostapd::PropertyNameBss, WpaValuePresence::Required }, + { ProtocolHostapd::PropertyNameBssSsid, WpaValuePresence::Required }, + { ProtocolHostapd::PropertyNameBssBssid, WpaValuePresence::Required }, + { ProtocolHostapd::PropertyNameBssNumStations, WpaValuePresence::Required }, }) // clang-format on { @@ -36,11 +43,18 @@ WpaStatusResponseParser::ParsePayload() { using namespace Wpa::Parsing; - const auto properties = GetProperties(); + const auto& properties = GetProperties(); const auto response = std::make_shared(); auto& status = response->Status; - for (const auto& [key, value] : properties) { + const auto enforceRequiredBssInfoSize = [&](const auto sizeRequired) { + if (std::size(status.Bss) < sizeRequired) { + status.Bss.resize(sizeRequired); + } + }; + + for (const auto& [key, mapped] : properties) { + const auto& [value, index] = mapped; if (key == ProtocolHostapd::ResponseStatusPropertyKeyState) { status.State = HostapdInterfaceStateFromString(value); } else if (key == ProtocolHostapd::ResponseStatusPropertyKeyIeee80211N) { @@ -55,19 +69,31 @@ WpaStatusResponseParser::ParsePayload() ParseInt(value, status.Ieee80211ax); } else if (key == ProtocolHostapd::ResponseStatusPropertyKeyDisableAX) { ParseInt(value, status.Disable11ax); + } else if (key == ProtocolHostapd::PropertyNameBss) { + enforceRequiredBssInfoSize(index.value() + 1); + status.Bss[index.value()].Interface = value; + } else if (key == ProtocolHostapd::PropertyNameBssBssid) { + enforceRequiredBssInfoSize(index.value() + 1); + status.Bss[index.value()].Bssid = value; + } else if (key == ProtocolHostapd::PropertyNameBssSsid) { + enforceRequiredBssInfoSize(index.value() + 1); + status.Bss[index.value()].Ssid = value; + } else if (key == ProtocolHostapd::PropertyNameBssNumStations) { + enforceRequiredBssInfoSize(index.value() + 1); + ParseInt(value, status.Bss[index.value()].NumStations); } } if (!(status.Ieee80211n == 0)) { - // TODO: parse attributes that are preset when this value is set. + // TODO: parse attributes that are present when this value is set. } if (!(status.Ieee80211ac == 0)) { - // TODO: parse attributes that are preset when this value is set. + // TODO: parse attributes that are present when this value is set. } if (!(status.Ieee80211ax == 0)) { - // TODO: parse attributes that are preset when this value is set. + // TODO: parse attributes that are present when this value is set. } return response; diff --git a/src/linux/wpa-controller/WpaController.cxx b/src/linux/wpa-controller/WpaController.cxx index 975fa706..f063ebbb 100644 --- a/src/linux/wpa-controller/WpaController.cxx +++ b/src/linux/wpa-controller/WpaController.cxx @@ -70,7 +70,7 @@ WpaController::GetCommandControlSocket() const auto controlSocketPath = m_controlSocketPath / m_interfaceName; struct wpa_ctrl* controlSocket = wpa_ctrl_open(controlSocketPath.c_str()); if (controlSocket == nullptr) { - LOGE << std::format("Failed to open control socket for {} interface at {}.\n", m_interfaceName, controlSocketPath.c_str()); + LOGE << std::format("Failed to open control socket for {} interface at {}.", m_interfaceName, controlSocketPath.c_str()); return nullptr; } @@ -85,29 +85,30 @@ WpaController::SendCommand(const WpaCommand& command) // Obtain a control socket connection to send the command over. struct wpa_ctrl* controlSocket = GetCommandControlSocket(); if (controlSocket == nullptr) { - LOGE << std::format("Failed to get control socket for {}.\n", m_interfaceName); + LOGE << std::format("Failed to get control socket for {}.", m_interfaceName); return nullptr; } // Send the command and receive the response. - std::array responseBuffer; + std::array responseBuffer{}; std::size_t responseSize = std::size(responseBuffer); auto commandPayload = command.GetPayload(); + LOGD << std::format("Sending wpa command to {} interface: '{}'", m_interfaceName, commandPayload); int ret = wpa_ctrl_request(controlSocket, std::data(commandPayload), std::size(commandPayload), std::data(responseBuffer), &responseSize, nullptr); switch (ret) { case 0: { - std::string_view responsePayload{ std::data(responseBuffer), responseSize }; + const std::string_view responsePayload{ std::data(responseBuffer), responseSize }; return command.ParseResponse(responsePayload); } case -1: - LOGE << std::format("Failed to send or receive command to {} interface.\n", m_interfaceName); + LOGE << std::format("Failed to send or receive command to/from {} interface.", m_interfaceName); return nullptr; case -2: - LOGE << std::format("Sending command to {} interface timed out.\n", m_interfaceName); + LOGE << std::format("Sending command to {} interface timed out.", m_interfaceName); return nullptr; default: - LOGE << std::format("Unknown error sending command to {} interface (ret={}).\n", m_interfaceName, ret); + LOGE << std::format("Unknown error sending command to {} interface (ret={}).", m_interfaceName, ret); return nullptr; } } diff --git a/src/linux/wpa-controller/WpaKeyValuePair.cxx b/src/linux/wpa-controller/WpaKeyValuePair.cxx index 3ab5fece..ab60b940 100644 --- a/src/linux/wpa-controller/WpaKeyValuePair.cxx +++ b/src/linux/wpa-controller/WpaKeyValuePair.cxx @@ -8,27 +8,3 @@ #include using namespace Wpa; - -std::optional -WpaKeyValuePair::TryParseValue(std::string_view input) -{ - // If not already parsed... - if (!Value.has_value()) { - // Find the starting position of the key. - const auto keyPosition = input.find(Key); - if (keyPosition != input.npos) { - // Assign the starting position of the value, advancing past the key. - input = input.substr(keyPosition + std::size(Key)); - - // Find the end of the line, marking the end of the value string. - const auto eolPosition = input.find('\n'); - if (eolPosition != input.npos) { - Value = input.substr(0, eolPosition); - } else { - LOGW << std::format("Failed to parse value for key: {} (missing eol)", Key); - } - } - } - - return Value; -} diff --git a/src/linux/wpa-controller/WpaResponseParser.cxx b/src/linux/wpa-controller/WpaResponseParser.cxx index 4f4fd46a..a8da19c8 100644 --- a/src/linux/wpa-controller/WpaResponseParser.cxx +++ b/src/linux/wpa-controller/WpaResponseParser.cxx @@ -1,10 +1,18 @@ +#include +#include #include #include +#include #include +#include +#include +#include #include #include +#include +#include #include #include #include @@ -19,7 +27,7 @@ WpaResponseParser::WpaResponseParser(const WpaCommand* command, std::string_view { } -const std::unordered_map& +const std::unordered_map>>& WpaResponseParser::GetProperties() const noexcept { return m_properties; @@ -53,24 +61,71 @@ WpaResponseParser::GetResponsePayload() const noexcept bool WpaResponseParser::TryParseProperties() { + // Convert a range to a string-view for pre-C++23 where std::string_view doesn't have a range constructor. + constexpr auto toStringView = [](auto&& sv) { + return std::string_view{ std::data(sv), std::size(sv) }; + }; + // Convert a key-value pair to its key. + constexpr auto toKey = [](auto&& keyValuePair) { + return keyValuePair.Key; + }; + // Determine if the property map has the specified key. + const auto hasKey = [&](auto&& key) { + return m_properties.contains(key); + }; + if (std::empty(m_propertiesToParse)) { return true; } - for (auto propertyToParseIterator = std::begin(m_propertiesToParse); propertyToParseIterator != std::end(m_propertiesToParse);) { - auto& propertyToParse = *propertyToParseIterator; - auto propertyValue = propertyToParse.TryParseValue(m_responsePayload); - if (propertyValue.has_value()) { - m_properties[propertyToParse.Key] = *propertyValue; - propertyToParseIterator = m_propertiesToParse.erase(propertyToParseIterator); + // Parse the payload into individual lines containing key value pairs. + auto lines = m_responsePayload | std::views::split(ProtocolWpa::KeyValueLineDelimeter) | std::views::transform(toStringView); + + // Parse each line into a key-value pair, and populate the property map with them. + for (const auto line : lines) { + auto keyValuePair = line | std::views::split(ProtocolWpa::KeyValueDelimiter) | std::views::transform(toStringView); + auto keyValuePairIterator = std::begin(keyValuePair); + + if (keyValuePairIterator == std::end(keyValuePair)) { + LOGV << std::format("Skipping empty key value pair line '{}'", line); + continue; + } + + // Ensure a delimited key and value were found. + const auto numElements = std::ranges::distance(keyValuePairIterator, std::end(keyValuePair)); + if (numElements < 2) { + LOGW << std::format("Skipping key value pair line '{}' with less than 2 elements ({})", line, numElements); continue; } - if (propertyToParse.IsRequired) { - LOGE << std::format("Failed to parse required property: {}\nPayload {}\n", propertyToParse.Key, m_responsePayload); - return false; + + // Parse out the key and value, ensuring the key is non-empty (empty values are allowed). + auto key = *std::next(keyValuePairIterator, 0); + const auto value = *std::next(keyValuePairIterator, 1); + if (std::empty(key)) { + continue; + } + + // Check and parse key index in the form of "[]". + std::string keyIndexString{}; + std::optional keyIndex{ std::nullopt }; + const auto indexStart = key.find(ProtocolWpa::KeyValueIndexDelimeterStart); + if (indexStart != std::string_view::npos) { + const auto indexEnd = key.find(ProtocolWpa::KeyValueIndexDelimeterEnd); + if (indexEnd != std::string_view::npos) { + keyIndexString = std::string(key.substr(indexStart + 1, indexEnd - indexStart - 1)); + keyIndex = std::stoul(keyIndexString); + key = key.substr(0, indexStart); + } + } else { + keyIndexString = '*'; } - ++propertyToParseIterator; + + m_properties[key] = std::make_pair(value, keyIndex); + LOGV << std::format("Parsed [{:03}/{}] {}={}", std::size(m_properties), keyIndexString, key, value); } + // Remove parsed properties from the list of properties to parse. + m_propertiesToParse.erase(std::begin(std::ranges::remove_if(m_propertiesToParse, hasKey, toKey)), std::end(m_propertiesToParse)); + return true; } diff --git a/src/linux/wpa-controller/include/Wpa/Hostapd.hxx b/src/linux/wpa-controller/include/Wpa/Hostapd.hxx index 239eca4a..ebe3b2b0 100644 --- a/src/linux/wpa-controller/include/Wpa/Hostapd.hxx +++ b/src/linux/wpa-controller/include/Wpa/Hostapd.hxx @@ -73,6 +73,16 @@ struct Hostapd : HostapdStatus GetStatus() override; + /** + * @brief Set the ssid of the access point. + * + * @param ssid The ssid to set. + * @return true + * @return false + */ + bool + SetSsid(std::string_view ssid, bool reload = true); + /** * @brief Get a property value for the interface. * diff --git a/src/linux/wpa-controller/include/Wpa/ProtocolHostapd.hxx b/src/linux/wpa-controller/include/Wpa/ProtocolHostapd.hxx index 6b50b9b9..6d6d426b 100644 --- a/src/linux/wpa-controller/include/Wpa/ProtocolHostapd.hxx +++ b/src/linux/wpa-controller/include/Wpa/ProtocolHostapd.hxx @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -49,6 +50,7 @@ struct BssInfo int NumStations{ 0 }; std::string Bssid; std::string Interface; + std::string Ssid; // Present with: // - CONFIG_IEEE80211BE compilation option @@ -156,8 +158,8 @@ struct HostapdStatus // // - The current operational mode supports the currently active frequency. // std::optional MaxTxPower; - // // Always present (but may be empty). - // std::vector Bss; + // Always present (but may be empty). + std::vector Bss; // // Present with: // // - Non-zero channel utilization. @@ -201,40 +203,32 @@ struct ProtocolHostapd : static constexpr auto PropertyHwModeValueAD = "ad"; static constexpr auto PropertyHwModeValueAny = "any"; -// Macros below used to avoid repeating the same string literals in multiple places. -// NOLINTBEGIN(cppcoreguidelines-macro-usage) -#define HOSTAPD_PROPERTY_KEY_VALUE_DELIMETER "=" -#define HOSTAPD_PROPERTY_NAME_IEEE80211N "ieee80211n" -#define HOSTAPD_PROPERTY_NAME_DISABLE11N "disable_11n" -#define HOSTAPD_PROPERTY_NAME_IEEE80211AC "ieee80211ac" -#define HOSTAPD_PROPERTY_NAME_DISABLE11AC "disable_11ac" -#define HOSTAPD_PROPERTY_NAME_IEEE80211AX "ieee80211ax" -#define HOSTAPD_PROPERTY_NAME_DISABLE11AX "disable_11ax" -#define HOSTAPD_PROPERTY_NAME_STATE "state" - -// Helper macro to create a hostapd configuration file key with value delimiter. -#define HOSTAPD_DELIMITED_KEY(name) name HOSTAPD_PROPERTY_KEY_VALUE_DELIMETER - // NOLINTEND(cppcoreguidelines-macro-usage) - - static constexpr auto ProperyKeyValueNameDelimeter = HOSTAPD_PROPERTY_KEY_VALUE_DELIMETER; - static constexpr auto PropertyNameIeee80211N = HOSTAPD_PROPERTY_NAME_IEEE80211N; - static constexpr auto PropertyNameDisable11N = HOSTAPD_PROPERTY_NAME_DISABLE11N; - static constexpr auto PropertyNameIeee80211AC = HOSTAPD_PROPERTY_NAME_IEEE80211AC; - static constexpr auto PropertyNameDisable11AC = HOSTAPD_PROPERTY_NAME_DISABLE11AC; - static constexpr auto PropertyNameIeee80211AX = HOSTAPD_PROPERTY_NAME_IEEE80211AX; - static constexpr auto PropertyNameDisable11AX = HOSTAPD_PROPERTY_NAME_DISABLE11AX; + static constexpr auto PropertyNameSsid = "ssid"; + + static constexpr auto PropertyNameIeee80211N = "ieee80211n"; + static constexpr auto PropertyNameDisable11N = "disable_11n"; + static constexpr auto PropertyNameIeee80211AC = "ieee80211ac"; + static constexpr auto PropertyNameDisable11AC = "disable_11ac"; + static constexpr auto PropertyNameIeee80211AX = "ieee80211ax"; + static constexpr auto PropertyNameDisable11AX = "disable_11ax"; static constexpr auto PropertyNameWmmEnabled = "wmm_enabled"; + static constexpr auto PropertyNameState = "state"; + + // Indexed property names for BSS entries in the "STATUS" response. + static constexpr auto PropertyNameBss = "bss"; + static constexpr auto PropertyNameBssBssid = "bssid"; + static constexpr auto PropertyNameBssSsid = "ssid"; + static constexpr auto PropertyNameBssNumStations = "num_sta"; // Response properties for the "STATUS" command. // Note: all properties must be terminated with the key-value delimeter (=). - static constexpr auto PropertyNameState = HOSTAPD_PROPERTY_NAME_STATE; - static constexpr auto ResponseStatusPropertyKeyState = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_STATE); - static constexpr auto ResponseStatusPropertyKeyIeee80211N = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_IEEE80211N); - static constexpr auto ResponseStatusPropertyKeyDisable11N = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_DISABLE11N); - static constexpr auto ResponseStatusPropertyKeyIeee80211AC = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_IEEE80211AC); - static constexpr auto ResponseStatusPropertyKeyDisableAC = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_DISABLE11AC); - static constexpr auto ResponseStatusPropertyKeyIeee80211AX = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_IEEE80211AX); - static constexpr auto ResponseStatusPropertyKeyDisableAX = HOSTAPD_DELIMITED_KEY(HOSTAPD_PROPERTY_NAME_DISABLE11AX); + static constexpr auto ResponseStatusPropertyKeyState = PropertyNameState; + static constexpr auto ResponseStatusPropertyKeyIeee80211N = PropertyNameIeee80211N; + static constexpr auto ResponseStatusPropertyKeyDisable11N = PropertyNameDisable11N; + static constexpr auto ResponseStatusPropertyKeyIeee80211AC = PropertyNameIeee80211AC; + static constexpr auto ResponseStatusPropertyKeyDisableAC = PropertyNameDisable11AC; + static constexpr auto ResponseStatusPropertyKeyIeee80211AX = PropertyNameIeee80211AX; + static constexpr auto ResponseStatusPropertyKeyDisableAX = PropertyNameDisable11AX; }; /** diff --git a/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx b/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx index 18e0b990..2f4ef926 100644 --- a/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx +++ b/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx @@ -13,6 +13,9 @@ struct ProtocolWpa { // The delimeter used to separate key-value pairs in the WPA control protocol. static constexpr auto KeyValueDelimiter = '='; + static constexpr auto KeyValueLineDelimeter = '\n'; + static constexpr auto KeyValueIndexDelimeterStart = '['; + static constexpr auto KeyValueIndexDelimeterEnd = ']'; // Command payloads. static constexpr auto CommandPayloadPing = "PING"; diff --git a/src/linux/wpa-controller/include/Wpa/WpaKeyValuePair.hxx b/src/linux/wpa-controller/include/Wpa/WpaKeyValuePair.hxx index a2c23e71..247d22a6 100644 --- a/src/linux/wpa-controller/include/Wpa/WpaKeyValuePair.hxx +++ b/src/linux/wpa-controller/include/Wpa/WpaKeyValuePair.hxx @@ -2,7 +2,6 @@ #ifndef WPA_KEY_VALUE_PAIR_HXX #define WPA_KEY_VALUE_PAIR_HXX -#include #include #include @@ -38,37 +37,19 @@ struct WpaKeyValuePair * * @param key The key of the property. * @param presence Whether the property is required or optional. + * @param isIndexed Whether the property is indexed (i.e. has a numeric suffix in its key name). */ - constexpr WpaKeyValuePair(std::string_view key, WpaValuePresence presence) : + constexpr WpaKeyValuePair(std::string_view key, WpaValuePresence presence, bool isIndexed = false) : Key(key), - IsRequired(notstd::to_underlying(presence)) + IsRequired(notstd::to_underlying(presence)), + IsIndexed(isIndexed) { - // Ensure the specified key ends with the delimiter. If not, this is a - // programming error so throw a runtime exception. - if (!Key.ends_with(KeyDelimiter)) { - throw std::runtime_error(std::format("Key must end with {} delimiter.", KeyDelimiter)); - } } - /** - * Parses the input string and attempts to resolve the property value, - * assigning it to the Value member if found. Note that this function does - * not parse the value itself, it only resolves the value location in the - * input string, making it available for later parsing by derived classes - * that know its type/structure. - * - * The input string is expected to contain a property of the form: - * key=value, as is encoded by the WPA control protocol. * @brief - * - * @param input - * @return std::optional - */ - std::optional - TryParseValue(std::string_view input); - std::string_view Key; std::optional Value; bool IsRequired{ true }; + bool IsIndexed{ false }; }; } // namespace Wpa diff --git a/src/linux/wpa-controller/include/Wpa/WpaResponseParser.hxx b/src/linux/wpa-controller/include/Wpa/WpaResponseParser.hxx index fdbd3a92..d61324bb 100644 --- a/src/linux/wpa-controller/include/Wpa/WpaResponseParser.hxx +++ b/src/linux/wpa-controller/include/Wpa/WpaResponseParser.hxx @@ -2,10 +2,13 @@ #ifndef WPA_RESPONSE_PARSER_HXX #define WPA_RESPONSE_PARSER_HXX +#include #include #include +#include #include #include +#include #include #include @@ -26,12 +29,14 @@ struct WpaResponseParser virtual ~WpaResponseParser() = default; /** - * Prevent copy and move operations. + * Prevent copy and move operations. */ WpaResponseParser(const WpaResponseParser&) = delete; WpaResponseParser(WpaResponseParser&&) = delete; - WpaResponseParser& operator=(const WpaResponseParser&) = delete; - WpaResponseParser& operator=(WpaResponseParser&&) = delete; + WpaResponseParser& + operator=(const WpaResponseParser&) = delete; + WpaResponseParser& + operator=(WpaResponseParser&&) = delete; /** * @brief Construct a new WpaResponseParser object. @@ -53,16 +58,16 @@ struct WpaResponseParser /** * @brief Get the command associated with the response payload. - * - * @return const WpaCommand* + * + * @return const WpaCommand* */ const WpaCommand* GetCommand() const noexcept; /** * @brief Get the response payload. - * - * @return std::string_view + * + * @return std::string_view */ std::string_view GetResponsePayload() const noexcept; @@ -81,9 +86,9 @@ protected: /** * @brief Obtain the map of parsed properties. * - * @return const std::unordered_map& + * @return const std::unordered_map>>& */ - const std::unordered_map& + const std::unordered_map>>& GetProperties() const noexcept; private: @@ -103,7 +108,7 @@ private: const WpaCommand* m_command; std::string_view m_responsePayload; std::vector m_propertiesToParse; - std::unordered_map m_properties{}; + std::unordered_map>> m_properties{}; }; /** @@ -116,12 +121,14 @@ struct WpaResponseParserFactory virtual ~WpaResponseParserFactory() = default; /** - * Prevent copy and move operations. + * Prevent copy and move operations. */ WpaResponseParserFactory(const WpaResponseParserFactory&) = delete; WpaResponseParserFactory(WpaResponseParserFactory&&) = delete; - WpaResponseParserFactory& operator=(const WpaResponseParserFactory&) = delete; - WpaResponseParserFactory& operator=(WpaResponseParserFactory&&) = delete; + WpaResponseParserFactory& + operator=(const WpaResponseParserFactory&) = delete; + WpaResponseParserFactory& + operator=(WpaResponseParserFactory&&) = delete; /** * @brief Create a response parser object. diff --git a/tests/unit/linux/wpa-controller/TestHostapd.cxx b/tests/unit/linux/wpa-controller/TestHostapd.cxx index a4745821..5f501e8c 100644 --- a/tests/unit/linux/wpa-controller/TestHostapd.cxx +++ b/tests/unit/linux/wpa-controller/TestHostapd.cxx @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -301,3 +302,58 @@ TEST_CASE("Send command: Terminate() ping failure (root)", "[wpa][hostapd][clien REQUIRE_THROWS_AS(hostapd.Ping(), HostapdException); } + +TEST_CASE("Send SetSsid() command (root)", "[wpa][hostapd][client][remote]") +{ + using namespace Wpa; + + constexpr auto SsidValid{ "whatever" }; + constexpr auto SsidInvalidTooLong{ "This SSID is way too long to be valid because it has more than 32 characters" }; + + Hostapd hostapd(WpaDaemonManager::InterfaceNameDefault); + + SECTION("SetSsid() doesn't throw") + { + REQUIRE_NOTHROW(hostapd.SetSsid(SsidValid)); + } + + SECTION("SetSsid() returns true for valid SSID") + { + REQUIRE(hostapd.SetSsid(SsidValid)); + } + + SECTION("SetSsid() throws for empty SSID") + { + REQUIRE_THROWS_AS(hostapd.SetSsid(""), HostapdException); + } + + SECTION("SetSsid() throws for SSID with length > 32") + { + REQUIRE_THROWS_AS(hostapd.SetSsid(SsidInvalidTooLong), HostapdException); + } + + SECTION("SetSsid() changes the SSID for valid input") + { + constexpr auto SsidToSet{ SsidValid }; + REQUIRE(hostapd.SetSsid(SsidToSet)); + const auto status = hostapd.GetStatus(); + REQUIRE(!std::empty(status.Bss)); + REQUIRE(status.Bss[0].Ssid == SsidToSet); + } + + // Note: The below tests use starts_with() to check the SSID instead of == as hostapd has a bug where attempting to + // provide an invalid SSID results in the reported SSID having some junk characters following the real SSID. + + SECTION("SetSsid() does not change the SSID for invalid inputs") + { + const auto statusInitial = hostapd.GetStatus(); + REQUIRE(!std::empty(statusInitial.Bss)); + + const auto* const SsidToSet{ SsidInvalidTooLong }; + REQUIRE_THROWS_AS(hostapd.SetSsid(SsidToSet), HostapdException); + + const auto statusAfterFail = hostapd.GetStatus(); + REQUIRE(!std::empty(statusAfterFail.Bss)); + REQUIRE(statusAfterFail.Bss[0].Ssid.starts_with(statusInitial.Bss[0].Ssid)); + } +} diff --git a/tests/unit/wifi/helpers/AccessPointControllerTest.cxx b/tests/unit/wifi/helpers/AccessPointControllerTest.cxx index 2582ecd0..b1594b10 100644 --- a/tests/unit/wifi/helpers/AccessPointControllerTest.cxx +++ b/tests/unit/wifi/helpers/AccessPointControllerTest.cxx @@ -1,5 +1,4 @@ - #include #include #include @@ -10,6 +9,7 @@ #include #include +#include #include #include #include @@ -83,6 +83,17 @@ AccessPointControllerTest::SetFrequencyBands(std::vector return true; } +AccessPointOperationStatus +AccessPointControllerTest::SetSssid(std::string_view ssid) +{ + if (AccessPoint == nullptr) { + throw std::runtime_error("AccessPointControllerTest::SetSssid called with null AccessPoint"); + } + + AccessPoint->Ssid = ssid; + return AccessPointOperationStatus::MakeSucceeded(); +} + AccessPointControllerFactoryTest::AccessPointControllerFactoryTest(AccessPointTest *accessPoint) : AccessPoint(accessPoint) {} diff --git a/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx b/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx index 8ef57133..b47fa305 100644 --- a/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx +++ b/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx @@ -6,7 +6,10 @@ #include #include +#include #include +#include +#include namespace Microsoft::Net::Wifi::Test { @@ -18,25 +21,37 @@ struct AccessPointTest; * This implementation takes an AccessPointTest object which it uses to implement the IAccessPointController interface. * The owner of this class must ensure that the passes AccessPointTest* remains valid for the lifetime of this object. */ -struct AccessPointControllerTest final : - public Microsoft::Net::Wifi::IAccessPointController +struct AccessPointControllerTest final : public Microsoft::Net::Wifi::IAccessPointController { AccessPointTest *AccessPoint{ nullptr }; + AccessPointControllerTest() = default; + ~AccessPointControllerTest() override = default; + /** * @brief Construct a new AccessPointControllerTest object that uses the specified AccessPointTest to implement the * IAccessPointController interface. * * @param accessPoint The access point to use. */ - AccessPointControllerTest(AccessPointTest *accessPoint); + explicit AccessPointControllerTest(AccessPointTest *accessPoint); + + /** + * Prevent copying and moving of AccessPointControllerTest objects. + */ + AccessPointControllerTest(const AccessPointControllerTest &) = delete; + AccessPointControllerTest & + operator=(const AccessPointControllerTest &) = delete; + AccessPointControllerTest(AccessPointControllerTest &&) = delete; + AccessPointControllerTest & + operator=(AccessPointControllerTest &&) = delete; /** * @brief Get the interface name associated with this controller. * * @return std::string_view */ - virtual std::string_view + std::string_view GetInterfaceName() const override; /** @@ -45,7 +60,7 @@ struct AccessPointControllerTest final : * @return true * @return false */ - virtual bool + bool GetIsEnabled() override; /** @@ -53,7 +68,7 @@ struct AccessPointControllerTest final : * * @return Ieee80211AccessPointCapabilities */ - virtual Ieee80211AccessPointCapabilities + Ieee80211AccessPointCapabilities GetCapabilities() override; /** @@ -63,7 +78,7 @@ struct AccessPointControllerTest final : * @return true * @return false */ - virtual bool + bool SetProtocol(Microsoft::Net::Wifi::Ieee80211Protocol ieeeProtocol) override; /** @@ -73,18 +88,28 @@ struct AccessPointControllerTest final : * @return true * @return false */ - virtual bool + bool SetFrequencyBands(std::vector frequencyBands) override; + + /** + * @brief Set the SSID of the access point. + * + * @param ssid The SSID to be set. + * @return AccessPointOperationStatus + */ + AccessPointOperationStatus + SetSssid(std::string_view ssid) override; }; /** * @brief IAccessPointControllerFactory implementation for testing purposes. */ -struct AccessPointControllerFactoryTest final : - public Microsoft::Net::Wifi::IAccessPointControllerFactory +struct AccessPointControllerFactoryTest final : public Microsoft::Net::Wifi::IAccessPointControllerFactory { AccessPointTest *AccessPoint{ nullptr }; + ~AccessPointControllerFactoryTest() override = default; + /** * @brief Construct a new AccessPointControllerFactoryTest object with no AccessPointTest parent/owner. This may * only be used for cases where the constructed object won't actually be used (eg. unit tests for other components @@ -95,11 +120,22 @@ struct AccessPointControllerFactoryTest final : AccessPointControllerFactoryTest() = default; /** - * @brief Construct a new AccessPointControllerFactoryTest object. The owner of this object must ensure that the passed AccessPointTest remains valid for the lifetime of this object. + * @brief Construct a new AccessPointControllerFactoryTest object. The owner of this object must ensure that the + * passed AccessPointTest remains valid for the lifetime of this object. * * @param accessPoint The access point to create controllers for. */ - AccessPointControllerFactoryTest(AccessPointTest *accessPoint); + explicit AccessPointControllerFactoryTest(AccessPointTest *accessPoint); + + /** + * Prevent copying and moving of AccessPointControllerFactoryTest objects. + */ + AccessPointControllerFactoryTest(const AccessPointControllerFactoryTest &) = delete; + AccessPointControllerFactoryTest & + operator=(const AccessPointControllerFactoryTest &) = delete; + AccessPointControllerFactoryTest(AccessPointControllerFactoryTest &&) = delete; + AccessPointControllerFactoryTest & + operator=(AccessPointControllerFactoryTest &&) = delete; /** * @brief Create a new instance that can control the access point. diff --git a/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx b/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx index 5e832b3a..ea9e4f06 100644 --- a/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx +++ b/tests/unit/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx @@ -28,6 +28,7 @@ struct AccessPointTest final : bool IsEnabled{ false }; Microsoft::Net::Wifi::Ieee80211Protocol Protocol; std::vector FrequencyBands; + std::string Ssid; /** * @brief Construct a new AccessPointTest object with the given interface name and default capabilities.