Skip to content

Commit

Permalink
Merge pull request #213 from microsoft/hapdauth
Browse files Browse the repository at this point in the history
Implement configuration of hostapd wpa protocol and key management
  • Loading branch information
abeltrano authored Mar 12, 2024
2 parents 0fae759 + d2908f7 commit 87b0285
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 90 deletions.
1 change: 1 addition & 0 deletions src/linux/external/hostap/hostapd/.config
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ CONFIG_IPV6=y
# IEEE Std 802.11r-2008 (Fast BSS Transition)
# [Manually enabled]
CONFIG_IEEE80211R=y
CONFIG_IEEE80211R_AP=y

# Use the hostapd's IEEE 802.11 authentication (ACL), but without
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
Expand Down
35 changes: 4 additions & 31 deletions src/linux/wifi/core/AccessPointControllerLinux.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using namespace Microsoft::Net::Wifi;

using Microsoft::Net::Netlink::Nl80211::Nl80211Wiphy;
using Wpa::EnforceConfigurationChange;

AccessPointControllerLinux::AccessPointControllerLinux(std::string_view interfaceName) :
AccessPointController(interfaceName),
Expand Down Expand Up @@ -174,7 +175,7 @@ AccessPointControllerLinux::SetProtocol(Ieee80211Protocol ieeeProtocol) noexcept
try {
for (auto& propertyToSet : propertiesToSet) {
std::tie(propertyKeyToSet, propertyValueToSet) = std::move(propertyToSet);
hostapdOperationSucceeded = m_hostapd.SetProperty(propertyKeyToSet, propertyValueToSet);
hostapdOperationSucceeded = m_hostapd.SetProperty(propertyKeyToSet, propertyValueToSet, EnforceConfigurationChange::Defer);
if (!hostapdOperationSucceeded) {
break;
}
Expand Down Expand Up @@ -233,7 +234,7 @@ AccessPointControllerLinux::SetFrequencyBands(std::vector<Ieee80211FrequencyBand

// Set the hostapd "setband" property.
try {
hostapdOperationSucceeded = m_hostapd.SetProperty(propertyKeyToSet, propertyValueToSet);
hostapdOperationSucceeded = m_hostapd.SetProperty(propertyKeyToSet, propertyValueToSet, EnforceConfigurationChange::Now);
} catch (const Wpa::HostapdException& ex) {
hostapdOperationSucceeded = false;
errorDetails = ex.what();
Expand All @@ -245,20 +246,6 @@ AccessPointControllerLinux::SetFrequencyBands(std::vector<Ieee80211FrequencyBand
return status;
}

// Reload hostapd configuration to pick up the changes.
try {
hostapdOperationSucceeded = m_hostapd.Reload();
} catch (const Wpa::HostapdException& ex) {
hostapdOperationSucceeded = false;
errorDetails = ex.what();
}

if (!hostapdOperationSucceeded) {
status.Code = AccessPointOperationStatusCode::InternalError;
status.Details = std::format("failed to reload hostapd configuration - {}", errorDetails.value_or("unspecified error"));
return status;
}

status.Code = AccessPointOperationStatusCode::Succeeded;

return status;
Expand All @@ -282,7 +269,7 @@ AccessPointControllerLinux::SetSsid(std::string_view ssid) noexcept

// Attempt to set the SSID.
try {
hostapdOperationSucceeded = m_hostapd.SetProperty(Wpa::ProtocolHostapd::PropertyNameSsid, ssid);
hostapdOperationSucceeded = m_hostapd.SetProperty(Wpa::ProtocolHostapd::PropertyNameSsid, ssid, EnforceConfigurationChange::Now);
} catch (Wpa::HostapdException& ex) {
hostapdOperationSucceeded = false;
errorDetails = ex.what();
Expand All @@ -294,20 +281,6 @@ AccessPointControllerLinux::SetSsid(std::string_view ssid) noexcept
return status;
}

// Reload the configuration to pick up the changes.
try {
hostapdOperationSucceeded = m_hostapd.Reload();
} catch (Wpa::HostapdException& ex) {
hostapdOperationSucceeded = false;
errorDetails = ex.what();
}

if (!hostapdOperationSucceeded) {
status.Code = AccessPointOperationStatusCode::InternalError;
status.Details = "failed to reload access point configuration";
return status;
}

status.Code = AccessPointOperationStatusCode::Succeeded;
return status;
}
Expand Down
123 changes: 93 additions & 30 deletions src/linux/wpa-controller/Hostapd.cxx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@

#include <cstdint>
#include <format>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include <Wpa/Hostapd.hxx>
#include <Wpa/IHostapd.hxx>
Expand All @@ -12,6 +16,7 @@
#include <Wpa/WpaCommandStatus.hxx>
#include <Wpa/WpaCore.hxx>
#include <Wpa/WpaResponseStatus.hxx>
#include <magic_enum.hpp>
#include <plog/Log.h>

using namespace Wpa;
Expand Down Expand Up @@ -41,38 +46,30 @@ Hostapd::Ping()
return response->Payload().starts_with(ProtocolHostapd::ResponsePayloadPing);
}

HostapdStatus
Hostapd::GetStatus()
bool
Hostapd::Reload()
{
static constexpr WpaCommandStatus StatusCommand;
static constexpr WpaCommand ReloadCommand(ProtocolHostapd::CommandPayloadReload);

auto response = m_controller.SendCommand<WpaResponseStatus>(StatusCommand);
const auto response = m_controller.SendCommand(ReloadCommand);
if (!response) {
throw HostapdException("Failed to send hostapd 'status' command");
throw HostapdException("Failed to send hostapd 'reload' command");
}

return response->Status;
return response->IsOk();
}

bool
Hostapd::SetSsid(std::string_view ssid, bool reload)
HostapdStatus
Hostapd::GetStatus()
{
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;
}
static constexpr WpaCommandStatus StatusCommand;

const bool configurationReloadSucceeded = Reload();
if (!configurationReloadSucceeded) {
throw HostapdException("Failed to reload hostapd configuration");
auto response = m_controller.SendCommand<WpaResponseStatus>(StatusCommand);
if (!response) {
throw HostapdException("Failed to send hostapd 'status' command");
}

return true;
return response->Status;
}

bool
Expand All @@ -95,17 +92,32 @@ Hostapd::GetProperty(std::string_view propertyName, std::string& propertyValue)
}

bool
Hostapd::SetProperty(std::string_view propertyName, std::string_view propertyValue)
Hostapd::SetProperty(std::string_view propertyName, std::string_view propertyValue, EnforceConfigurationChange enforceConfigurationChange)
{
LOGD << std::format("Attempting to set hostapd property '{}'({}) to '{}'({})", propertyName, std::size(propertyName), propertyValue, std::size(propertyValue));
LOGD << std::format("Attempting to set hostapd property '{}' (size={}) to '{}' (size={})", propertyName, std::size(propertyName), propertyValue, std::size(propertyValue));

const WpaCommandSet setCommand(propertyName, propertyValue);
const auto response = m_controller.SendCommand(setCommand);
if (!response) {
throw HostapdException("Failed to send hostapd 'set' command");
}

return response->IsOk();
if (!response->IsOk()) {
LOGV << std::format("Invalid response received when setting hostapd property '{}' to '{}' (payload={})", propertyName, propertyValue, response->Payload());
throw HostapdException(std::format("Failed to set hostapd property '{}' to '{}'", propertyName, propertyValue));
}

if (enforceConfigurationChange == EnforceConfigurationChange::Defer) {
LOGD << std::format("Skipping enforcement of '{}' configuration change (requested)", propertyName);
return true;
}

const bool configurationReloadSucceeded = Reload();
if (!configurationReloadSucceeded) {
throw HostapdException(std::format("Failed to reload hostapd configuration after '{}' property change", propertyName));
}

return true;
}

bool
Expand Down Expand Up @@ -166,14 +178,65 @@ Hostapd::Terminate()
}

bool
Hostapd::Reload()
Hostapd::SetSsid(std::string_view ssid, EnforceConfigurationChange enforceConfigurationChange)
{
static constexpr WpaCommand ReloadCommand(ProtocolHostapd::CommandPayloadReload);
const bool ssidWasSet = SetProperty(ProtocolHostapd::PropertyNameSsid, ssid, enforceConfigurationChange);
return ssidWasSet;
}

const auto response = m_controller.SendCommand(ReloadCommand);
if (!response) {
throw HostapdException("Failed to send hostapd 'reload' command");
bool
Hostapd::SetWpaProtocols(std::vector<WpaProtocol> protocols, EnforceConfigurationChange enforceConfigurationChange)
{
if (std::empty(protocols)) {
throw HostapdException("No WPA protocols were provided");
}

return response->IsOk();
uint32_t protocolsToSet = 0;
for (const auto protocol : protocols) {
const auto protocolValue = std::to_underlying(protocol);
if ((protocolValue & ~WpaProtocolMask) != 0) {
throw HostapdException(std::format("Invalid WPA protocol value '{}'", protocolValue));
}
protocolsToSet |= protocolValue;
}

protocolsToSet &= WpaProtocolMask;

const auto protocolsValue = std::format("{}", protocolsToSet);
const bool protocolsWereSet = SetProperty(ProtocolHostapd::PropertyNameWpaProtocol, protocolsValue, enforceConfigurationChange);
if (!protocolsWereSet) {
throw HostapdException(std::format("Failed to set hostapd 'wpa' property to '{}'", protocolsValue));
}

return true;
}

bool
Hostapd::SetKeyManagement(std::vector<WpaKeyManagement> keyManagements, EnforceConfigurationChange enforceConfigurationChange)
{
if (std::empty(keyManagements)) {
throw HostapdException("No WPA key management values were provided");
}

// Convert the key management values to a space-delimited string of individual key management values expected by hostapd.
std::string keyManagementPropertyValue;
{
std::ostringstream keyManagementPropertyValueBuilder{};
for (const auto keyManagement : keyManagements) {
const auto keyManagementValue = WpaKeyManagementPropertyValue(keyManagement);
if (keyManagementValue == WpaKeyManagementInvalidValue) {
throw HostapdException(std::format("Invalid WPA key management value '{}'", magic_enum::enum_name(keyManagement)));
}
keyManagementPropertyValueBuilder << keyManagementValue << ' ';
}

keyManagementPropertyValue = keyManagementPropertyValueBuilder.str();
}

const auto keyManagementWasSet = SetProperty(ProtocolHostapd::PropertyNameWpaKeyManagement, keyManagementPropertyValue, enforceConfigurationChange);
if (!keyManagementWasSet) {
throw HostapdException(std::format("Failed to set hostapd 'wpa_key_mgmt' property to '{}'", keyManagementPropertyValue));
}

return true;
}
55 changes: 40 additions & 15 deletions src/linux/wpa-controller/include/Wpa/Hostapd.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <Wpa/IHostapd.hxx>
#include <Wpa/ProtocolHostapd.hxx>
#include <Wpa/ProtocolWpa.hxx>
#include <Wpa/WpaController.hxx>

namespace Wpa
Expand Down Expand Up @@ -57,6 +58,15 @@ struct Hostapd :
bool
Ping() override;

/**
* @brief Reloads the interface. This will cause any settings that have been changed to take effect.
*
* @return true If the configuration was reloaded successfully.
* @return false If the configuration was not reloaded successfully.
*/
bool
Reload() override;

/**
* @brief Get the name of the interface hostapd is managing.
*
Expand All @@ -73,16 +83,6 @@ 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.
*
Expand All @@ -99,20 +99,45 @@ struct Hostapd :
*
* @param propertyName The name of the property to set.
* @param propertyValue The value of the property to set.
* @param enforceConfigurationChange When the enforce the configuration change. A value of 'Now' will trigger a configuration reload.
* @return true The property was set successfully.
* @return false The property was not set successfully.
*/
bool
SetProperty(std::string_view propertyName, std::string_view propertyValue) override;
SetProperty(std::string_view propertyName, std::string_view propertyValue, EnforceConfigurationChange enforceConfigurationChange = EnforceConfigurationChange::Now) override;

/**
* @brief Reloads the interface.
* @brief Set the ssid for the interface.
*
* @return true
* @return false
* @param ssid The ssid to set.
* @param enforceConfigurationChange When the enforce the configuration change. A value of 'Now' will trigger a configuration reload.
* @return true If the ssid was set successfully.
* @return false If the ssid was not set successfully.
*/
bool
Reload() override;
SetSsid(std::string_view ssid, EnforceConfigurationChange enforceConfigurationChange = EnforceConfigurationChange::Now) override;

/**
* @brief Set the WPA protocol(s) for the interface.
*
* @param protocols The protocols to set.
* @param enforceConfigurationChange When the enforce the configuration change. A value of 'Now' will trigger a configuration reload.
* @return true If the protocols were set successfully.
* @return false If the protocols were not set successfully.
*/
bool
SetWpaProtocols(std::vector<WpaProtocol> protocols, EnforceConfigurationChange enforceConfigurationChange = EnforceConfigurationChange::Now) override;

/**
* @brief Set the Key Management object
*
* @param keyManagements The key management value(s) to set.
* @param enforceConfigurationChange When the enforce the configuration change. A value of 'Now' will trigger a configuration reload.
* @return true The key management value(s) were set successfully.
* @return false The key management value(s) were not set successfully.
*/
bool
SetKeyManagement(std::vector<WpaKeyManagement> keyManagements, EnforceConfigurationChange enforceConfigurationChange = EnforceConfigurationChange::Now) override;

private:
const std::string m_interface;
Expand Down
Loading

0 comments on commit 87b0285

Please sign in to comment.