From 5a6b4f4cc3e573905a2daeff97270b0f2d8a2794 Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 16:16:51 +0000 Subject: [PATCH 1/3] Add wpa event parser. --- src/linux/wpa-controller/CMakeLists.txt | 1 + src/linux/wpa-controller/WpaEvent.cxx | 60 +++++++++++++++++++ src/linux/wpa-controller/WpaEventHandler.cxx | 14 +++-- .../include/Wpa/ProtocolWpa.hxx | 7 +++ .../wpa-controller/include/Wpa/WpaCore.hxx | 16 +++++ .../wpa-controller/include/Wpa/WpaEvent.hxx | 25 +++++++- 6 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 src/linux/wpa-controller/WpaEvent.cxx diff --git a/src/linux/wpa-controller/CMakeLists.txt b/src/linux/wpa-controller/CMakeLists.txt index 5aa49382..4c4f5c75 100644 --- a/src/linux/wpa-controller/CMakeLists.txt +++ b/src/linux/wpa-controller/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources(wpa-controller WpaController.cxx WpaControlSocket.cxx WpaControlSocketConnection.cxx + WpaEvent.cxx WpaEventHandler.cxx WpaEventListenerProxy.cxx WpaKeyValuePair.cxx diff --git a/src/linux/wpa-controller/WpaEvent.cxx b/src/linux/wpa-controller/WpaEvent.cxx new file mode 100644 index 00000000..3f231035 --- /dev/null +++ b/src/linux/wpa-controller/WpaEvent.cxx @@ -0,0 +1,60 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Wpa; + +/* static */ +std::optional +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 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(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(LogLevel), magic_enum::enum_name(Source), Interface.value_or(""), Payload); +} diff --git a/src/linux/wpa-controller/WpaEventHandler.cxx b/src/linux/wpa-controller/WpaEventHandler.cxx index 9a63e250..11a4f256 100644 --- a/src/linux/wpa-controller/WpaEventHandler.cxx +++ b/src/linux/wpa-controller/WpaEventHandler.cxx @@ -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 diff --git a/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx b/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx index 3eb182d4..d9ceff02 100644 --- a/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx +++ b/src/linux/wpa-controller/include/Wpa/ProtocolWpa.hxx @@ -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. * diff --git a/src/linux/wpa-controller/include/Wpa/WpaCore.hxx b/src/linux/wpa-controller/include/Wpa/WpaCore.hxx index 0fc06805..9f764413 100644 --- a/src/linux/wpa-controller/include/Wpa/WpaCore.hxx +++ b/src/linux/wpa-controller/include/Wpa/WpaCore.hxx @@ -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, }; @@ -28,6 +42,8 @@ GetWpaTypeDaemonBinaryName(WpaType type) noexcept return "hostapd"; case WpaType::WpaSupplicant: return "wpa_supplicant"; + case WpaType::Unknown: + [[fallthrough]]; default: return "unknown"; } diff --git a/src/linux/wpa-controller/include/Wpa/WpaEvent.hxx b/src/linux/wpa-controller/include/Wpa/WpaEvent.hxx index 2d0bc3eb..e5132f70 100644 --- a/src/linux/wpa-controller/include/Wpa/WpaEvent.hxx +++ b/src/linux/wpa-controller/include/Wpa/WpaEvent.hxx @@ -2,7 +2,9 @@ #ifndef WPA_EVENT_HXX #define WPA_EVENT_HXX +#include #include +#include #include @@ -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 Interface{ std::nullopt }; + + /** + * @brief Parse a string into a WpaEvent. + * + * @param eventPayload The string to parse. + * @return std::optional + */ + static std::optional + Parse(std::string_view eventPayload); + + /** + * @brief Convert the event to a string. + * + * @return std::string + */ + std::string + ToString() const; }; } // namespace Wpa From 4bbbad617c7417adb09573a4faeea7ebe4365c12 Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 16:17:40 +0000 Subject: [PATCH 2/3] Use WpaEvent::ToString for output. --- src/linux/wpa-controller/Hostapd.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linux/wpa-controller/Hostapd.cxx b/src/linux/wpa-controller/Hostapd.cxx index c3cb6062..823841ca 100644 --- a/src/linux/wpa-controller/Hostapd.cxx +++ b/src/linux/wpa-controller/Hostapd.cxx @@ -530,5 +530,5 @@ Hostapd::GetIpAddress() const noexcept void Hostapd::OnWpaEvent(WpaEventSender* sender, const WpaEventArgs* eventArgs) { - LOGD << std::format("Hostapd event @ {}: {:#08x} {}", eventArgs->Timestamp, reinterpret_cast(sender), eventArgs->Event.Payload); + LOGD << std::format("Hostapd event @ {}: {:#08x} {}", eventArgs->Timestamp, reinterpret_cast(sender), eventArgs->Event.ToString()); } From 5ba9e04365a45e96d350716de1dfced1b3338488 Mon Sep 17 00:00:00 2001 From: Andrew Beltrano Date: Tue, 2 Jul 2024 16:30:29 +0000 Subject: [PATCH 3/3] Update log formatting output. --- src/linux/wpa-controller/Hostapd.cxx | 3 ++- src/linux/wpa-controller/WpaEvent.cxx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/linux/wpa-controller/Hostapd.cxx b/src/linux/wpa-controller/Hostapd.cxx index 823841ca..f38414b1 100644 --- a/src/linux/wpa-controller/Hostapd.cxx +++ b/src/linux/wpa-controller/Hostapd.cxx @@ -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(sender), eventArgs->Event.ToString()); + 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(sender), event.Payload); } diff --git a/src/linux/wpa-controller/WpaEvent.cxx b/src/linux/wpa-controller/WpaEvent.cxx index 3f231035..3c7c870b 100644 --- a/src/linux/wpa-controller/WpaEvent.cxx +++ b/src/linux/wpa-controller/WpaEvent.cxx @@ -56,5 +56,5 @@ WpaEvent::Parse(std::string_view eventPayload) std::string WpaEvent::ToString() const { - return std::format("[{}] {}{}{}", magic_enum::enum_name(LogLevel), magic_enum::enum_name(Source), Interface.value_or(""), Payload); + return std::format("[{}|{}] {} {}", magic_enum::enum_name(Source), magic_enum::enum_name(LogLevel), Interface.value_or(""), Payload); }