Skip to content

Commit 201d04c

Browse files
authored
Merge pull request #304 from microsoft/wpaeventparsing
Add WPA Event Parsing helpers
2 parents 1022cf1 + 5ba9e04 commit 201d04c

File tree

7 files changed

+119
-7
lines changed

7 files changed

+119
-7
lines changed

src/linux/wpa-controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_sources(wpa-controller
1818
WpaController.cxx
1919
WpaControlSocket.cxx
2020
WpaControlSocketConnection.cxx
21+
WpaEvent.cxx
2122
WpaEventHandler.cxx
2223
WpaEventListenerProxy.cxx
2324
WpaKeyValuePair.cxx

src/linux/wpa-controller/Hostapd.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,5 +530,6 @@ Hostapd::GetIpAddress() const noexcept
530530
void
531531
Hostapd::OnWpaEvent(WpaEventSender* sender, const WpaEventArgs* eventArgs)
532532
{
533-
LOGD << std::format("Hostapd event @ {}: {:#08x} {}", eventArgs->Timestamp, reinterpret_cast<uintptr_t>(sender), eventArgs->Event.Payload);
533+
const auto& event{ eventArgs->Event };
534+
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);
534535
}

src/linux/wpa-controller/WpaEvent.cxx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
#include <charconv>
3+
#include <format>
4+
#include <optional>
5+
#include <string>
6+
#include <string_view>
7+
#include <type_traits>
8+
9+
#include <Wpa/ProtocolWpa.hxx>
10+
#include <Wpa/WpaCore.hxx>
11+
#include <Wpa/WpaEvent.hxx>
12+
#include <magic_enum.hpp>
13+
14+
using namespace Wpa;
15+
16+
/* static */
17+
std::optional<WpaEvent>
18+
WpaEvent::Parse(std::string_view eventPayload)
19+
{
20+
WpaEvent event;
21+
22+
// Find the log level delimeters.
23+
const std::size_t logLevelStart = eventPayload.find(ProtocolWpa::EventLogLevelDelimeterStart);
24+
const std::size_t logLevelEnd = (logLevelStart != std::string::npos) ? eventPayload.find(ProtocolWpa::EventLogLevelDelimeterEnd, logLevelStart) : std::string::npos;
25+
if (logLevelEnd == std::string::npos) {
26+
return std::nullopt;
27+
}
28+
29+
// Parse the log level.
30+
std::underlying_type_t<WpaLogLevel> logLevelValue{};
31+
const auto logLevel = eventPayload.substr(logLevelStart + 1, logLevelEnd - logLevelStart - 1);
32+
const auto logLevelConversionResult = std::from_chars(std::data(logLevel), std::data(logLevel) + std::size(logLevel), logLevelValue);
33+
if (logLevelConversionResult.ec != std::errc{}) {
34+
return std::nullopt;
35+
}
36+
37+
auto logLevelEnum = magic_enum::enum_cast<WpaLogLevel>(logLevelValue);
38+
if (logLevelEnum.has_value()) {
39+
event.LogLevel = logLevelEnum.value();
40+
}
41+
42+
// Find the interface name prefix, if present (it's only present with messages from global control sockets).
43+
const std::size_t interfaceStart = eventPayload.find(ProtocolWpa::EventInterfaceNamePrefix);
44+
const std::size_t interfaceEnd = (interfaceStart != std::string::npos) ? eventPayload.find(' ', interfaceStart) : std::string::npos;
45+
if (interfaceEnd != std::string::npos) {
46+
event.Interface = eventPayload.substr(interfaceStart, interfaceEnd - interfaceStart);
47+
}
48+
49+
// Find the payload.
50+
const std::size_t payloadStart = (interfaceEnd != std::string::npos) ? interfaceEnd + 1 : logLevelEnd + 1;
51+
event.Payload = eventPayload.substr(payloadStart);
52+
53+
return event;
54+
}
55+
56+
std::string
57+
WpaEvent::ToString() const
58+
{
59+
return std::format("[{}|{}] {} {}", magic_enum::enum_name(Source), magic_enum::enum_name(LogLevel), Interface.value_or(""), Payload);
60+
}

src/linux/wpa-controller/WpaEventHandler.cxx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ WpaEventHandler::ProcessNextEvent(WpaControlSocketConnection& wpaControlSocketCo
110110
std::string wpaEventPayload{ std::data(wpaEventBuffer), wpaEventBufferSize };
111111

112112
// Create a WPA event args object to pass to the listeners.
113+
auto wpaEvent = WpaEvent::Parse(wpaEventPayload);
114+
if (!wpaEvent.has_value()) {
115+
LOGW << std::format("Failed to parse WPA event payload on interface '{}': payload='{}'", InterfaceName, wpaEventPayload);
116+
return;
117+
}
118+
119+
// Update the event source as this cannot be inferred/parsed from the payload.
120+
wpaEvent->Source = m_wpaType;
121+
113122
WpaEventArgs wpaEventArgs{
114123
.Timestamp = timestampNow,
115-
.Event = {
116-
.Source = m_wpaType,
117-
.Payload = { std::data(wpaEventBuffer), wpaEventBufferSize },
118-
},
124+
.Event = std::move(wpaEvent.value()),
119125
};
120126

121127
// Make a copy of the event listeners to minimuze the time we hold the state lock. This is safe since the event

src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ struct ProtocolWpa
6666
*/
6767
static constexpr auto EventSizeMax{ 4096 };
6868

69+
/**
70+
* @brief The prefix used to identify the interface name in an event payload.
71+
*/
72+
static constexpr auto EventInterfaceNamePrefix{ "IFNAME=" };
73+
static constexpr auto EventLogLevelDelimeterStart{ "<" };
74+
static constexpr auto EventLogLevelDelimeterEnd{ ">" };
75+
6976
/**
7077
* @brief Determines if a response payload indicates success.
7178
*

src/linux/wpa-controller/include/Wpa/WpaCore.hxx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@
66

77
namespace Wpa
88
{
9+
/**
10+
* @brief Log levels for WPA messages and events.
11+
*/
12+
enum class WpaLogLevel : int32_t {
13+
Unknown = -1,
14+
Excessive = 0,
15+
MessageDump = 1,
16+
Debug = 2,
17+
Info = 3,
18+
Warning = 4,
19+
Error = 5,
20+
};
21+
922
/**
1023
* @brief The type of WPA daemon/service.
1124
*/
1225
enum class WpaType {
26+
Unknown,
1327
Hostapd,
1428
WpaSupplicant,
1529
};
@@ -28,6 +42,8 @@ GetWpaTypeDaemonBinaryName(WpaType type) noexcept
2842
return "hostapd";
2943
case WpaType::WpaSupplicant:
3044
return "wpa_supplicant";
45+
case WpaType::Unknown:
46+
[[fallthrough]];
3147
default:
3248
return "unknown";
3349
}

src/linux/wpa-controller/include/Wpa/WpaEvent.hxx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#ifndef WPA_EVENT_HXX
33
#define WPA_EVENT_HXX
44

5+
#include <optional>
56
#include <string>
7+
#include <string_view>
68

79
#include <Wpa/WpaCore.hxx>
810

@@ -13,8 +15,27 @@ namespace Wpa
1315
*/
1416
struct WpaEvent
1517
{
16-
WpaType Source;
17-
std::string Payload;
18+
WpaType Source{ WpaType::Unknown };
19+
WpaLogLevel LogLevel{ WpaLogLevel::Unknown };
20+
std::string Payload{};
21+
std::optional<std::string> Interface{ std::nullopt };
22+
23+
/**
24+
* @brief Parse a string into a WpaEvent.
25+
*
26+
* @param eventPayload The string to parse.
27+
* @return std::optional<WpaEvent>
28+
*/
29+
static std::optional<WpaEvent>
30+
Parse(std::string_view eventPayload);
31+
32+
/**
33+
* @brief Convert the event to a string.
34+
*
35+
* @return std::string
36+
*/
37+
std::string
38+
ToString() const;
1839
};
1940

2041
} // namespace Wpa

0 commit comments

Comments
 (0)