Skip to content

Commit

Permalink
Merge pull request #304 from microsoft/wpaeventparsing
Browse files Browse the repository at this point in the history
Add WPA Event Parsing helpers
  • Loading branch information
abeltrano authored Jul 2, 2024
2 parents 1022cf1 + 5ba9e04 commit 201d04c
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/linux/wpa-controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_sources(wpa-controller
WpaController.cxx
WpaControlSocket.cxx
WpaControlSocketConnection.cxx
WpaEvent.cxx
WpaEventHandler.cxx
WpaEventListenerProxy.cxx
WpaKeyValuePair.cxx
Expand Down
3 changes: 2 additions & 1 deletion src/linux/wpa-controller/Hostapd.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -530,5 +530,6 @@ Hostapd::GetIpAddress() const noexcept
void
Hostapd::OnWpaEvent(WpaEventSender* sender, const WpaEventArgs* eventArgs)
{
LOGD << std::format("Hostapd event @ {}: {:#08x} {}", eventArgs->Timestamp, reinterpret_cast<uintptr_t>(sender), eventArgs->Event.Payload);
const auto& event{ eventArgs->Event };
LOGD << std::format("> [{}-Event|{}|{}|Sender={:#08x}] {}", magic_enum::enum_name(event.Source), magic_enum::enum_name(event.LogLevel), eventArgs->Timestamp, reinterpret_cast<uintptr_t>(sender), event.Payload);
}
60 changes: 60 additions & 0 deletions src/linux/wpa-controller/WpaEvent.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

#include <charconv>
#include <format>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>

#include <Wpa/ProtocolWpa.hxx>
#include <Wpa/WpaCore.hxx>
#include <Wpa/WpaEvent.hxx>
#include <magic_enum.hpp>

using namespace Wpa;

/* static */
std::optional<WpaEvent>
WpaEvent::Parse(std::string_view eventPayload)
{
WpaEvent event;

// Find the log level delimeters.
const std::size_t logLevelStart = eventPayload.find(ProtocolWpa::EventLogLevelDelimeterStart);
const std::size_t logLevelEnd = (logLevelStart != std::string::npos) ? eventPayload.find(ProtocolWpa::EventLogLevelDelimeterEnd, logLevelStart) : std::string::npos;
if (logLevelEnd == std::string::npos) {
return std::nullopt;
}

// Parse the log level.
std::underlying_type_t<WpaLogLevel> logLevelValue{};
const auto logLevel = eventPayload.substr(logLevelStart + 1, logLevelEnd - logLevelStart - 1);
const auto logLevelConversionResult = std::from_chars(std::data(logLevel), std::data(logLevel) + std::size(logLevel), logLevelValue);
if (logLevelConversionResult.ec != std::errc{}) {
return std::nullopt;
}

auto logLevelEnum = magic_enum::enum_cast<WpaLogLevel>(logLevelValue);
if (logLevelEnum.has_value()) {
event.LogLevel = logLevelEnum.value();
}

// Find the interface name prefix, if present (it's only present with messages from global control sockets).
const std::size_t interfaceStart = eventPayload.find(ProtocolWpa::EventInterfaceNamePrefix);
const std::size_t interfaceEnd = (interfaceStart != std::string::npos) ? eventPayload.find(' ', interfaceStart) : std::string::npos;
if (interfaceEnd != std::string::npos) {
event.Interface = eventPayload.substr(interfaceStart, interfaceEnd - interfaceStart);
}

// Find the payload.
const std::size_t payloadStart = (interfaceEnd != std::string::npos) ? interfaceEnd + 1 : logLevelEnd + 1;
event.Payload = eventPayload.substr(payloadStart);

return event;
}

std::string
WpaEvent::ToString() const
{
return std::format("[{}|{}] {} {}", magic_enum::enum_name(Source), magic_enum::enum_name(LogLevel), Interface.value_or(""), Payload);
}
14 changes: 10 additions & 4 deletions src/linux/wpa-controller/WpaEventHandler.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,18 @@ WpaEventHandler::ProcessNextEvent(WpaControlSocketConnection& wpaControlSocketCo
std::string wpaEventPayload{ std::data(wpaEventBuffer), wpaEventBufferSize };

// Create a WPA event args object to pass to the listeners.
auto wpaEvent = WpaEvent::Parse(wpaEventPayload);
if (!wpaEvent.has_value()) {
LOGW << std::format("Failed to parse WPA event payload on interface '{}': payload='{}'", InterfaceName, wpaEventPayload);
return;
}

// Update the event source as this cannot be inferred/parsed from the payload.
wpaEvent->Source = m_wpaType;

WpaEventArgs wpaEventArgs{
.Timestamp = timestampNow,
.Event = {
.Source = m_wpaType,
.Payload = { std::data(wpaEventBuffer), wpaEventBufferSize },
},
.Event = std::move(wpaEvent.value()),
};

// Make a copy of the event listeners to minimuze the time we hold the state lock. This is safe since the event
Expand Down
7 changes: 7 additions & 0 deletions src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ struct ProtocolWpa
*/
static constexpr auto EventSizeMax{ 4096 };

/**
* @brief The prefix used to identify the interface name in an event payload.
*/
static constexpr auto EventInterfaceNamePrefix{ "IFNAME=" };
static constexpr auto EventLogLevelDelimeterStart{ "<" };
static constexpr auto EventLogLevelDelimeterEnd{ ">" };

/**
* @brief Determines if a response payload indicates success.
*
Expand Down
16 changes: 16 additions & 0 deletions src/linux/wpa-controller/include/Wpa/WpaCore.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,24 @@

namespace Wpa
{
/**
* @brief Log levels for WPA messages and events.
*/
enum class WpaLogLevel : int32_t {
Unknown = -1,
Excessive = 0,
MessageDump = 1,
Debug = 2,
Info = 3,
Warning = 4,
Error = 5,
};

/**
* @brief The type of WPA daemon/service.
*/
enum class WpaType {
Unknown,
Hostapd,
WpaSupplicant,
};
Expand All @@ -28,6 +42,8 @@ GetWpaTypeDaemonBinaryName(WpaType type) noexcept
return "hostapd";
case WpaType::WpaSupplicant:
return "wpa_supplicant";
case WpaType::Unknown:
[[fallthrough]];
default:
return "unknown";
}
Expand Down
25 changes: 23 additions & 2 deletions src/linux/wpa-controller/include/Wpa/WpaEvent.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#ifndef WPA_EVENT_HXX
#define WPA_EVENT_HXX

#include <optional>
#include <string>
#include <string_view>

#include <Wpa/WpaCore.hxx>

Expand All @@ -13,8 +15,27 @@ namespace Wpa
*/
struct WpaEvent
{
WpaType Source;
std::string Payload;
WpaType Source{ WpaType::Unknown };
WpaLogLevel LogLevel{ WpaLogLevel::Unknown };
std::string Payload{};
std::optional<std::string> Interface{ std::nullopt };

/**
* @brief Parse a string into a WpaEvent.
*
* @param eventPayload The string to parse.
* @return std::optional<WpaEvent>
*/
static std::optional<WpaEvent>
Parse(std::string_view eventPayload);

/**
* @brief Convert the event to a string.
*
* @return std::string
*/
std::string
ToString() const;
};

} // namespace Wpa
Expand Down

0 comments on commit 201d04c

Please sign in to comment.