diff --git a/api/protos/WifiCore.proto b/api/protos/WifiCore.proto index 219c2d76..2c114481 100644 --- a/api/protos/WifiCore.proto +++ b/api/protos/WifiCore.proto @@ -208,11 +208,12 @@ message Dot11AccessPointConfiguration Dot11MacAddress Bssid = 2; Dot11PhyType PhyType = 3; Dot11AuthenticationData AuthenticationData = 4; - Dot11AuthenticationDot1x AuthenticationDot1x = 9; repeated Dot11AuthenticationAlgorithm AuthenticationAlgorithms = 5; repeated Dot11CipherSuiteConfiguration PairwiseCipherSuites = 6; repeated Dot11AkmSuite AkmSuites = 7; repeated Dot11FrequencyBand FrequencyBands = 8; + Dot11AuthenticationDot1x AuthenticationDot1x = 9; + bool mldAp = 10; } message Dot11AccessPointCapabilities diff --git a/src/common/net/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx b/src/common/net/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx index 26a08bc7..1a7a39e6 100644 --- a/src/common/net/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx +++ b/src/common/net/wifi/core/include/microsoft/net/wifi/IAccessPointController.hxx @@ -157,6 +157,15 @@ struct IAccessPointController */ virtual AccessPointOperationStatus SetRadiusConfiguration(Ieee8021xRadiusConfiguration radiusConfiguration) noexcept = 0; + + /** + * @brief Set whether the access point is a Multi-Link Device (MLD) AP. + * + * @param mldAp True if the access point is an MLD AP, false otherwise. + * @return AccessPointOperationStatus + */ + virtual AccessPointOperationStatus + SetMldAp(bool mldAp) noexcept = 0; }; /** diff --git a/src/common/service/NetRemoteService.cxx b/src/common/service/NetRemoteService.cxx index 6727eb7e..48f57430 100644 --- a/src/common/service/NetRemoteService.cxx +++ b/src/common/service/NetRemoteService.cxx @@ -613,6 +613,12 @@ NetRemoteService::WifiAccessPointEnableImpl(std::string_view accessPointId, cons return wifiOperationStatus; } } + + bool mldAp = dot11AccessPointConfiguration->mldap(); + wifiOperationStatus = WifiAccessPointSetMldApImpl(accessPointId, mldAp, accessPointController); + if (wifiOperationStatus.code() != WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded) { + return wifiOperationStatus; + } } // Obtain current operational state. @@ -1467,6 +1473,36 @@ NetRemoteService::WifiAccessPointSetAuthenticationDot1xImpl(std::string_view acc return wifiOperationStatus; } +WifiAccessPointOperationStatus +NetRemoteService::WifiAccessPointSetMldApImpl(std::string_view accessPointId, bool mldAp, std::shared_ptr accessPointController) +{ + WifiAccessPointOperationStatus wifiOperationStatus{}; + + AccessPointOperationStatus operationStatus{ accessPointId }; + + // Create an AP controller for the requested AP if one wasn't specified. + if (accessPointController == nullptr) { + operationStatus = TryGetAccessPointController(accessPointId, accessPointController); + if (!operationStatus.Succeeded() || accessPointController == nullptr) { + wifiOperationStatus.set_code(ToDot11AccessPointOperationStatusCode(operationStatus.Code)); + wifiOperationStatus.set_message(std::format("Failed to create access point controller for access point {} - {}", accessPointId, operationStatus.ToString())); + return wifiOperationStatus; + } + } + + // Attempt to set the MLD AP setting. + operationStatus = accessPointController->SetMldAp(mldAp); + if (!operationStatus.Succeeded()) { + wifiOperationStatus.set_code(ToDot11AccessPointOperationStatusCode(operationStatus.Code)); + wifiOperationStatus.set_message(std::format("Failed to set MLD AP setting for access point {} - {}", accessPointId, operationStatus.ToString())); + return wifiOperationStatus; + } + + wifiOperationStatus.set_code(WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded); + + return wifiOperationStatus; +} + using google::protobuf::Map; WifiAccessPointOperationStatus diff --git a/src/common/service/include/microsoft/net/remote/service/NetRemoteService.hxx b/src/common/service/include/microsoft/net/remote/service/NetRemoteService.hxx index 5f66c80c..a369a84c 100644 --- a/src/common/service/include/microsoft/net/remote/service/NetRemoteService.hxx +++ b/src/common/service/include/microsoft/net/remote/service/NetRemoteService.hxx @@ -370,6 +370,17 @@ protected: Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatus WifiAccessPointSetAuthenticationDot1xImpl(std::string_view accessPointId, const Microsoft::Net::Wifi::Dot11AuthenticationDot1x& dot11AuthenticationDot1x, std::shared_ptr accessPointController = nullptr); + /** + * @brief Set whether the access point is a Multi-Link Device Access Point (MLD AP). + * + * @param accessPointId The access point identifier. + * @param mldAp True to configure the access point as an MLD AP, false otherwise. + * @param accessPointController The access point controller for the specified access point (optional). + * @return Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatus + */ + Microsoft::Net::Remote::Wifi::WifiAccessPointOperationStatus + WifiAccessPointSetMldApImpl(std::string_view accessPointId, bool mldAp, std::shared_ptr accessPointController = nullptr); + /** * @brief Get the sttaic attributes of the specified access point. * diff --git a/src/linux/net/wifi/core/AccessPointControllerLinux.cxx b/src/linux/net/wifi/core/AccessPointControllerLinux.cxx index 79736cc9..c75839db 100644 --- a/src/linux/net/wifi/core/AccessPointControllerLinux.cxx +++ b/src/linux/net/wifi/core/AccessPointControllerLinux.cxx @@ -664,6 +664,29 @@ AccessPointControllerLinux::SetRadiusConfiguration(Ieee8021xRadiusConfiguration return status; } +AccessPointOperationStatus +AccessPointControllerLinux::SetMldAp(bool mldAp) noexcept +{ + AccessPointOperationStatus status{ GetInterfaceName() }; + const AccessPointOperationStatusLogOnExit logStatusOnExit(&status); + + AUDITD << std::format("Attempting to set MLD AP for AP {} to {}", status.AccessPointId, mldAp ? "enabled" : "disabled"); + + std::string_view propertyValueToSet = mldAp ? Wpa::ProtocolHostapd::PropertyEnabled : Wpa::ProtocolHostapd::PropertyDisabled; + + // Set the hostapd "mld_ap" property. + try { + m_hostapd.SetProperty(Wpa::ProtocolHostapd::PropertyNameMldAp, propertyValueToSet, EnforceConfigurationChange::Now); + } catch (const Wpa::HostapdException& ex) { + status.Code = AccessPointOperationStatusCode::InternalError; + status.Details = std::format("failed to set hostapd property '{}' to '{}' - {}", Wpa::ProtocolHostapd::PropertyNameMldAp, propertyValueToSet, ex.what()); + return status; + } + + status.Code = AccessPointOperationStatusCode::Succeeded; + return status; +} + std::unique_ptr AccessPointControllerLinuxFactory::Create(std::string_view interfaceName) { diff --git a/src/linux/net/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx b/src/linux/net/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx index f8d30c2d..24af0530 100644 --- a/src/linux/net/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx +++ b/src/linux/net/wifi/core/include/microsoft/net/wifi/AccessPointControllerLinux.hxx @@ -156,6 +156,15 @@ struct AccessPointControllerLinux : AccessPointOperationStatus SetRadiusConfiguration(Microsoft::Net::Ieee8021xRadiusConfiguration radiusConfiguration) noexcept override; + /** + * @brief Set whether the access point is a Multi-Link Device (MLD) AP. + * + * @param mldAp True if the access point is an MLD AP, false otherwise. + * @return AccessPointOperationStatus + */ + AccessPointOperationStatus + SetMldAp(bool mldAp) noexcept override; + private: Wpa::Hostapd m_hostapd; }; diff --git a/src/linux/net/wifi/wpa-controller/include/Wpa/ProtocolHostapd.hxx b/src/linux/net/wifi/wpa-controller/include/Wpa/ProtocolHostapd.hxx index 468f9c51..59047ee0 100644 --- a/src/linux/net/wifi/wpa-controller/include/Wpa/ProtocolHostapd.hxx +++ b/src/linux/net/wifi/wpa-controller/include/Wpa/ProtocolHostapd.hxx @@ -684,6 +684,9 @@ struct ProtocolHostapd : static constexpr auto PropertyValueSaePasswordClearAll = ""; static constexpr auto PropertyValueSaeKeyValueSeparator = "|"; + // Wi-Fi 7 (IEEE 802.11be) properties. + static constexpr auto PropertyNameMldAp = "mld_ap"; + // Property names for "GET_CONFIG" command. static constexpr auto PropertyNameKeyManagement = "key_mgmt"; static constexpr auto PropertyNameGroupCipher = "group_cipher"; diff --git a/tests/unit/TestNetRemoteServiceClient.cxx b/tests/unit/TestNetRemoteServiceClient.cxx index 1dd8968e..4d7b29bd 100644 --- a/tests/unit/TestNetRemoteServiceClient.cxx +++ b/tests/unit/TestNetRemoteServiceClient.cxx @@ -314,6 +314,48 @@ TEST_CASE("WifiAccessPointEnable API", "[basic][rpc][client][remote]") REQUIRE(result.has_status()); REQUIRE(result.status().code() == WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded); } + + SECTION("Enable MLD AP") + { + // Enable the MLD AP flag via the configuration and verify it sticks. + Dot11AccessPointConfiguration apConfiguration{}; + apConfiguration.set_mldap(true); + + WifiAccessPointEnableRequest request{}; + request.set_accesspointid(InterfaceName1); + *request.mutable_configuration() = std::move(apConfiguration); + + WifiAccessPointEnableResult result{}; + grpc::ClientContext clientContext{}; + + auto status = client->WifiAccessPointEnable(&clientContext, request, &result); + REQUIRE(status.ok()); + REQUIRE(result.accesspointid() == request.accesspointid()); + REQUIRE(result.has_status()); + REQUIRE(result.status().code() == WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded); + REQUIRE(apTest1->MldAp == true); + } + + SECTION("Disable MLD AP") + { + // Disable the MLD AP flag via the configuration and verify it sticks. + Dot11AccessPointConfiguration apConfiguration{}; + apConfiguration.set_mldap(false); + + WifiAccessPointEnableRequest request{}; + request.set_accesspointid(InterfaceName2); + *request.mutable_configuration() = std::move(apConfiguration); + + WifiAccessPointEnableResult result{}; + grpc::ClientContext clientContext{}; + + auto status = client->WifiAccessPointEnable(&clientContext, request, &result); + REQUIRE(status.ok()); + REQUIRE(result.accesspointid() == request.accesspointid()); + REQUIRE(result.has_status()); + REQUIRE(result.status().code() == WifiAccessPointOperationStatusCode::WifiAccessPointOperationStatusCodeSucceeded); + REQUIRE(apTest2->MldAp == false); + } } TEST_CASE("WifiAccessPointDisable API", "[basic][rpc][client][remote]") diff --git a/tests/unit/net/wifi/helpers/AccessPointControllerTest.cxx b/tests/unit/net/wifi/helpers/AccessPointControllerTest.cxx index eb236e29..47de7274 100644 --- a/tests/unit/net/wifi/helpers/AccessPointControllerTest.cxx +++ b/tests/unit/net/wifi/helpers/AccessPointControllerTest.cxx @@ -203,6 +203,18 @@ AccessPointControllerTest::SetRadiusConfiguration(Ieee8021xRadiusConfiguration r return AccessPointOperationStatus::MakeSucceeded(AccessPoint->InterfaceName); } +AccessPointOperationStatus +AccessPointControllerTest::SetMldAp(bool mldAp) noexcept +{ + assert(AccessPoint != nullptr); + if (AccessPoint == nullptr) { + return AccessPointOperationStatus::InvalidAccessPoint("null AccessPoint"); + } + + AccessPoint->MldAp = mldAp; + return AccessPointOperationStatus::MakeSucceeded(AccessPoint->InterfaceName); +} + AccessPointControllerFactoryTest::AccessPointControllerFactoryTest(AccessPointTest *accessPoint) : AccessPoint(accessPoint) {} diff --git a/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx b/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx index 7d19adf0..3f62867e 100644 --- a/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx +++ b/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointControllerTest.hxx @@ -166,6 +166,15 @@ struct AccessPointControllerTest final : */ AccessPointOperationStatus SetRadiusConfiguration(Ieee8021xRadiusConfiguration radiusConfiguration) noexcept override; + + /** + * @brief Set whether the access point is a Multi-Link Device (MLD) AP. + * + * @param mldAp True if the access point is an MLD AP, false otherwise. + * @return AccessPointOperationStatus + */ + AccessPointOperationStatus + SetMldAp(bool mldAp) noexcept override; }; /** diff --git a/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx b/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx index afe7c741..6a5ff5bc 100644 --- a/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx +++ b/tests/unit/net/wifi/helpers/include/microsoft/net/wifi/test/AccessPointTest.hxx @@ -41,6 +41,7 @@ struct AccessPointTest final : std::unordered_map> CipherSuites; AccessPointOperationalState OperationalState{ AccessPointOperationalState::Disabled }; AccessPointAttributes Attributes{}; + bool MldAp{ false }; /** * @brief Construct a new AccessPointTest object with the given interface name and capabilities.