diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4ac2ffa --- /dev/null +++ b/.clang-format @@ -0,0 +1,96 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +# AlignConsecutiveBitFields: true +AlignConsecutiveDeclarations: false +# AlignConsecutiveMacros: true +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +# AllowAllArgumentsOnNextLine: true +# AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +# AllowShortLambdasOnASingleLine: Inline +# AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +# AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: All +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BasedOnStyle: WebKit +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + # AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakInheritanceList: AfterColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth : 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +# DeriveLineEnding: true +# DerivePointerAlignment: true +FixNamespaceComments: true +IncludeBlocks: Preserve +# IndentCaseBlocks: false +IndentCaseLabels: true +# IndentExternBlock: NoIndent +# IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 4 +# InsertTrailingCommas: Wrapped +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +# SpaceAroundPointerQualifiers: After +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +# SpaceBeforeSquareBrackets: false +# SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +# SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +Standard: Cpp11 +# UseCRLF: false +UseTab: Never \ No newline at end of file diff --git a/.gitignore b/.gitignore index dfcfd56..e34befd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,350 +1,38 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Prerequisites +*.d -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta +# Compiled Object files +*.slo +*.lo +*.o *.obj -*.iobj + +# Precompiled Headers +*.gch *.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc -# Chutzpah Test files -_Chutzpah* +# Compiled Dynamic libraries +*.so +*.dylib +*.dll -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb +# Fortran module files +*.mod +*.smod -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap +# Compiled Static libraries +*.lai +*.la +*.a +*.lib -# Visual Studio Trace Files -*.e2e +# Executables +*.exe +*.out +*.app -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ +# Build directories. +build*/ +debug/ +release/ +.vscode \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..877d75a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,86 @@ +# project options +cmake_minimum_required(VERSION 3.14) +project(wifi-telemetry CXX) + +# c++ standard options +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# allow setting target_link_libraries() from any dir +cmake_policy(SET CMP0079 NEW) + +# define option for use of Microsoft tracelogging +option( + ENABLE_MS_LTTNG_EXTENSIONS + "Enable use of Microsoft tracelogging extensions for LTTNG" + OFF +) + +# define option to build hostap dev dependency in-tree +option( + BUILD_HOSTAP_EXTERNAL + "Build wpa_supplicant/hostapd from built-in external source" + ON +) + +# define option to build sdbus-c++ dev dependency in-tree +option( + BUILD_SDBUS_CPP_EXTERNAL + "Build sdbus-c++ from built-in external source" + ON +) + +include(ExternalProject) +include(GNUInstallDirs) +include(CheckPIESupported) +include(cmake/find_wpa_client.cmake) +include(cmake/find_systemd.cmake) +include(cmake/FindLTTngUST.cmake) +include(cmake/find_pthreads.cmake) +include(cmake/find_sdbus-cpp.cmake) +if (ENABLE_MS_LTTNG_EXTENSIONS) + include(cmake/find_tracelogging.cmake) +endif() + +check_pie_supported() +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# compilation options +add_compile_options( + -Wall + -Wshadow + -Wformat-security + -Werror + -Wextra + -Wpedantic + -Wconversion +) + +if (CMAKE_C_COMPILER_ID STREQUAL "Clang") + add_compile_options( + -fstack-protector + -fvisibility=hidden + ) +elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU") + add_compile_options( + -fstack-clash-protection + -fstack-protector-all + -fvisibility=hidden + -Wl,-z,noexecstack + -Wl,-z,now + -Wl,-z,relro + ) +endif() + +if (CMAKE_BUILD_TYPE MATCHES "(Release|RelWithDebInfo|MinSizeRel)") + add_compile_definitions(_FORTIFY_SOURCE=2) +endif() + +if (ENABLE_MS_LTTNG_EXTENSIONS) + add_compile_definitions(TRACE_USE_TRACELOGGING) +endif() + +include_directories(include) + +add_subdirectory(src) diff --git a/README.md b/README.md index 5cd7cec..5bb28fc 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,55 @@ -# Project - -> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. - -As the maintainer of this project, please make a few updates: - -- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +# Wi-Fi Telemetry + +This project provides a Wi-Fi telemetry library and daemon. It enables realtime +collection and reporting of Wi-Fi related events. It can be used in conjunction +with the the [wifi-ztp](https://github.com/microsoft/wifi-ztp) project to +analyze and collect Wi-Fi zero touch provisioning telemetry as well (-s ztp +command line option). + +## Building + +### Ubuntu (focal) + +```bash +sudo apt install \ + build-essential \ + cmake \ + git \ + liblttng-ust-dev \ + libpci-dev \ + libssl-dev \ + libsystemd-dev \ + pkg-config +``` + +Checkout and build: + +```bash +git clone git@github.com:microsoft/wifi-telemetry.git +cd wifi-telemetry +mkdir build && cd $_ +cmake .. +make -j $(nproc) +``` + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com). + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft +trademarks or logos is subject to and must follow +[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). +Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. +Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/SUPPORT.md b/SUPPORT.md index dc72f0e..b69d58e 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,25 +1,14 @@ -# TODO: The maintainer of this repo has not yet edited this file - -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). -- **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide. - -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* - # Support ## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +This project uses GitHub Issues to track bugs and feature requests. Please +search the existing issues before filing new issues to avoid duplicates. For +new issues, file your bug or feature request as a new Issue. -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. +For help and questions about using this project, please contact +. ## Microsoft Support Policy -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. +Support for this project is limited to the resources listed above. diff --git a/cmake/FindLTTngUST.cmake b/cmake/FindLTTngUST.cmake new file mode 100644 index 0000000..332d0f2 --- /dev/null +++ b/cmake/FindLTTngUST.cmake @@ -0,0 +1,116 @@ +#.rst: +# FindLTTngUST +# ------------ +# +# This module finds the `LTTng-UST `__ library. +# +# Imported target +# ^^^^^^^^^^^^^^^ +# +# This module defines the following :prop_tgt:`IMPORTED` target: +# +# ``LTTng::UST`` +# The LTTng-UST library, if found +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module sets the following +# +# ``LTTNGUST_FOUND`` +# ``TRUE`` if system has LTTng-UST +# ``LTTNGUST_INCLUDE_DIRS`` +# The LTTng-UST include directories +# ``LTTNGUST_LIBRARIES`` +# The libraries needed to use LTTng-UST +# ``LTTNGUST_VERSION_STRING`` +# The LTTng-UST version +# ``LTTNGUST_HAS_TRACEF`` +# ``TRUE`` if the ``tracef()`` API is available in the system's LTTng-UST +# ``LTTNGUST_HAS_TRACELOG`` +# ``TRUE`` if the ``tracelog()`` API is available in the system's LTTng-UST + +#============================================================================= +# Copyright 2016 Kitware, Inc. +# Copyright 2016 Philippe Proulx +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +find_path(LTTNGUST_INCLUDE_DIRS NAMES lttng/tracepoint.h) +# Must also check for the path of generated header files since out-of-tree +# build is a possibility (Yocto). +find_path(LTTNGUST_INCLUDE_DIRS_GENERATED NAMES lttng/ust-config.h) +find_library(LTTNGUST_LIBRARIES NAMES lttng-ust) + +if(LTTNGUST_INCLUDE_DIRS AND LTTNGUST_INCLUDE_DIRS_GENERATED AND LTTNGUST_LIBRARIES) + # find tracef() and tracelog() support + set(LTTNGUST_HAS_TRACEF 0) + set(LTTNGUST_HAS_TRACELOG 0) + + if(EXISTS "${LTTNGUST_INCLUDE_DIRS}/lttng/tracef.h") + set(LTTNGUST_HAS_TRACEF TRUE) + endif() + + if(EXISTS "${LTTNGUST_INCLUDE_DIRS}/lttng/tracelog.h") + set(LTTNGUST_HAS_TRACELOG TRUE) + endif() + + # get version + set(lttngust_version_file "${LTTNGUST_INCLUDE_DIRS_GENERATED}/lttng/ust-version.h") + + if(EXISTS "${lttngust_version_file}") + file(STRINGS "${lttngust_version_file}" lttngust_version_major_string + REGEX "^[\t ]*#define[\t ]+LTTNG_UST_MAJOR_VERSION[\t ]+[0-9]+[\t ]*$") + file(STRINGS "${lttngust_version_file}" lttngust_version_minor_string + REGEX "^[\t ]*#define[\t ]+LTTNG_UST_MINOR_VERSION[\t ]+[0-9]+[\t ]*$") + file(STRINGS "${lttngust_version_file}" lttngust_version_patch_string + REGEX "^[\t ]*#define[\t ]+LTTNG_UST_PATCHLEVEL_VERSION[\t ]+[0-9]+[\t ]*$") + string(REGEX REPLACE ".*([0-9]+).*" "\\1" + lttngust_v_major "${lttngust_version_major_string}") + string(REGEX REPLACE ".*([0-9]+).*" "\\1" + lttngust_v_minor "${lttngust_version_minor_string}") + string(REGEX REPLACE ".*([0-9]+).*" "\\1" + lttngust_v_patch "${lttngust_version_patch_string}") + set(LTTNGUST_VERSION_STRING + "${lttngust_v_major}.${lttngust_v_minor}.${lttngust_v_patch}") + unset(lttngust_version_major_string) + unset(lttngust_version_minor_string) + unset(lttngust_version_patch_string) + unset(lttngust_v_major) + unset(lttngust_v_minor) + unset(lttngust_v_patch) + else() + message(FATAL_ERROR "Missing version header") + endif() + + unset(lttngust_version_file) + + if(NOT TARGET LTTng::UST) + add_library(LTTng::UST UNKNOWN IMPORTED) + set_target_properties(LTTng::UST PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${LTTNGUST_INCLUDE_DIRS};${LTTNGUST_INCLUDE_DIRS_GENERATED}" + INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${LTTNGUST_LIBRARIES}") + endif() + + # add libdl to required libraries + set(LTTNGUST_LIBRARIES ${LTTNGUST_LIBRARIES} ${CMAKE_DL_LIBS}) +endif() + +# handle the QUIETLY and REQUIRED arguments and set LTTNGUST_FOUND to +# TRUE if all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LTTngUST FOUND_VAR LTTNGUST_FOUND + REQUIRED_VARS LTTNGUST_LIBRARIES + LTTNGUST_INCLUDE_DIRS + VERSION_VAR LTTNGUST_VERSION_STRING) +mark_as_advanced(LTTNGUST_LIBRARIES LTTNGUST_INCLUDE_DIRS) \ No newline at end of file diff --git a/cmake/find_pthreads.cmake b/cmake/find_pthreads.cmake new file mode 100644 index 0000000..416f5ae --- /dev/null +++ b/cmake/find_pthreads.cmake @@ -0,0 +1,2 @@ + +find_package(Threads REQUIRED) \ No newline at end of file diff --git a/cmake/find_sdbus-cpp.cmake b/cmake/find_sdbus-cpp.cmake new file mode 100644 index 0000000..ed66aff --- /dev/null +++ b/cmake/find_sdbus-cpp.cmake @@ -0,0 +1,6 @@ + +if (BUILD_SDBUS_CPP_EXTERNAL) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../external/sdbus-c++) +else() + find_package(sdbus-c++ REQUIRED) +endif() \ No newline at end of file diff --git a/cmake/find_systemd.cmake b/cmake/find_systemd.cmake new file mode 100644 index 0000000..e63f240 --- /dev/null +++ b/cmake/find_systemd.cmake @@ -0,0 +1,9 @@ +find_library(LIBSYSTEMD + NAMES libsystemd.so + REQUIRED) + +if(LIBSYSTEMD) + set(LIBSYSTEMD_TARGET ${LIBSYSTEMD}) +endif() + +MESSAGE(STATUS LIBSYSTEMD=${LIBSYSTEMD}) \ No newline at end of file diff --git a/cmake/find_tracelogging.cmake b/cmake/find_tracelogging.cmake new file mode 100644 index 0000000..ee8ed5a --- /dev/null +++ b/cmake/find_tracelogging.cmake @@ -0,0 +1,2 @@ + +find_package(tracelogging REQUIRED) diff --git a/cmake/find_wpa_client.cmake b/cmake/find_wpa_client.cmake new file mode 100644 index 0000000..5e0d9d0 --- /dev/null +++ b/cmake/find_wpa_client.cmake @@ -0,0 +1,12 @@ +if (BUILD_HOSTAP_EXTERNAL) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../external/hostap) +else() + find_library(LIBWPA_CLIENT + NAMES libwpa_client.so + REQUIRED) +endif() + +if(LIBWPA_CLIENT) + set(LIBWPA_CLIENT_TARGET ${LIBWPA_CLIENT}) + MESSAGE(STATUS "Found wpa client: ${LIBWPA_CLIENT}") +endif() diff --git a/cmake/wifi-telemetry-config.cmake b/cmake/wifi-telemetry-config.cmake new file mode 100644 index 0000000..25554b9 --- /dev/null +++ b/cmake/wifi-telemetry-config.cmake @@ -0,0 +1,9 @@ + +include(CMakeFindDependencyMacro) + +find_library(LIBSYSTEMD NAMES libsystemd.so) +find_library(LIBWPA_CLIENT NAMES libwpa_client.so) +find_dependency(LTTngUST) +find_dependency(Threads) + +include("${CMAKE_CURRENT_LIST_DIR}/wifi-telemetry-targets.cmake") diff --git a/external/hostap/.config b/external/hostap/.config new file mode 100644 index 0000000..e57b0b2 --- /dev/null +++ b/external/hostap/.config @@ -0,0 +1,2 @@ +CONFIG_CTRL_IFACE=y +CONFIG_BUILD_WPA_CLIENT_SO=y \ No newline at end of file diff --git a/external/hostap/CMakeLists.txt b/external/hostap/CMakeLists.txt new file mode 100644 index 0000000..d54b9a5 --- /dev/null +++ b/external/hostap/CMakeLists.txt @@ -0,0 +1,23 @@ + +find_program(MAKE_EXE + NAMES gmake nmake make + REQUIRED +) + +set(HOSTAP_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/hostap-prefix/src/hostap/wpa_supplicant) + +ExternalProject_Add(hostap + GIT_REPOSITORY "http://w1.fi/hostap.git" + GIT_TAG "5f082c158c49a23614b69e4465e9a7299f004f5f" + CONFIGURE_COMMAND cp -n ${CMAKE_CURRENT_SOURCE_DIR}/.config ${HOSTAP_PREFIX}/.config + BINARY_DIR ${HOSTAP_PREFIX} + BUILD_COMMAND QUIET=1 $(MAKE) libwpa_client.so + INSTALL_DIR ${HOSTAP_PREFIX} + INSTALL_COMMAND QUIET=1 $(MAKE) install +) + +set(LIBWPA_CLIENT + ${HOSTAP_PREFIX}/libwpa_client.so + CACHE FILEPATH + "wpa client shared object" +) diff --git a/external/sdbus-c++/CMakeLists.txt b/external/sdbus-c++/CMakeLists.txt new file mode 100644 index 0000000..b726873 --- /dev/null +++ b/external/sdbus-c++/CMakeLists.txt @@ -0,0 +1,23 @@ + +set(SDBUS_CPP_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/sdbus-cpp-prefix) + +ExternalProject_Add(sdbus-cpp + GIT_REPOSITORY "https://github.com/Kistler-Group/sdbus-cpp.git" + GIT_TAG "v0.8.3" +) + +set(SDBUS_CPP_EXTERNAL_LIBS + "${SDBUS_CPP_PREFIX}/src/sdbus-cpp-build" + CACHE PATH + "Directory of external sdbus-c++ libraries" +) +set(SDBUS_CPP_EXTERNAL_NAME + sdbus-c++ + CACHE STRING + "sdbus-c++ shared object name" +) +set(SDBUS_CPP_EXTERNAL + ${SDBUS_CPP_EXTERNAL_LIBS}/lib${SDBUS_CPP_EXTERNAL_NAME}.so + CACHE FILEPATH + "sdbus-c++ shared object" +) diff --git a/include/wifi-telemetry/wifi/wifi_80211.hpp b/include/wifi-telemetry/wifi/wifi_80211.hpp new file mode 100644 index 0000000..58b850c --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_80211.hpp @@ -0,0 +1,230 @@ + +#ifndef __WIFI_80211_HPP__ +#define __WIFI_80211_HPP__ + +#include +#include + +/** + * @brief Unsolicited Management Frame reason code for Disassociation. + * @see IEEE Std 802.11-2016, Section 9.4.1.7, Table 9-45. + */ +enum class WifiDeauthenticationReasonCode : uint16_t +{ + Unspecified = 1, + InvalidAuthentication = 2, + LeavingNetworkDeauth = 3, + Inactivity = 4, + NoMoreStas = 5, + InvalidClass2Frame = 6, + InvalidClass3Frame = 7, + LeavingNetworkDisassoc = 8, + NotAuthenticated = 9, + UnacceptablePowerCapability = 10, + UnacceptableSupportedChannels = 11, + BssTransitionDeassocac = 12, + InvalidElement = 13, + MicFailure = 14, + FourWayHandshakeTimeout = 15, + GroupKeyHandshakeTimeout = 16, + HandshakeElementMismatch = 17, + InvalidGroupCipher = 18, + InvalidPairwiseCipher = 19, + InvalidAkmp = 20, + UnsupportedRsneVersion = 21, + InvalidRsneCapabilities = 22, + Ieee80211XAuthFailed = 23, + CipherOutOfPolicy = 24, + TdlsPeerUnreachable = 25, + TdlsUnspecifiedReason = 26, + SspRequestedDissassoc = 27, + NoSspRoamingAgreement = 28, + BadCipherOrAkm = 29, + NotAuthorizedThisLocation = 30, + ServiceChangePrecludesTs = 31, + UnspecifiedQosReason = 32, + NotEnoughBandwidth = 33, + MissingAcks = 34, + ExceededTxop = 35, + StaLeaving = 36, + EndTsBaDls = 37, + UnknownTsBa = 38, + Timeout = 39, + PeerkeyMismatch = 45, + PeerInitiated = 46, + ApInitiated = 47, + InvalidFtActionFrameCount = 48, + InvalidPmkid = 49, + InvalidMde = 50, + InvalidFte = 51, + MeshPeeringCanceled = 52, + MeshMaxPeers = 53, + MeshConfigurationPolicyViolation = 54, + MeshCloseRecvd = 55, + MeshMaxRetries = 56, + MeshConfirmTimeout = 57, + MeshInvalidGtk = 58, + MeshInconsistentParameters = 59, + MeshInvalidSecurityCapability = 60, + MeshPathErrorNoProxyInformation = 61, + MeshPathErrorNoForwardingInformation = 62, + MeshPathErrorDestinationUnreachable = 63, + MaxAddressAlreadyExistsInMbss = 64, + MeshChannelSwitchRegulatoryRequirements = 65, + MeshChannelSwitchUnspecified = 66, +}; + +/** + * @brief Unsolicited Management Frame status code. + * @see IEEE Std 802.11-2016, Section 9.4.1.9, Table 9-46. + */ +enum class WifiStatusCode : uint16_t +{ + Success = 0, + Refused = 1, + TdlsRejectedAlternativeProvided = 2, + TdlsRejected = 3, + /* reserved = 4 */ + SecurityDisabled = 5, + UnacceptableLifetime = 6, + NotInSameBss = 7, + /* reserved = 8-9 */ + RefusedCapabilitiesMismatch = 10, + DeniedNoAssociationExists = 11, + DeniedOtherReason = 12, + UnsupportedAuthAlgorithm = 13, + TransactionSequenceError = 14, + ChallengeFailure = 15, + RejectedSequenceTimeout = 16, + DeniedNoMoreStas = 17, + RefusedBasicRatesMismatch = 18, + DeniedNoShortPreableSupport = 19, + /* reserved = 20-21 */ + RejectedSpectrumManagementRequired = 22, + RejectedBadPowerCapability = 23, + RejectedBadSupportedChannels = 24, + DeniedNoShortSlotTimeSupport = 25, + /* reserved = 26 */ + DeniedNoHtSupport = 27, + R0KHUnreachable = 28, + DeniedPcoTimeNotSupported = 29, + RefusedTemporarily = 30, + RobustManagementPolicyViolation = 31, + UnspecifiedQosFailure = 32, + DeniedInsufficientBandwidth = 33, + DeniedPoorChannelConditions = 34, + DeniedQosNotSupported = 35, + /* reserved = 36 */ + RequestDeclined = 37, + InvalidParameters = 38, + RejectedWithSuggestedChanges = 39, + StatusInvalidElement = 40, + StatusInvalidGroupCipher = 41, + StatusInvalidPairwiseCipher = 42, + StatusInvalidAkmp = 43, + UnsupportedRsneVersion = 44, + InvalidRsneCapabilities = 45, + StatusCipherOutOfPolicy = 46, + RejectedForDelayPeriod = 47, + DlsNotAllowed = 48, + NotPresent = 49, + NotQosSta = 50, + DeniedListenIntervalTooLarge = 51, + StatusInvalidFtActionFrameCount = 52, + StatusInvalidPmkid = 53, + StatusInvalidMde = 54, + StatusInvalidFte = 55, + RequestTclasNotSupported = 56, + InsufficientTclasProcessingResources = 57, + TryAnotherBss = 58, + GasAdvertisementProtocolNotSupported = 59, + NoOutstandingGasRequest = 60, + GasResponseNotReceivedFromServer = 61, + GasQueryTimeout = 62, + GasQueryResponseTooLarge = 63, + RejectedHomeWithSuggestedChanges = 64, + ServerUnreachable = 65, + /* reserved = 66 */ + RejectedForSspPermissions = 67, + RefusedUnauthenticatedAccessNotSupported = 68, + /* reserved = 69-71 */ + InvalidRsne = 72, + UApsdCoexistanceNotSupported = 73, + UApsdCoexModeNotSupported = 74, + BadIntervalWithUApsdCoex = 75, + AntiCloggingTokenRequired = 76, + UnsupportedFiniteCyclicGroup = 77, + CannotFindAlternativeTbtt = 78, + TransmissionFailure = 79, + RequestTclassNotSupported = 80, + TclasResourcesExhausted = 81, + RejectedWithSuggestedBssTransition = 82, + RejectWithSchedule = 83, + RejectNoWakeupSpecified = 84, + SuccessPowerSaveMode = 85, + PendingAdmittingFstSession = 86, + PerformingFstNow = 87, + PendingGapInBaWindow = 88, + RejectUPidSetting = 89, + /* reserved = 90-91 */ + RefusedExternalReason = 92, + RefusedApOutOutMemory = 93, + RejectedEmergencyServicesNotSupported = 94, + QueryResponseOutstanding = 95, + RejectBadDseBand = 96, + TclasProcessingTerminated = 97, + TsScheduleConflict = 98, + DeniedWithSuggestedBandAndChannel = 99, + MccaopReservationConflict = 100, + MafLimitExceeded = 101, + MccaTrackLimitExceeded = 102, + DeniedDueToSpectrumManagement = 103, + DeniedVhtNotSupported = 104, + EnablementDenied = 105, + RestrictionFromAuthorixzedGdb = 106, + AuthorizationDeenabled = 107, + /* reserved = 108-165, 535 */ +}; + +enum class WifiAuthenticationType : uint32_t +{ + Unknown = 0x0000, + Open = 0x0001, + WpaPsk = 0x0002, + Shared = 0x0004, + Wpa = 0x0008, + Wpa2 = 0x0010, + Wpa2Psk = 0x0020, +}; + +inline const char* +WifiAuthenticationTypeToString(const WifiAuthenticationType auth_type) +{ + static const char* strs[] = { + "unknown", + "open", + "wpa-psk", + "shared", + "wpa", + "wpa2", + "wpa2-psk" + }; + + return strs[unsigned(auth_type)]; +} + +typedef std::array wifi_80211_mac; + +#define WIFI_80211_MAC_FMT "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx" +#define STR_2_WIFI80211_MAC(a) &a[0], &a[1], &a[2], &a[3], &a[4], &a[5] + +static const wifi_80211_mac wifi_80211_any_address{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const wifi_80211_mac wifi_80211_broadcast_address{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static inline bool +is_wifi80211_broadcast_address(const wifi_80211_mac& mac) +{ + return mac == wifi_80211_broadcast_address; +} + +#endif //__WIFI_80211_HPP__ diff --git a/include/wifi-telemetry/wifi/wifi_80211_format_mac.hpp b/include/wifi-telemetry/wifi/wifi_80211_format_mac.hpp new file mode 100644 index 0000000..e82942f --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_80211_format_mac.hpp @@ -0,0 +1,123 @@ + +#ifndef __WIFI_FORMAT_80211_MAC_HPP__ +#define __WIFI_FORMAT_80211_MAC_HPP__ + +#include +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" + +class put_dot11mac +{ +public: + put_dot11mac( + const wifi_80211_mac& mac, + const char separator = ':') : + m_mac(mac), + m_separator(separator) + {} + + std::ostream& + operator()( + std::ostream& out) + { + out << std::setbase(16) + << std::setw(2) << std::setfill('0') << short(m_mac[0]) << m_separator + << std::setw(2) << std::setfill('0') << short(m_mac[1]) << m_separator + << std::setw(2) << std::setfill('0') << short(m_mac[2]) << m_separator + << std::setw(2) << std::setfill('0') << short(m_mac[3]) << m_separator + << std::setw(2) << std::setfill('0') << short(m_mac[4]) << m_separator + << std::setw(2) << std::setfill('0') << short(m_mac[5]) + << std::setbase(0); + + return out; + } + +private: + const wifi_80211_mac& m_mac; + const char m_separator; +}; + +inline std::ostream& +operator<<( + std::ostream& out, + put_dot11mac manipulator) +{ + return manipulator(out); +} + +class get_dot11mac +{ +public: + get_dot11mac( + wifi_80211_mac& mac, + const char separator = ':') : + m_mac(mac), + m_separator(separator) + {} + + std::istream& + operator()( + std::istream& in) + { + static constexpr std::size_t c_NumOctets = std::tuple_size::type>::value; + static constexpr std::size_t c_NumSeparators = c_NumOctets - 1; + + uint32_t mac[c_NumOctets]; + char separators[c_NumSeparators]; + + in >> std::setbase(16) + >> mac[0] >> separators[0] + >> mac[1] >> separators[1] + >> mac[2] >> separators[2] + >> mac[3] >> separators[3] + >> mac[4] >> separators[4] + >> mac[5] + >> std::setbase(0); + + for (std::size_t i = 0; i < c_NumOctets; i++) { + if (i < c_NumSeparators && separators[i] != m_separator) { + in.setstate(std::ios_base::failbit); + break; + } + + m_mac[i] = uint8_t(mac[i] & 0x000000FF); + } + + return in; + } + +private: + wifi_80211_mac& m_mac; + const char m_separator; +}; + +inline std::istream& +operator>>( + std::istream& in, + get_dot11mac manipulator) +{ + return manipulator(in); +} + +inline std::string +wifi_80211_mac_to_string(const wifi_80211_mac& mac, const char separator = ':') +{ + std::ostringstream ss; + ss << put_dot11mac(mac, separator); + return ss.str(); +} + +inline wifi_80211_mac +wifi_80211_mac_from_string(const std::string& mac_str, const char separator = ':') +{ + std::istringstream ss(mac_str); + wifi_80211_mac mac; + ss >> get_dot11mac(mac, separator); + + return mac; +} + +#endif //__WIFI_FORMAT_80211_MAC_HPP__ diff --git a/include/wifi-telemetry/wifi/wifi_common.hpp b/include/wifi-telemetry/wifi/wifi_common.hpp new file mode 100644 index 0000000..4f337a5 --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_common.hpp @@ -0,0 +1,11 @@ + +#ifndef __WIFI_COMMON_HPP__ +#define __WIFI_COMMON_HPP__ + +enum class WifiOperationalMode +{ + Station, + AccessPoint, +}; + +#endif //__WIFI_COMMON_HPP__ diff --git a/include/wifi-telemetry/wifi/wifi_dpp.hpp b/include/wifi-telemetry/wifi/wifi_dpp.hpp new file mode 100644 index 0000000..e270624 --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_dpp.hpp @@ -0,0 +1,102 @@ + +#ifndef __WIFI_DPP_HPP__ +#define __WIFI_DPP_HPP__ + +enum class WifiDppRole +{ + Initiator, + Responder +}; + +inline const char* +WifiDppRoleToString(const WifiDppRole role) +{ + static const char* strs[] = { + "initiator", + "responder" + }; + + return strs[unsigned(role)]; +} + +enum class WifiDppDeviceRole +{ + Enrollee, + Configurator +}; + +inline const char* +WifiDppDeviceRoleToString(const WifiDppDeviceRole role) +{ + static const char* strs[] = { + "enrollee", + "configurator" + }; + + return strs[unsigned(role)]; +} + +enum class WifiDppExchangeEnrolleeState +{ + Bootstrapping, + Authenticated, + Provisioning, + Provisioned, + Terminated +}; + +inline const char* +WifiDppExchangeEnrolleeStateToString(const WifiDppExchangeEnrolleeState state) +{ + static const char* strs[] = { + "bootstrapping", + "authenticated", + "provisioning", + "provisioned", + "terminated" + }; + + return strs[unsigned(state)]; +} + +enum class WifiDppExchangeConfiguratorState +{ + Bootstrapping, + Authenticated, + Finished, + Terminated +}; + +inline const char* +WifiDppExchangeConfiguratorStateToString(const WifiDppExchangeConfiguratorState state) +{ + static const char* strs[] = { + "bootstrapping", + "authenticated", + "finished", + "terminated" + }; + + return strs[unsigned(state)]; +} + +enum class WifiDppFailureType +{ + None, + Unspecified, + AuthenticationTimeout, +}; + +inline const char* +WifiDppFailureTypeToString(const WifiDppFailureType type) +{ + static const char* strs[] = { + "none", + "unspecified", + "authentication-timeout", + }; + + return strs[unsigned(type)]; +} + +#endif //__WIFI_DPP_HPP__ diff --git a/include/wifi-telemetry/wifi/wifi_dpp_exchange.hpp b/include/wifi-telemetry/wifi/wifi_dpp_exchange.hpp new file mode 100644 index 0000000..2bee855 --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_dpp_exchange.hpp @@ -0,0 +1,80 @@ + +#ifndef __WIFI_DPP_EXCHANGE_HPP__ +#define __WIFI_DPP_EXCHANGE_HPP__ + +#include +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wifi/wifi_common.hpp" +#include "wifi-telemetry/wifi/wifi_dpp.hpp" + +struct WifiDppExchange +{ + WifiDppExchange(WifiDppDeviceRole device_role_, WifiDppRole role_) : + WifiDppExchange(device_role_, role_, std::chrono::system_clock::now()) + {} + + WifiDppExchange(WifiDppDeviceRole device_role_, WifiDppRole role_, std::chrono::time_point timestamp_start_) : + device_role(device_role_), + role(role_), + timestamp_start(timestamp_start_) + {} + + virtual ~WifiDppExchange(void) = default; + + void + start(void) + { + if (!timestamp_start) + timestamp_start = std::chrono::system_clock::now(); + } + + void + stop(void) + { + if (timestamp_start && !timestamp_end) { + timestamp_end = std::chrono::system_clock::now(); + duration = std::chrono::duration_cast(timestamp_end.value() - timestamp_start.value()); + } + } + + WifiDppDeviceRole device_role; + WifiDppRole role; + WifiDppFailureType failure_type = WifiDppFailureType::None; + std::optional failure_details; + std::optional> timestamp_start; + std::optional> timestamp_end; + std::optional duration; +}; + +struct WifiDppExchangeEnrollee : + public WifiDppExchange +{ + WifiDppExchangeEnrollee(WifiDppRole role_) : + WifiDppExchange(WifiDppDeviceRole::Enrollee, role_) + {} + + WifiDppExchangeEnrolleeState state = WifiDppExchangeEnrolleeState::Bootstrapping; + std::unordered_set chirp_frequencies; +}; + +struct WifiDppExchangeConfigurator : + public WifiDppExchange +{ + WifiDppExchangeConfigurator(int32_t peer_id_, const wifi_80211_mac peer_bssid_, uint32_t frequency_, WifiDppRole role_) : + WifiDppExchange(WifiDppDeviceRole::Configurator, role_), + peer_id(peer_id_), + peer_bssid(peer_bssid_), + frequency(frequency_) + {} + + WifiDppExchangeConfiguratorState state = WifiDppExchangeConfiguratorState::Bootstrapping; + int32_t peer_id; + wifi_80211_mac peer_bssid; + uint32_t frequency; +}; + +#endif //__WIFI_DPP_EXCHANGE_HPP__ diff --git a/include/wifi-telemetry/wifi/wifi_telemetry_80211.hpp b/include/wifi-telemetry/wifi/wifi_telemetry_80211.hpp new file mode 100644 index 0000000..1422bda --- /dev/null +++ b/include/wifi-telemetry/wifi/wifi_telemetry_80211.hpp @@ -0,0 +1,14 @@ + +#ifndef __WIFI_TELEMETRY_80211_HPP__ +#define __WIFI_TELEMETRY_80211_HPP__ + +enum class WifiConnectionAttemptResult +{ + Succeeded = 0, + Unspecified = 1, + AuthenticationRejected = 2, + AssociationRejected = 3, + NetworkNotFound = 4, +}; + +#endif // __WIFI_TELEMETRY_80211_HPP__ diff --git a/include/wifi-telemetry/wifi_interface_configuration.hpp b/include/wifi-telemetry/wifi_interface_configuration.hpp new file mode 100644 index 0000000..a7320ec --- /dev/null +++ b/include/wifi-telemetry/wifi_interface_configuration.hpp @@ -0,0 +1,24 @@ + +#ifndef __WIFI_INTERFACE_CONFIGURATION_HPP__ +#define __WIFI_INTERFACE_CONFIGURATION_HPP__ + +#include + +#include "wifi-telemetry/wifi/wifi_common.hpp" + +struct WifiInterfaceConfiguration +{ + WifiInterfaceConfiguration(const std::string& name_, const WifiOperationalMode operational_mode_) : + name(name_), + operational_mode(operational_mode_) + {} + + WifiInterfaceConfiguration(const std::string& name_) : + WifiInterfaceConfiguration(name_, WifiOperationalMode::Station) + {} + + std::string name; + WifiOperationalMode operational_mode; +}; + +#endif //__WIFI_INTERFACE_CONFIGURATION_HPP__ diff --git a/include/wifi-telemetry/wifi_telemetry_monitor.hpp b/include/wifi-telemetry/wifi_telemetry_monitor.hpp new file mode 100644 index 0000000..1298251 --- /dev/null +++ b/include/wifi-telemetry/wifi_telemetry_monitor.hpp @@ -0,0 +1,103 @@ + +#ifndef __WIFI_TELEMETRY_MONITOR_HPP__ +#define __WIFI_TELEMETRY_MONITOR_HPP__ + +#include +#include +#include + +#include "wifi-telemetry/wifi_interface_configuration.hpp" +#include "wifi-telemetry/wifi_telemetry_source_instance.hpp" +#include "wifi-telemetry/wifi_telemetry_source.hpp" +#include "wifi-telemetry/wifi/wifi_common.hpp" + +class __attribute__ ((visibility ("default"))) WifiTelemetryMonitor +{ +public: + /** + * @brief Construct a new Wifi Telemetry Monitor object. + */ + WifiTelemetryMonitor(void); + ~WifiTelemetryMonitor(void); + + /** + * @brief Adds a new telemetry source. + * + * @tparam TSource The type of the telemetry source. Must be a type deriving from WifiTelemetrySource. + * @tparam TSourceArgs The parameter pack type for T's constructor arguments. + * @param interface The interface configuration of the source (if any). + * @param activation_args The arguments needed to activate the telemetry source, or nullptr if none. + * @param args The arguments to forward to the constructor of type TSource. + * @return std::shared_ptr + */ + template < + typename TSource, + typename ...TSourceArgs, + typename std::enable_if::value>* = nullptr + > + std::shared_ptr + add_source(const std::optional& interface, const std::shared_ptr activation_args, TSourceArgs... args) + { + std::shared_ptr source = std::make_shared(interface, args...); + add_source_impl(source, activation_args); + return source; + } + + /** + * @brief Adds a new telemetry source. This version assumes no activation args are required. + * + * @tparam TSource The type of the telemetry source. Must be a type deriving from WifiTelemetrySource. + * @tparam TSourceArgs The parameter pack type for T's constructor arguments. + * @param interface The interface configuration of the source (if any). + * @param args The arguments to forward to the constructor of type TSource. + * @return std::shared_ptr + */ + template < + typename TSource, + typename ...TSourceArgs, + typename std::enable_if::value>* = nullptr + > + std::shared_ptr + add_source(const std::optional& interface, TSourceArgs&&... args) + { + std::shared_ptr source = std::make_shared(interface, args...); + add_source_impl(source); + return source; + } + + /** + * @brief Removes a telemetry source from the monitor. + * + * @param source A previously added telemetry source. + */ + void + remove_source(const std::shared_ptr source); + + /** + * @brief Starts monitoring the configured interface. + */ + void + start(void); + + /** + * @brief Stops monitoring the configured interface. + */ + void + stop(void); + +private: + void + add_source_impl(const std::shared_ptr source, const std::shared_ptr activation_args = nullptr); + + std::size_t + activate_sources(void); + + void + deactivate_sources(void); + +protected: + bool m_activated = false; + std::vector> m_sources; +}; + +#endif //__WIFI_TELEMETRY_MONITOR_HPP__ diff --git a/include/wifi-telemetry/wifi_telemetry_source.hpp b/include/wifi-telemetry/wifi_telemetry_source.hpp new file mode 100644 index 0000000..cdb0828 --- /dev/null +++ b/include/wifi-telemetry/wifi_telemetry_source.hpp @@ -0,0 +1,44 @@ + +#ifndef __WIFI_TELEMETRY_SOURCE_HPP__ +#define __WIFI_TELEMETRY_SOURCE_HPP__ + +#include +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_common.hpp" +#include "wifi-telemetry/wifi_interface_configuration.hpp" + +struct WifiTelemetrySourceActivationArgs +{ + ~WifiTelemetrySourceActivationArgs(void) = default; +}; + +class WifiTelemetrySource +{ +protected: + WifiTelemetrySource(const std::optional& interface); + WifiTelemetrySource(const std::optional& interface, const std::string& name); + +public: + virtual ~WifiTelemetrySource(void); + + virtual int + activate(const std::shared_ptr args); + + virtual void + deactivate(void); + + virtual const std::string_view + name() const; + + const std::optional& + interface(void) const; + +protected: + std::string m_name = "unnamed"; + const std::optional m_interface; +}; + +#endif //__WIFI_TELEMETRY_SOURCE_HPP__ diff --git a/include/wifi-telemetry/wifi_telemetry_source_instance.hpp b/include/wifi-telemetry/wifi_telemetry_source_instance.hpp new file mode 100644 index 0000000..1a3e36e --- /dev/null +++ b/include/wifi-telemetry/wifi_telemetry_source_instance.hpp @@ -0,0 +1,23 @@ + +#ifndef __WIFI_TELEMETRY_SOURCE_INSTANCE_HPP__ +#define __WIFI_TELEMETRY_SOURCE_INSTANCE_HPP__ + +#include + +#include "wifi-telemetry/wifi_telemetry_source.hpp" + +struct WifiTelemetrySourceInstance +{ +public: + WifiTelemetrySourceInstance(std::shared_ptr source_, const std::shared_ptr args_) : + source(std::move(source_)), + args(args_) + {} + + virtual ~WifiTelemetrySourceInstance(void) = default; + + std::shared_ptr source; + std::shared_ptr args; +}; + +#endif // __WIFI_TELEMETRY_SOURCE_INSTANCE_HPP__ diff --git a/include/wifi-telemetry/wifi_telemetry_source_wpa.hpp b/include/wifi-telemetry/wifi_telemetry_source_wpa.hpp new file mode 100644 index 0000000..ed3282b --- /dev/null +++ b/include/wifi-telemetry/wifi_telemetry_source_wpa.hpp @@ -0,0 +1,110 @@ + +#ifndef __WIFI_TELEMETRY_SOURCE_WPA_HPP__ +#define __WIFI_TELEMETRY_SOURCE_WPA_HPP__ + +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_common.hpp" +#include "wifi-telemetry/wifi/wifi_dpp_exchange.hpp" +#include "wifi-telemetry/wifi_telemetry_source.hpp" +#include "wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp" +#include "wifi-telemetry/wpa/wpa_controller.hpp" + +class __attribute__((visibility("default"))) WifiTelemetrySourceWpa : + public WifiTelemetrySource, + public std::enable_shared_from_this, + public WpaEventHandler +{ +public: + WifiTelemetrySourceWpa(const std::optional& interface); + ~WifiTelemetrySourceWpa(void); + + int + activate(const std::shared_ptr args) override; + + void + deactivate(void) override; + +private: + bool + trace_station(void) const; + + bool + trace_ap(void) const; + + bool + trace_dpp(void) const; + + bool + trace_dpp_enrollee(void) const; + + bool + trace_dpp_configurator(void) const; + + // WpaEventHandler + void + on_connected(const WpaEventArgs& args) override; + + void + on_disconnected(const WpaEventArgs& args) override; + + void + on_association_rejected(const WpaEventArgs& args) override; + + void + on_authentication_rejected(const WpaEventArgs& args) override; + + void + on_network_not_found(const WpaEventArgs& args) override; + + void + on_dpp_chirp_received(const WpaEventArgs& args) override; + + void + on_dpp_frame_transmit_status(const WpaEventArgs& args) override; + + void + on_dpp_authentication_init_failure(const WpaEventArgs& args) override; + + void + on_dpp_authentication_succeeded(const WpaEventArgs& args) override; + + void + on_dpp_configuration_received(const WpaEventArgs& args) override; + + void + on_dpp_configuration_sent(const WpaEventArgs& args) override; + + void + on_dpp_failure(const WpaEventArgs& args) override; + +private: + template< + typename T, + typename std::enable_if::value>* = nullptr> + std::shared_ptr + resolve_dpp_exchange(void) + { + return std::dynamic_pointer_cast(m_dpp_exchange); + } + + bool + is_dpp_exchange_in_progress(void) const; + + void + complete_connection_attempt(const WifiTelemetryInputStationConnectionAttempt& input); + + void + complete_dpp_exchange_enrollee(std::shared_ptr& enrollee); + + void + complete_dpp_exchange_configurator(std::shared_ptr& configurator); + + std::shared_ptr m_dpp_exchange = nullptr; + std::shared_ptr m_controller; + WpaEventToken m_event_token; +}; + +#endif //__WIFI_TELEMETRY_SOURCE_WPA_HPP__ diff --git a/include/wifi-telemetry/wifi_telemetry_source_ztpd.hpp b/include/wifi-telemetry/wifi_telemetry_source_ztpd.hpp new file mode 100644 index 0000000..b3ef51c --- /dev/null +++ b/include/wifi-telemetry/wifi_telemetry_source_ztpd.hpp @@ -0,0 +1,34 @@ + +#ifndef __WIFI_TELEMETRY_SOURCE_ZTPD_HPP__ +#define __WIFI_TELEMETRY_SOURCE_ZTPD_HPP__ + +#include +#include + +#include + +#include "wifi-telemetry/wifi_telemetry_source.hpp" + +class __attribute__((visibility("default"))) WifiTelemetrySourceZtpd : + public WifiTelemetrySource, + public sdbus::ProxyInterfaces +{ +public: + WifiTelemetrySourceZtpd(const std::optional& interface_config); + ~WifiTelemetrySourceZtpd(void); + + int + activate(const std::shared_ptr args) override; + + void + deactivate(void) override; + +private: + // sdbus::Properties_proxy + void + onPropertiesChanged(const std::string& interfaceName, + const std::map& changedProperties, + const std::vector& invalidatedProperties) override; +}; + +#endif //__WIFI_TELEMETRY_SOURCE_ZTPD_HPP__ diff --git a/include/wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp b/include/wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp new file mode 100644 index 0000000..539d640 --- /dev/null +++ b/include/wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp @@ -0,0 +1,106 @@ + +#ifndef __WIFI_TELEMETRY_INPUT_STATION_CONNECTION_ATTEMPT_HPP__ +#define __WIFI_TELEMETRY_INPUT_STATION_CONNECTION_ATTEMPT_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wifi/wifi_telemetry_80211.hpp" +#include "wifi-telemetry/wpa/wpa_command_signal_poll.hpp" +#include "wifi-telemetry/wpa/wpa_command_status.hpp" + +/** + * @brief Helper class that sanitizes the connection_attempt telemetry event + * inputs. + * + * Some of the input data is optional; this class checks such inputs for their + * existence/validity, and if missing/invalid, assigns defaults or appropriate + * values that the telemetry system understands as being invalid. + */ +class WifiTelemetryInputStationConnectionAttempt +{ +public: + /** + * @brief Factory methods for creating instances of the correspoding + * result-type. + * + * These ensure that invalid objects are impossible to create. For example, + * if the connection attempt result was 'AssociationRejected' ALL required + * parameters must be specified for the instance to be created since no + * public constructor exists that allows construction without the required + * arguments. + */ + + static WifiTelemetryInputStationConnectionAttempt + unspecified(void); + + static WifiTelemetryInputStationConnectionAttempt + succeeded(const wifi_80211_mac& bssid, const std::shared_ptr signal_poll, const std::shared_ptr status); + + static WifiTelemetryInputStationConnectionAttempt + association_rejected(const std::optional& bssid, const WifiStatusCode status_code); + + static WifiTelemetryInputStationConnectionAttempt + authentication_rejected(const wifi_80211_mac& bssid, const WifiStatusCode status_code, const WifiAuthenticationType authentication_type); + + static WifiTelemetryInputStationConnectionAttempt + network_not_found(void); + + uint32_t + result(void) const; + + const char* + bssid(void) const; + + const char* + ssid(void) const; + + int32_t + noise(void) const; + + int32_t + rssi(void) const; + + int32_t + link_speed(void) const; + + uint32_t + frequency(void) const; + + uint32_t + wifi_generation(void) const; + + const char* + key_mgmt(void) const; + + const char* + pairwise_cipher(void) const; + + const char* + group_cipher(void) const; + + uint16_t + status_code(void) const; + + const char* + authentication_type(void) const; + +private: + WifiTelemetryInputStationConnectionAttempt( + const WifiConnectionAttemptResult result, + const std::optional bssid, + const std::shared_ptr signal_poll, + const std::shared_ptr status, + const WifiStatusCode status_code, + const WifiAuthenticationType authentication_type); + + const WifiConnectionAttemptResult m_result; + const std::string m_bssid; + const std::shared_ptr m_signal_poll; + const std::shared_ptr m_status; + const WifiStatusCode m_status_code; + const std::string m_authentication_type; +}; + +#endif //__WIFI_TELEMETRY_INPUT_STATION_CONNECTION_ATTEMPT_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command.hpp b/include/wifi-telemetry/wpa/wpa_command.hpp new file mode 100644 index 0000000..3216402 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command.hpp @@ -0,0 +1,26 @@ + +#ifndef __WPA_COMMAND_HPP__ +#define __WPA_COMMAND_HPP__ + +#include +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_command_response.hpp" +#include "wifi-telemetry/wpa/wpa_command_response_parser.hpp" + +struct WpaCommand : private WpaCommandResponseParserFactory +{ + WpaCommand(const std::string& name); + virtual ~WpaCommand() = default; + + std::shared_ptr + parse_response(const std::string_view payload) const; + + const std::string name; + std::string data; +}; + +#endif //__WPA_COMMAND_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_bss.hpp b/include/wifi-telemetry/wpa/wpa_command_bss.hpp new file mode 100644 index 0000000..be78046 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_bss.hpp @@ -0,0 +1,54 @@ + +#ifndef __WPA_COMMAND_BSS_HPP__ +#define __WPA_COMMAND_BSS_HPP__ + +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_command.hpp" +#include "wifi-telemetry/wpa/wpa_command_response_parser.hpp" + +struct WpaCommandBssResponseParser : public WpaCommandResponseParser +{ + WpaCommandBssResponseParser(const std::string_view payload); + + std::shared_ptr + parse_payload(void) const override; +}; + +struct WpaCommandBssResponse : public WpaCommandResponse +{ + WpaCommandBssResponse(); + + wifi_80211_mac bssid; + uint32_t id; + uint32_t capabilities; + uint32_t update_index; + uint64_t tsf; + int32_t frequency; + int32_t beacon_int; + int32_t quality; + int32_t snr; + int32_t estimated_throughput; + int32_t age; + std::string flags; + std::string ssid; + std::vector ie; + std::vector beacon_ie; +}; + +struct WpaCommandBss : public WpaCommand +{ + WpaCommandBss(std::string bssid_); + WpaCommandBss(const wifi_80211_mac& bssid_); + + std::string bssid; + +private: + std::unique_ptr + create_response_parser(const std::string_view payload) const override; +}; + +#endif // __WPA_COMMAND_BSS_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_key_value_pair.hpp b/include/wifi-telemetry/wpa/wpa_command_key_value_pair.hpp new file mode 100644 index 0000000..2617338 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_key_value_pair.hpp @@ -0,0 +1,32 @@ + +#ifndef __WPA_COMMAND_KEY_VALUE_PAIR_HPP__ +#define __WPA_COMMAND_KEY_VALUE_PAIR_HPP__ + +#include +#include +#include + +struct WpaCommandKeyValuePair +{ + WpaCommandKeyValuePair(const char* key_, bool is_required); + + const char* + operator()(void) const; + + bool + resolve(const std::string_view input); + + const char* key; + const std::size_t key_length; + std::optional value; + std::size_t value_pos; + bool is_required; +}; + +enum +{ + WpaValueRequired = true, + WpaValueOptional = false +}; + +#endif //__WPA_COMMAND_KEY_VALUE_PAIR_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_response.hpp b/include/wifi-telemetry/wpa/wpa_command_response.hpp new file mode 100644 index 0000000..8f38686 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_response.hpp @@ -0,0 +1,24 @@ + +#ifndef __WPA_COMMAND_RESPONSE_HPP__ +#define __WPA_COMMAND_RESPONSE_HPP__ + +#include +#include +#include + +struct WpaCommandResponse +{ + virtual ~WpaCommandResponse() = default; +}; + +/** + * @brief Used to send+receive unstructured commands+responses. + */ +struct WpaCommandResponseGeneric : public WpaCommandResponse +{ + WpaCommandResponseGeneric(const std::string payload); + + std::string payload; +}; + +#endif //__WPA_COMMAND_RESPONSE_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_response_parser.hpp b/include/wifi-telemetry/wpa/wpa_command_response_parser.hpp new file mode 100644 index 0000000..0737051 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_response_parser.hpp @@ -0,0 +1,42 @@ + +#ifndef __WPA_COMMAND_RESPONSE_PARSER_HPP__ +#define __WPA_COMMAND_RESPONSE_PARSER_HPP__ + +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_command_key_value_pair.hpp" +#include "wifi-telemetry/wpa/wpa_command_response.hpp" + +struct WpaCommandResponseParser +{ + WpaCommandResponseParser(const std::string& command_name, const std::string_view payload, const std::initializer_list properties); + virtual ~WpaCommandResponseParser() = default; + + virtual std::shared_ptr + parse(void); + + const std::string command_name; + const std::string_view payload; + +protected: + virtual std::shared_ptr + parse_payload(void) const = 0; + + bool + resolve_properties(void); + +protected: + std::vector properties; + std::optional properties_resolved_result = std::nullopt; +}; + +struct WpaCommandResponseParserFactory +{ + virtual std::unique_ptr + create_response_parser(const std::string_view payload) const = 0; +}; + +#endif //__WPA_COMMAND_RESPONSE_PARSER_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_signal_poll.hpp b/include/wifi-telemetry/wpa/wpa_command_signal_poll.hpp new file mode 100644 index 0000000..8ba009e --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_signal_poll.hpp @@ -0,0 +1,47 @@ + +#ifndef __WPA_COMMAND_SIGNAL_POLL_HPP__ +#define __WPA_COMMAND_SIGNAL_POLL_HPP__ + +#include +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_command.hpp" +#include "wifi-telemetry/wpa/wpa_command_response.hpp" +#include "wifi-telemetry/wpa/wpa_command_response_parser.hpp" + +struct WpaCommandSignalPollResponseParser : public WpaCommandResponseParser +{ + WpaCommandSignalPollResponseParser(const std::string_view payload); + + std::shared_ptr + parse_payload(void) const override; +}; + +struct WpaCommandSignalPollResponse : public WpaCommandResponse +{ + WpaCommandSignalPollResponse(int32_t noise, int32_t rssi, int32_t link_speed, uint32_t frequency); + + int32_t noise; + int32_t rssi; + int32_t link_speed; + uint32_t frequency; + std::optional channel_width; + std::optional center_frequency_1 = std::nullopt; + std::optional center_frequency_2 = std::nullopt; + std::optional rssi_average = std::nullopt; + std::optional rssi_average_beacon = std::nullopt; +}; + +struct WpaCommandSignalPoll : public WpaCommand +{ + WpaCommandSignalPoll(void); + +private: + std::unique_ptr + create_response_parser(const std::string_view payload) const override; +}; + +#endif // __WPA_COMMAND_SIGNAL_POLL_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_command_status.hpp b/include/wifi-telemetry/wpa/wpa_command_status.hpp new file mode 100644 index 0000000..b2db4ea --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_command_status.hpp @@ -0,0 +1,60 @@ + +#ifndef __WPA_COMMAND_STATUS_HPP__ +#define __WPA_COMMAND_STATUS_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_command.hpp" +#include "wifi-telemetry/wpa/wpa_state.hpp" + +struct WpaCommandStatusResponseParser : public WpaCommandResponseParser +{ + WpaCommandStatusResponseParser(const std::string_view payload); + + std::shared_ptr + parse_payload(void) const override; +}; + +struct WpaCommandStatusResponse : public WpaCommandResponse +{ + WpaCommandStatusResponse(void) = default; + WpaCommandStatusResponse(WpaState state_); + + WpaState state = WpaState::Unknown; + std::optional bssid; + std::optional frequency; + std::optional ssid; + std::optional passphrase; + std::optional id_string; + std::optional mode; + std::optional wifi_generation; + std::optional sae_group; + std::optional ip_address; + std::optional p2p_address; + std::optional address; + std::optional hs20; + std::optional provisioning_sp; + std::optional home_sp; + std::optional sp_type; + std::optional eap_session_id; + std::optional uuid; + std::optional ieee80211ac; + std::optional pairwise_cipher; + std::optional group_cipher; + std::optional key_mgmt; + std::optional pmf; + std::optional mgmt_group_cipher; +}; + +struct WpaCommandStatus : public WpaCommand +{ + WpaCommandStatus(void); + +private: + std::unique_ptr + create_response_parser(const std::string_view payload) const override; +}; + +#endif // __WPA_COMMAND_STATUS_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_commands.hpp b/include/wifi-telemetry/wpa/wpa_commands.hpp new file mode 100644 index 0000000..e754186 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_commands.hpp @@ -0,0 +1,4 @@ + +#include "wifi-telemetry/wpa/wpa_command_bss.hpp" +#include "wifi-telemetry/wpa/wpa_command_signal_poll.hpp" +#include "wifi-telemetry/wpa/wpa_command_status.hpp" diff --git a/include/wifi-telemetry/wpa/wpa_controller.hpp b/include/wifi-telemetry/wpa/wpa_controller.hpp new file mode 100644 index 0000000..648f0cc --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_controller.hpp @@ -0,0 +1,160 @@ + +#ifndef __WPA_CONTROLLER_HPP__ +#define __WPA_CONTROLLER_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_command.hpp" +#include "wifi-telemetry/wpa/wpa_command_response.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" +#include "wifi-telemetry/wpa/wpa_event_handler.hpp" +#include "wifi-telemetry/wpa/wpa_interface_info.hpp" + +typedef uint32_t WpaEventToken; + +class WpaController +{ +public: + WpaController(const std::string_view interface, WpaType type); + WpaController(const std::string_view interface, WpaType type, const std::string_view control_socket_path); + ~WpaController(void); + + /** + * @brief Registers a handler for wpa events. + * + * @param handler The handler to invoke in response to events. + * @return WpaEventToken A registration token to be used to unregister the handler. + */ + WpaEventToken + register_for_events(std::weak_ptr handler); + + /** + * @brief Unregisters an event handler for wpa events. + * + * @param token The registration token previously obtains from register_for_events. + */ + void + unregister_for_events(WpaEventToken token); + + /** + * @brief + * + * @param command + * @return std::shared_ptr + */ + std::shared_ptr + send_command(const WpaCommand& command); + + /** + * @brief Syntactic sugar for returning a specific derived type for the response. + * + * @tparam TResponse + * @param command + * @return std::shared_ptr + */ + template < + typename TResponse, + typename std::enable_if::value>* = nullptr> + std::shared_ptr + send_command(const WpaCommand& command) + { + return std::dynamic_pointer_cast(send_command(command)); + } + + /** + * @brief Returns the type of controller + * + * @return const WpaType The type of controller. + */ + WpaType + type(void) const + { + return m_type; + } + +private: + /** + * @brief Retrieves the next available message from the control socket and + * processes it. + */ + void + process_wpa_event_next(void); + + /** + * @brief Waits for and processes all wpa control events. + */ + void + process_wpa_events(void); + + /** + * @brief Get the command control socket object. + * + * @return struct wpa_ctrl* + */ + struct wpa_ctrl* + get_command_control_socket(void); + +private: + const WpaType m_type; + std::string m_interface; + std::filesystem::path m_ctrl_socket_path; + std::thread m_ctrl_event_thread; + bool m_terminate_pending = false; + int m_ctrl_event_fd_stop = -1; + struct wpa_ctrl* m_ctrl_event = nullptr; + struct wpa_ctrl* m_ctrl_command = nullptr; + std::shared_mutex m_ctrl_command_gate; + + uint32_t m_event_token_next = 0; + std::mutex m_event_subscribers_gate; + std::unordered_map> m_event_subscribers; + +private: + /** + * @brief Maximum wpas message size, in bytes. The wpa_cli tool uses this + * as an upper bound so is used similarly here. + */ + static constexpr std::size_t c_wpa_event_size_max = 4096; + + /** + * @brief Default control socket pasth for wpa_supplicant. + */ + static constexpr std::string_view m_ctrl_socket_path_default_wpas = std::string_view("/var/run/wpa_supplicant"); + + /** + * @brief Default control socket pasth for hostapd. + */ + static constexpr std::string_view m_ctrl_socket_path_default_hostapd = std::string_view("/var/run/hostapd"); + + /** + * @brief Determines default control socket path for each wpa service type. + * + * @param type The type of wpa service to determine the default control socket for. + * @return constexpr std::string_view A string describing the default control sock path. + */ + static constexpr std::string_view + default_control_socket_path(WpaType type) + { + switch (type) { + case WpaType::Hostapd: + return m_ctrl_socket_path_default_hostapd; + case WpaType::WpaSupplicant: + return m_ctrl_socket_path_default_wpas; + default: + return ""; + } + } +}; + +#endif //__WPA_CONTROLLER_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event.hpp b/include/wifi-telemetry/wpa/wpa_event.hpp new file mode 100644 index 0000000..8b75c97 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event.hpp @@ -0,0 +1,29 @@ + +#ifndef __WPA_EVENT_HPP__ +#define __WPA_EVENT_HPP__ + +enum WpaEventType +{ + Connected, + Disconnected, + NetworkNotFound, + AssociationRejected, + AuthenticationRejected, + DppAuthenticationInitFailure, + DppAuthenticationSucceeded, + DppConfigurationReceived, + DppConfigurationSent, + DppFailure, + DppFrameTransmitStatus, + DppChirpReceived +}; + +struct WpaEvent +{ + WpaEvent(WpaEventType type); + + WpaEventType type; + virtual ~WpaEvent() = default; +}; + +#endif //__WPA_EVENT_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_association_rejected.hpp b/include/wifi-telemetry/wpa/wpa_event_association_rejected.hpp new file mode 100644 index 0000000..b793a35 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_association_rejected.hpp @@ -0,0 +1,19 @@ + +#ifndef __WPA_EVENT_ASSOCIATION_REJECTED_HPP__ +#define __WPA_EVENT_ASSOCIATION_REJECTED_HPP__ + +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventAssociationRejected : public WpaEvent +{ + WpaEventAssociationRejected(WifiStatusCode status_code); + WpaEventAssociationRejected(WifiStatusCode status_code, const wifi_80211_mac& bssid); + + const WifiStatusCode status_code; + const std::optional bssid = std::nullopt; +}; + +#endif // __WPA_EVENT_ASSOCIATION_REJECTED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_authentication_rejected.hpp b/include/wifi-telemetry/wpa/wpa_event_authentication_rejected.hpp new file mode 100644 index 0000000..af06694 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_authentication_rejected.hpp @@ -0,0 +1,17 @@ + +#ifndef __WPA_EVENT_AUTHENTICATION_REJECTED_HPP__ +#define __WPA_EVENT_AUTHENTICATION_REJECTED_HPP__ + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventAuthenticationRejected : public WpaEvent +{ + WpaEventAuthenticationRejected(const wifi_80211_mac& bssid, WifiStatusCode status_code, WifiAuthenticationType authentication_type); + + const wifi_80211_mac bssid; + const WifiStatusCode status_code; + const WifiAuthenticationType authentication_type; +}; + +#endif // __WPA_EVENT_AUTHENTICATION_REJECTED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_connected.hpp b/include/wifi-telemetry/wpa/wpa_event_connected.hpp new file mode 100644 index 0000000..d83f8ad --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_connected.hpp @@ -0,0 +1,17 @@ + +#ifndef __WPA_EVENT_CONNECTED_HPP__ +#define __WPA_EVENT_CONNECTED_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventConnected : public WpaEvent +{ + WpaEventConnected(const wifi_80211_mac& bssid); + const wifi_80211_mac bssid; +}; + +#endif // __WPA_EVENT_CONNECTED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_disconnected.hpp b/include/wifi-telemetry/wpa/wpa_event_disconnected.hpp new file mode 100644 index 0000000..f9c3710 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_disconnected.hpp @@ -0,0 +1,20 @@ + +#ifndef __WPA_EVENT_DISCONNECTED_HPP__ +#define __WPA_EVENT_DISCONNECTED_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDisconnected : public WpaEvent +{ + WpaEventDisconnected(const wifi_80211_mac& bssid_, WifiDeauthenticationReasonCode reason_code_, bool locally_generated); + + const wifi_80211_mac bssid; + const WifiDeauthenticationReasonCode reason_code; + bool locally_generated = false; +}; + +#endif // __WPA_EVENT_DISCONNECTED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_auth_init_failure.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_auth_init_failure.hpp new file mode 100644 index 0000000..ef46361 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_auth_init_failure.hpp @@ -0,0 +1,14 @@ + +#ifndef __WPA_EVENT_DPP_AUTH_INIT_FAILURE_HPP__ +#define __WPA_EVENT_DPP_AUTH_INIT_FAILURE_HPP__ + +#include + +#include "wifi-telemetry/wpa/wpa_event_dpp_failure.hpp" + +struct WpaEventDppAuthenticationInitFailure : public WpaEventDppFailure +{ + WpaEventDppAuthenticationInitFailure(void); +}; + +#endif // __WPA_EVENT_DPP_AUTH_INIT_FAILURE_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_auth_success.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_auth_success.hpp new file mode 100644 index 0000000..1f1fa25 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_auth_success.hpp @@ -0,0 +1,13 @@ + +#ifndef __WPA_EVENT_DPP_AUTH_SUCCESS_HPP__ +#define __WPA_EVENT_DPP_AUTH_SUCCESS_HPP__ + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppAuthenticationSucceeded : public WpaEvent +{ + WpaEventDppAuthenticationSucceeded(int initiator); + int initiator; +}; + +#endif // __WPA_EVENT_DPP_AUTH_SUCCESS_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_chirp_received.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_chirp_received.hpp new file mode 100644 index 0000000..5861f3f --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_chirp_received.hpp @@ -0,0 +1,19 @@ + +#ifndef __WPA_EVENT_DPP_CHIRP_RECEIVED_HPP__ +#define __WPA_EVENT_DPP_CHIRP_RECEIVED_HPP__ + +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppChirpReceived : public WpaEvent +{ + WpaEventDppChirpReceived(int32_t id, const wifi_80211_mac& bssid, unsigned int frequency); + + int32_t id; + wifi_80211_mac bssid; + uint32_t frequency; +}; + +#endif // __WPA_EVENT_DPP_CHIRP_RECEIVED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_conf_received.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_conf_received.hpp new file mode 100644 index 0000000..903ab9d --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_conf_received.hpp @@ -0,0 +1,12 @@ + +#ifndef __WPA_EVENT_DPP_CONF_RECEIVED_HPP__ +#define __WPA_EVENT_DPP_CONF_RECEIVED_HPP__ + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppConfigurationReceived : public WpaEvent +{ + WpaEventDppConfigurationReceived(void); +}; + +#endif // __WPA_EVENT_DPP_CONF_RECEIVED_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_conf_sent.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_conf_sent.hpp new file mode 100644 index 0000000..eceb848 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_conf_sent.hpp @@ -0,0 +1,12 @@ + +#ifndef __WPA_EVENT_DPP_CONF_SENT_HPP__ +#define __WPA_EVENT_DPP_CONF_SENT_HPP__ + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppConfigurationSent : public WpaEvent +{ + WpaEventDppConfigurationSent(void); +}; + +#endif // __WPA_EVENT_DPP_CONF_SENT_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_failure.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_failure.hpp new file mode 100644 index 0000000..c917542 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_failure.hpp @@ -0,0 +1,22 @@ + +#ifndef __WPA_EVENT_DPP_FAILURE_HPP__ +#define __WPA_EVENT_DPP_FAILURE_HPP__ + +#include + +#include "wifi-telemetry/wifi/wifi_dpp.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppFailure : public WpaEvent +{ + WpaEventDppFailure(const std::string& failure_details = ""); + WpaEventDppFailure(WifiDppFailureType failure_type, const std::string& failure_details = ""); + + WifiDppFailureType failure_type; + const std::string failure_details; + +protected: + WpaEventDppFailure(WpaEventType type, WifiDppFailureType failure_type, const std::string& failure_details = ""); +}; + +#endif // __WPA_EVENT_DPP_FAILURE_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_dpp_frame_transmit_status.hpp b/include/wifi-telemetry/wpa/wpa_event_dpp_frame_transmit_status.hpp new file mode 100644 index 0000000..ec4b997 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_dpp_frame_transmit_status.hpp @@ -0,0 +1,20 @@ + +#ifndef __WPA_EVENT_DPP_FRAME_TRANSMIT_STATUS_HPP__ +#define __WPA_EVENT_DPP_FRAME_TRANSMIT_STATUS_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211.hpp" +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventDppFrameTransmitStatus : public WpaEvent +{ + WpaEventDppFrameTransmitStatus(const wifi_80211_mac& destination_bssid, unsigned int frequency, const std::string& status); + + wifi_80211_mac destination_bssid; + uint32_t frequency; + std::string status; +}; + +#endif // __WPA_EVENT_DPP_FRAME_TRANSMIT_STATUS_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_handler.hpp b/include/wifi-telemetry/wpa/wpa_event_handler.hpp new file mode 100644 index 0000000..7c02fcb --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_handler.hpp @@ -0,0 +1,68 @@ + +#ifndef __WPA_EVENT_HANDLER_HPP__ +#define __WPA_EVENT_HANDLER_HPP__ + +#include +#include + +#include "wifi-telemetry/wpa/wpa_events.hpp" + +template +struct WpaEventArgs +{ + WpaEventArgs(std::chrono::time_point timestamp_, std::shared_ptr event_) : + timestamp(timestamp_), + event(event_) + {} + + std::chrono::time_point timestamp; + std::shared_ptr event; + + const T& + data() const + { + return *event; + } +}; + +class WpaEventHandler +{ +public: + virtual void + on_connected(const WpaEventArgs& args); + + virtual void + on_disconnected(const WpaEventArgs& args); + + virtual void + on_association_rejected(const WpaEventArgs& args); + + virtual void + on_authentication_rejected(const WpaEventArgs& args); + + virtual void + on_network_not_found(const WpaEventArgs& args); + + virtual void + on_dpp_chirp_received(const WpaEventArgs& args); + + virtual void + on_dpp_frame_transmit_status(const WpaEventArgs& args); + + virtual void + on_dpp_authentication_init_failure(const WpaEventArgs& args); + + virtual void + on_dpp_authentication_succeeded(const WpaEventArgs& args); + + virtual void + on_dpp_configuration_received(const WpaEventArgs& args); + + virtual void + on_dpp_configuration_sent(const WpaEventArgs& args); + + virtual void + on_dpp_failure(const WpaEventArgs& args); +}; + +#endif //__WPA_EVENT_HANDLER_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_event_network_not_found.hpp b/include/wifi-telemetry/wpa/wpa_event_network_not_found.hpp new file mode 100644 index 0000000..a893ae9 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_event_network_not_found.hpp @@ -0,0 +1,12 @@ + +#ifndef __WPA_EVENT_NETWORK_NOT_FOUND_HPP__ +#define __WPA_EVENT_NETWORK_NOT_FOUND_HPP__ + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +struct WpaEventNetworkNotFound : public WpaEvent +{ + WpaEventNetworkNotFound(void); +}; + +#endif // __WPA_EVENT_NETWORK_NOT_FOUND_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_events.hpp b/include/wifi-telemetry/wpa/wpa_events.hpp new file mode 100644 index 0000000..eb28e33 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_events.hpp @@ -0,0 +1,14 @@ + +#include "wifi-telemetry/wpa/wpa_event.hpp" +#include "wifi-telemetry/wpa/wpa_event_association_rejected.hpp" +#include "wifi-telemetry/wpa/wpa_event_authentication_rejected.hpp" +#include "wifi-telemetry/wpa/wpa_event_connected.hpp" +#include "wifi-telemetry/wpa/wpa_event_disconnected.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_auth_init_failure.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_auth_success.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_chirp_received.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_conf_received.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_conf_sent.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_failure.hpp" +#include "wifi-telemetry/wpa/wpa_event_dpp_frame_transmit_status.hpp" +#include "wifi-telemetry/wpa/wpa_event_network_not_found.hpp" diff --git a/include/wifi-telemetry/wpa/wpa_interface_info.hpp b/include/wifi-telemetry/wpa/wpa_interface_info.hpp new file mode 100644 index 0000000..9ff8dd9 --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_interface_info.hpp @@ -0,0 +1,38 @@ + +#ifndef __WPA_INTERFACE_INFO_HPP__ +#define __WPA_INTERFACE_INFO_HPP__ + +#include +#include + +#include "wifi-telemetry/wifi/wifi_common.hpp" + +enum WpaType +{ + Hostapd, + WpaSupplicant +}; + +/** + * @brief Interface information common to all interfaces and modes. + */ +struct WpaInterfaceInfo +{ + std::string name; + std::string address; + + static constexpr WpaType + TypeFromOperationalMode(WifiOperationalMode mode) + { + switch (mode) { + case WifiOperationalMode::Station: + return WpaType::WpaSupplicant; + case WifiOperationalMode::AccessPoint: + return WpaType::Hostapd; + default: + throw new std::invalid_argument("no WpaType for operational mode"); + } + } +}; + +#endif // __WPA_INTERFACE_INFO_HPP__ diff --git a/include/wifi-telemetry/wpa/wpa_state.hpp b/include/wifi-telemetry/wpa/wpa_state.hpp new file mode 100644 index 0000000..74f117e --- /dev/null +++ b/include/wifi-telemetry/wpa/wpa_state.hpp @@ -0,0 +1,25 @@ + +#ifndef __WPA_STATE_HPP__ +#define __WPA_STATE_HPP__ + +#include + +enum class WpaState : uint32_t +{ + Unknown = 0, + Disconnected = 1, + InterfaceDisabled = 2, + Inactive = 3, + Scanning = 4, + Authenticating = 5, + Associating = 6, + Associated = 7, + FourWayHandshake = 8, + GroupHandshake = 9, + Completed = 10 +}; + +WpaState +wpa_state_from_string(const char* state); + +#endif //__WPA_STATE_HPP__ diff --git a/scripts/release-wifi-telemetryd.sh b/scripts/release-wifi-telemetryd.sh new file mode 100755 index 0000000..9351412 --- /dev/null +++ b/scripts/release-wifi-telemetryd.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +set -euf -o pipefail + +TEMPDIR= +DEBUG= +FEATURE=wifi-telemetry +REDIRECT=/dev/null +GIT_REPO_DEFAULT="git@github.com:microsoft/wifi-telemetry.git" +RELEASE_BRANCH_SOURCE_DEFAULT="main" + +function usage() { + echo "$0 -v [-b ] [-r ] [-f] [-d]" + echo + echo " -v " + echo " The numerical version, in semver format, of the release." + echo " Eg. 0.3.5" + echo " -b " + echo " The name of the branch from which release source should be obtained." + echo " (default=${RELEASE_BRANCH_SOURCE_DEFAULT})" + echo " -r " + echo " The git repo url to use." + echo " (default=${GIT_REPO_DEFAULT}" + echo " -f" + echo " Determines whether or not release tags should be forcibly pushed." + echo " Note that this will overwrite any existing tags; use with caution." + echo " (default=disabled)" + echo " -d" + echo " Enable script debugging. This enables script output (stdout+stderr)." + echo +} + +function finish() { + if [ ! -z ${TEMPDIR+x} ]; then + rm -rf ${TEMPDIR} >& ${REDIRECT} + fi +} + +function main() { + local RELEASE_VERSION= + local RELEASE_BRANCH_SOURCE=${RELEASE_BRANCH_SOURCE_DEFAULT} + local FORCE= + local GIT_REPO=${GIT_REPO_DEFAULT} + + while getopts ":v:b:r:fd" opt; do + case ${opt} in + v) + RELEASE_VERSION="${OPTARG}" + ;; + b) + RELEASE_BRANCH_SOURCE="${OPTARG}" + ;; + r) + GIT_REPO="${OPTARG}" + ;; + f) + FORCE=" -f" + ;; + d) + DEBUG=1 + REDIRECT=/dev/tty + ;; + *) + ;; + esac + done + + if [ -z ${RELEASE_VERSION+x} ]; then + echo "missing release version" + usage + exit 1 + fi + + local RELEASE_VERSION_TAG=v${RELEASE_VERSION} + local RELEASE_NAME=${FEATURE}-${RELEASE_VERSION} + local RELEASE_DIR_SOURCE=${FEATURE}-release-${RELEASE_VERSION} + local RELEASE_DIR=${RELEASE_NAME} + local RELEASE_ARCHIVE=${RELEASE_NAME} + + TEMPDIR=$(mktemp -d) + pushd ${TEMPDIR} >& ${REDIRECT} + echo "checking out branch '${RELEASE_BRANCH_SOURCE}' from ${GIT_REPO}" + git clone ${GIT_REPO} --branch ${RELEASE_BRANCH_SOURCE} ${RELEASE_DIR_SOURCE} >& ${REDIRECT} + pushd ${RELEASE_DIR_SOURCE} >& ${REDIRECT} + echo "tagging as ${RELEASE_VERSION_TAG}" + git tag ${RELEASE_VERSION_TAG} ${FORCE} >& ${REDIRECT} + git push origin refs/tags/${RELEASE_VERSION_TAG} ${FORCE} >& ${REDIRECT} + popd >& ${REDIRECT} + + echo "preparing release archive" + git clone --depth 1 --branch ${RELEASE_VERSION_TAG} ${GIT_REPO} ${RELEASE_DIR} >& ${REDIRECT} + pushd ${RELEASE_DIR} >& ${REDIRECT} + rm -rf .vscode .git .gitignore >& ${REDIRECT} + popd >& ${REDIRECT} + popd >& ${REDIRECT} + tar cJf ${PWD}/${RELEASE_NAME}.tar.xz -C ${TEMPDIR} ${RELEASE_DIR} >& ${REDIRECT} + echo "created release archive ${RELEASE_NAME}.tar.xz -> $(sha256sum ${RELEASE_NAME}.tar.xz | awk '{print $1}')" +} + +main "$@" +finish \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..2bd1ddf --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,14 @@ + +install( + EXPORT wifi-telemetry-targets + NAMESPACE wifi-telemetry:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wifi-telemetry +) + +install( + FILES "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/wifi-telemetry-config.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wifi-telemetry +) + +add_subdirectory(daemon) +add_subdirectory(lib) diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt new file mode 100644 index 0000000..d4cfeca --- /dev/null +++ b/src/daemon/CMakeLists.txt @@ -0,0 +1,35 @@ + +add_executable(wifi-telemetryd + "" +) + +set_property( + TARGET wifi-telemetryd + PROPERTY POSITION_INDEPENDENT_CODE TRUE +) + +install( + TARGETS wifi-telemetryd + DESTINATION ${CMAKE_INSTALL_SBINDIR} +) + +target_sources(wifi-telemetryd + PRIVATE + main.cxx +) + +target_include_directories(wifi-telemetryd + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +target_link_libraries(wifi-telemetryd + PRIVATE + wifi-telemetry + INTERFACE + ${LIBSYSTEMD_TARGET} + ${LIBWPA_CLIENT_TARGET} +) + +add_subdirectory(systemd) diff --git a/src/daemon/main.cxx b/src/daemon/main.cxx new file mode 100644 index 0000000..fa99027 --- /dev/null +++ b/src/daemon/main.cxx @@ -0,0 +1,118 @@ + +#include +#include +#include +#include +#include +#include + +#include "wifi-telemetry/wifi_telemetry_monitor.hpp" +#include "wifi-telemetry/wifi_telemetry_source_wpa.hpp" +#include "wifi-telemetry/wifi_telemetry_source_ztpd.hpp" + +static char* +triml(char* s) +{ + while (isspace(*s)) + s++; + return s; +} + +static WifiOperationalMode +parse_wifi_operational_mode(const char* operational_mode) +{ + if (strcmp(operational_mode, "ap") == 0 || + strcmp(operational_mode, "access-point") == 0) { + return WifiOperationalMode::AccessPoint; + } else if (strcmp(operational_mode, "sta") == 0 || + strcmp(operational_mode, "station") == 0) { + return WifiOperationalMode::Station; + } + + throw new std::invalid_argument("invalid operational mode string"); +} + +struct WifiTelemetrySourceArgs +{ + explicit WifiTelemetrySourceArgs(const std::string& name_) : + name(name_) + {} + + std::string name; + std::optional interface; + std::optional operational_mode; +}; + +int +main(int argc, char* argv[]) +{ + WifiTelemetryMonitor monitor{}; + WifiTelemetrySourceArgs* source_active = nullptr; + std::vector sources{}; + + for (;;) { + int opt = getopt(argc, argv, "i:o:s:"); + if (opt < 0) + break; + + switch (opt) { + case 'i': + if (source_active) + source_active->interface = triml(optarg); + break; + case 'o': + if (source_active) + source_active->operational_mode = parse_wifi_operational_mode(triml(optarg)); + break; + case 's': + source_active = &sources.emplace_back(triml(optarg)); + break; + default: + break; + } + } + + if (sources.size() == 0) { + std::cerr << "no sources added" << std::endl; + return -1; + } + + for (const auto& source : sources) { + // prepare interface configuration, if specified + std::optional interface; + if (source.interface.has_value()) { + interface.emplace(source.interface.value()); + if (source.operational_mode.has_value()) + interface->operational_mode = source.operational_mode.value(); + } + + // add the source + if (source.name == "wpa") + monitor.add_source(interface); + else if (source.name == "ztp") + monitor.add_source(interface); + } + + monitor.start(); + + // Mask SIGTERM and SIGINT so they can be explicitly waited on from the main thread. + int signal; + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGINT); + + if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) { + std::cerr << "failed to block terminate signals" << std::endl; + return -1; + } + + // Wait for the process to be signaled to exit. + while (sigwait(&mask, &signal) != 0) + ; + + // Received interrupt or terminate signal, so shutdown. + monitor.stop(); + + return 0; +} diff --git a/src/daemon/systemd/CMakeLists.txt b/src/daemon/systemd/CMakeLists.txt new file mode 100644 index 0000000..b0cbce0 --- /dev/null +++ b/src/daemon/systemd/CMakeLists.txt @@ -0,0 +1,23 @@ + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/wifi-telemetryd-ap@.service.in + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-ap@.service +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/wifi-telemetryd-station@.service.in + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-station@.service +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/wifi-telemetryd-ztp.service.in + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-ztp.service +) + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-ap@.service + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-station@.service + ${CMAKE_CURRENT_BINARY_DIR}/wifi-telemetryd-ztp.service + DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/system +) diff --git a/src/daemon/systemd/wifi-telemetryd-ap@.service.in b/src/daemon/systemd/wifi-telemetryd-ap@.service.in new file mode 100644 index 0000000..df76dd6 --- /dev/null +++ b/src/daemon/systemd/wifi-telemetryd-ap@.service.in @@ -0,0 +1,9 @@ +[Unit] +Description=Wi-Fi Telemetry Daemon (WPA Access Point) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +[Service] +Type=simple +Restart=on-failure +ExecStart=${CMAKE_INSTALL_FULL_SBINDIR}/wifi-telemetryd -s wpa -o ap -i %i diff --git a/src/daemon/systemd/wifi-telemetryd-station@.service.in b/src/daemon/systemd/wifi-telemetryd-station@.service.in new file mode 100644 index 0000000..1dafcf8 --- /dev/null +++ b/src/daemon/systemd/wifi-telemetryd-station@.service.in @@ -0,0 +1,9 @@ +[Unit] +Description=Wi-Fi Telemetry Daemon (WPA Station) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +[Service] +Type=simple +Restart=on-failure +ExecStart=${CMAKE_INSTALL_FULL_SBINDIR}/wifi-telemetryd -s wpa -o station -i %i diff --git a/src/daemon/systemd/wifi-telemetryd-ztp.service.in b/src/daemon/systemd/wifi-telemetryd-ztp.service.in new file mode 100644 index 0000000..c35ff6e --- /dev/null +++ b/src/daemon/systemd/wifi-telemetryd-ztp.service.in @@ -0,0 +1,7 @@ +[Unit] +Description=Wi-Fi Telemetry Daemon (Zero Touch Provisioning) + +[Service] +Type=simple +Restart=on-failure +ExecStart=${CMAKE_INSTALL_FULL_SBINDIR}/wifi-telemetryd -s ztp diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt new file mode 100644 index 0000000..dab803f --- /dev/null +++ b/src/lib/CMakeLists.txt @@ -0,0 +1,48 @@ + +add_library(wifi-telemetry + SHARED + "" +) + +if (BUILD_HOSTAP_EXTERNAL) + add_dependencies(wifi-telemetry hostap) +endif() + +target_include_directories(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +target_link_libraries(wifi-telemetry + INTERFACE + ${LIBSYSTEMD_TARGET} + ${LIBWPA_CLIENT_TARGET} + udev + pci +) + +target_sources(wifi-telemetry + PRIVATE + wifi_device_info.cxx + wifi_telemetry_monitor.cxx + wifi_telemetry_source.cxx + wifi_telemetry_instance_impl.cxx + wifi_telemetry_source_wpa.cxx + wifi_telemetry_source_ztpd.cxx + ../../include/wifi-telemetry/wifi/wifi_80211_format_mac.hpp +) + +add_subdirectory(wpa) +add_subdirectory(lttng-providers) +add_subdirectory(ztpd) + +install( + TARGETS wifi-telemetry + EXPORT wifi-telemetry-targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) diff --git a/src/lib/lttng-providers/CMakeLists.txt b/src/lib/lttng-providers/CMakeLists.txt new file mode 100644 index 0000000..a9f6f8f --- /dev/null +++ b/src/lib/lttng-providers/CMakeLists.txt @@ -0,0 +1,28 @@ + +target_sources(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_lttng_provider_station.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_lttng_provider_dpp.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_lttng_provider_wifi.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_trace_dpp.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_trace_station.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_trace_wifi.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_trace.cxx +) + +target_include_directories(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(wifi-telemetry + INTERFACE + LTTng::UST +) + +if (ENABLE_MS_LTTNG_EXTENSIONS) + target_link_libraries(wifi-telemetry + PRIVATE + tracelogging::tracelogging + ) +endif() diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.cxx b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.cxx new file mode 100644 index 0000000..d305361 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.cxx @@ -0,0 +1,5 @@ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +#include "wifi_telemetry_lttng_provider_dpp.hpp" diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.hpp b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.hpp new file mode 100644 index 0000000..f2a6d90 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_dpp.hpp @@ -0,0 +1,326 @@ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER wifi_dpp + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./wifi_telemetry_lttng_provider_dpp.hpp" + +#if !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_DPP_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_LTTNG_UST_TRACEF_PROVIDER_H + +#define __WIFI_TELEMETRY_LTTNG_PROVIDER_DPP_HPP__ + +#include +#include +#include + +/** + * @brief Event for a completed Device Provisioning Protocol (DPP) enrollee + * exchange. + * + * This event fires each time a DPP exchange completes with the host device + * acting as the enrollee device role. The event has a state which describes + * the furthest state of the exchange reached, which is a relative indication + * of success or failure. + * + * See the telemetry field descriptions below to determine which fields are + * valid for specific state values. + */ +TRACEPOINT_EVENT( + wifi_dpp, + dpp_exchange_enrollee, + TP_ARGS( + const char *, interface, + const char *, state, + const char *, role, + uint64_t, duration_milliseconds, + size_t, chirp_frequencies_count, + const uint32_t *, chirp_frequencies, + const char *, failure_type, + const char *, failure_details + ), + TP_FIELDS( + /** + * @brief Interface name [required]. + * + * This fields describes the name of the wireless interface. It is + * always included in the telemetry event. The interface may be virtual + * or physical; no indication is provided in the event. + * + * The data is formatted as a free-form string, consisting of + * printable ASCII characters. + * + * @example wlan0 + * @example wlp4s0 + */ + ctf_string(interface, interface) + + /** + * @brief The last/furthest state of the dpp exchange reached [required]. + * + * This field describes the furthest state of the dpp exchange reached. + * The exchange was successful if the terminal state was entered, + * indicated by the value "provisioned". The following are the possible + * values: + * + * - bootstrapping + * This is the initial state. + * - authenticated + * This state is entered once bootstrapping has completed. + * - provisioning + * This state is entered once the dpp peer has been successfully + * authenticated. + * - provisioned + * This state is entered once the enrollee has received a network + * configuration object. + * - terminated + * This state is erntered if an error occurs during the exchange. In + * this case, the 'failure_type' and 'failure_details' fields provide + * more information about the cause of the failure. + */ + ctf_string(state, state) + + /** + * @brief DPP exchange role [required]. + * + * This field indicates the DPP role of the host, either initiator or + * responder. + * + * The following strings are the possible values of this field: + * + * - initiator + * - responder + */ + ctf_string(role, role) + + /** + * @brief Exchange duration (milliseconds) [required]. + * + * This fields describes the duration of the DPP exchange, in + * milliseconds. + * + * The exchange is started when the first presence announcement (chirp) + * is transmitted and completes when either a timeout occurs, an error + * is encountered, or the enrollee is successfully provisioned. + * + * @example 2100 + */ + ctf_integer(uint64_t, duration_milliseconds, duration_milliseconds) + + /** + * @brief Exchange presence announcement (chirp) frequencies [required]. + * + * This field describes all the frequencies where presence announcement + * frames were sent. This is an additive, unique list that includes + * every such frequency chirped, even if the last iteration of chirping + * did not include a particular frequency. + * + * The chirp frequencies are encoded as an array of 32-bit unsigned + * integers. + */ + ctf_sequence(uint32_t, chirp_frequencies, chirp_frequencies, size_t, chirp_frequencies_count) + + /** + * @brief The DPP failure type [conditional]. + * + * This field describes the category of failure that occurred if the + * exchange failed. This field is only valid when the state field has + * the value "terminated". + * + * The following strings are the possible values of this field: + * + * - none + * This occurs when there was no error. + * - unspecified + * This occurs when the error is not known. + * - authentication-timeout + * This occurs when the DPP peer took too long to respond to the + * authentication request frame. + */ + ctf_string(failure_type, failure_type) + + /** + * @brief DPP failure details [conditional]. + * + * This field describes additional details regarding the cause of the + * failure. This field is only valid when the state field has the value + * "terminated". It is a free-form string. + */ + ctf_string(failure_details, failure_details))) + +/** + * @brief Event for a complete dpp configurator exchange. + */ +TRACEPOINT_EVENT( + wifi_dpp, + dpp_exchange_configurator, + TP_ARGS( + const char *, interface, + const char *, state, + const char *, role, + uint64_t, duration_milliseconds, + const char *, bssid, + uint32_t, frequency, + const char *, failure_type, + const char *, failure_details + ), + TP_FIELDS( + /** + * @brief Interface name [required]. + * + * This fields describes the name of the wireless interface. It is + * always included in the telemetry event. The interface may be virtual + * or physical; no indication is provided in the event. + * + * The data is formatted as a free-form string, consisting of + * printable ASCII characters. + * + * @example wlan0 + * @example wlp4s0 + */ + ctf_string(interface, interface) + + /** + * @brief The last/furthest state of the dpp exchange reached [required]. + * + * This field describes the furthest state of the dpp exchange reached. + * The exchange was successful if the terminal state was entered, + * indicated by the value "finished". The following are the possible + * values: + * + * - bootstrapping + * This is the initial state. + * - authenticated + * This state is entered once bootstrapping has completed. + * - finished + * This state is entered once the configurator has sent a network + * configuration object to the enrollee. + * - terminated + * This state is erntered if an error occurs during the exchange. In + * this case, the 'failure_type' and 'failure_details' fields provide + * more information about the cause of the failure. + */ + ctf_string(state, state) + + /** + * @brief DPP exchange role [required]. + * + * This field indicates the DPP role of the host, either initiator or + * responder. + * + * The following strings are the possible values of this field: + * + * - initiator + * - responder + */ + ctf_string(role, role) + + /** + * @brief Exchange duration (milliseconds) [required]. + * + * This fields describes the duration of the DPP exchange, in + * milliseconds. + * + * The exchange is started when the first presence announcement (chirp) + * is received and completes when either a timeout occurs, an error + * is encountered, or a network configuration object is sent to the + * enrollee. + * + * @example 2100 + */ + ctf_integer(uint64_t, duration_milliseconds, duration_milliseconds) + + /** + * @brief The Basic Service Set Identifier (BSSID) of the enrollee + * [required]. + * + * This field describes the BSSID of the enrollee involved in the + * exchange. + * + * The data is formatted as a 48-bit sequence of hexadecimal pairs, + * separated by a colon (:). + * + * @example 70:66:55:09:b6:6f + * @example 72:66:55:27:2c:1b + */ + ctf_string(bssid, bssid) + + /** + * @brief The radio frequency used for the exchange (MHz) [optional]. + * + * This field describes the radio frequency used for the exchange. This + * field is always valid for all state field values except + * 'bootstrapping', for which it is optionally valid. A value of zero + * (0) indicates the field is invalid and should be ignored. + * + * @example 2412 + * @example 5180 + */ + ctf_integer(uint32_t, frequency, frequency) + + /** + * @brief The DPP failure type [conditional]. + * + * This field describes the category of failure that occurred if the + * exchange failed. This field is only valid when the state field has + * the value "terminated". + * + * The following strings are the possible values of this field: + * + * - none + * This occurs when there was no error. + * - unspecified + * This occurs when the error is not known. + * - authentication-timeout + * This occurs when the DPP peer took too long to respond to the + * authentication request frame. + */ + ctf_string(failure_type, failure_type) + + /** + * @brief DPP failure details [conditional]. + * + * This field describes additional details regarding the cause of the + * failure. This field is only valid when the state field has the value + * "terminated". It is a free-form string. + */ + ctf_string(failure_details, failure_details))) + +/** + * @brief Event for Device Provisioning Protocol device role change. + * + * This event fires any time the active DPP device roles change on the host. + * The event reports the currently active device roles (not the device role + * that changed disposition). + */ +TRACEPOINT_EVENT( + wifi_dpp, + dpp_device_roles_changed, + TP_ARGS( + const char*, device_roles), + TP_FIELDS( + /** + * @brief Active Device Provisioning Protocol (DPP) device roles + * [required]. + * + * This field describes the currently active DPP device roles. There + * are two possible values: + * + * - enrollee + * - configurator + * + * This field is encoded as a single string using the comma (,) as a + * delimeter for each active device role, with no additional spaces + * separating the delimeter and subsequent role(s). The string may be + * empty or contain any combination of the above values in any order. + * + * @example enrollee,configurator + * @example enrollee + * @example configurator + * @example configurator,enrollee + */ + ctf_string(device_roles, device_roles))) + +#endif // !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_DPP_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ) + +#include diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.cxx b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.cxx new file mode 100644 index 0000000..f23e3f6 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.cxx @@ -0,0 +1,5 @@ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +#include "wifi_telemetry_lttng_provider_station.hpp" diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.hpp b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.hpp new file mode 100644 index 0000000..4eb8399 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_station.hpp @@ -0,0 +1,387 @@ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER wifi_station + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./wifi_telemetry_lttng_provider_station.hpp" + +#if !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_STATION_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_LTTNG_UST_TRACEF_PROVIDER_H + +#define __WIFI_TELEMETRY_LTTNG_PROVIDER_STATION_HPP__ + +#include +#include +#include + +#include "wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp" + +/** + * @brief Event for a connection attempt from a wifi station (client) to an + * access point or other peer. + * + * This event fires upon completion of all connection attempts. The attempt has + * a result which describes whether it was successful, and result-specific + * fields that further describe details of the result; connection state + * information when succesful, and failure details otherwise. + * + * See the telemetry field descriptions below to determine which fields are + * valid for specific result values. + */ +TRACEPOINT_EVENT( + wifi_station, + connection_attempt, + TP_ARGS( + const char*, interface, + const WifiTelemetryInputStationConnectionAttempt*, data), + TP_FIELDS( + /** + * @brief Interface name [required]. + * + * This fields describes the name of the wireless interface. It is + * always included in the telemetry event. The interface may be virtual + * or physical; no indication is provided in the event. + * + * The data is formatted as a free-form string, consisting of + * printable ASCII characters. + * + * @example wlan0 + * @example wlp4s0 + */ + ctf_string(interface, interface) + + /** + * @brief The result of the connection attempt [required]. + * + * This field describes the coarse result of the connection attempt. + * + * The data is formatted as an unsigned, 32-bit integer. @see + * WifiConnectionAttemptResult for a complete list of all possible + * values this field can take. + * + * result values and sub-fields: + * + * - WifiConnectionAttemptResult::Succeeded (value=0) + * This result occurs when the connection attempt was successful and a + * valid connection was made. In this case, the following telemetry + * fields will further describe the state of the connection: + * + * - bssid + * - ssid + * - rssi + * - noise + * - link_speed + * - frequency + * - wifi_generation + * - key_mgmt + * - group_cipher + * + * - WifiConnectionAttemptResult::Unspecified (value=1) + * This result occurs when the connection attempt failed and no + * specific reason for the failure could be determined. No additional + * fields are valid for this result value. + * + * - WifiConnectionAttemptResult::AuthenticationRejected (value=2) + * This result occurs when the connection attempt failed due to + * authentication being rejected by the peer. In this case, the + * following telemetry fields in this event will further describe the + * reason for failure: + * + * - bssid + * - status_code + * - authentication_type + * + * The status code field describes the specific reason for failure. + * See the comments in status code for more details. The + * autnentication type field describes the type of authentication + * attempted. + * + * - WifiConnectionAttemptResult::AssociationRejected (value=3) + * This result occurs when the connection attempt failed due to + * association being rejected. In this case, the following telemetry + * fields in this event will further describe the reason for failure: + * + * - bssid + * - status_code + * + * The status code field describes the specific reason for failure. See + * the comments in status code for more details. + * + * - WifiConnectionAttemptResult::NetworkNotFound: + * This result occurs whenm the connection attempt failed due to the + * selected network not being found. This can occur when either the + * target network is not in radio range, or when the target network was + * found but does not match the characteristics of its profile. For + * example, this can occur when the network profile specifies the + * target network uses one type of authentication, but the actual + * network uses a different type. In this case, no additional telemetry + * fields are valid. + */ + ctf_integer(uint32_t, result, data->result()) + + /** + * @brief The Basic Service Set Identifier (BSSID) of the connection + * attempt peer [conditional]. + * + * This field describes the BSSID of the wireless device peer involved + * in the connection attempt. + * + * The data is formatted as a 48-bit sequence of hexadecimal pairs, + * separated by a colon (:). + * + * @example 70:66:55:09:b6:6f + * @example 72:66:55:27:2c:1b + */ + ctf_string(bssid, data->bssid()) + + /** + * @brief The Service Set Identifier (SSID) of the peer [conditional]. + * + * This field describes the SSID of the wireless device peer involved + * in the connection attempt. + * + * @example My Home Network + * @example Microsoft-Guest + */ + ctf_string(ssid, data->ssid()) + + /** + * @brief Connection Received Signal Strength Indicator (RSSI) [conditional]. + * + * This field describes the RSSI of the connection at the time + * the connection was established. This is technically a unit-less + * value, however, is typically reported in dBm. RSSI units and values + * are vendor specific and so should not be compared across vendors and + * in some cases, even different devices. + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + * + * @example -70 + * @example 10 + */ + ctf_integer(int32_t, rssi, data->rssi()) + + /** + * @brief Connection noise level (dB) [conditional]. + * + * This fields describes the noise level of the connection at the time + * the connection was established. The unit is decibels (dB). + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_integer(int32_t, noise, data->noise()) + + /** + * @brief Connection link speed (Mbps) [conditional]. + * + * This fields describes the link speed of the connection at the time + * the connection was established. The unit is Mbps. + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_integer(int32_t, link_speed, data->link_speed()) + + /** + * @brief Connection frequency (MHz) [conditional]. + * + * This field describes the frequency of the connection at the time the + * connection was established. The unit is MHz. + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_integer(uint32_t, frequency, data->frequency()) + + /** + * @brief Connection Wi-Fi generation [conditional]. + * + * This field describes the numerical Wi-Fi generation of the + * connection. This corresponds to specific IEEE Wi-Fi standards, + * starting with 802.11n (4), 802.11ac (5), 802.11ax (6). + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_integer(uint32_t, wifi_generation, data->wifi_generation()) + + /** + * @brief Connection key management [conditional]. + * + * This field describes the key management method used by the + * connection. The following strings are some of the possible values: + * + * - NONE + * - WPA-NONE + * - IEEE 802.1X (no WPA) + * - FT-EAP + * - FT-EAP-SHA384 + * - FT-PSK + * - WPA2-EAP-SHA256 + * - WPA2-PSK-SHA256 + * - WPS + * - SAE + * - FT-SAE + * - OSEN + * - WPA2-EAP-SUITE-B + * - WPA2-EAP-SUITE-B-192 + * - FILS-SHA256 + * - FILS-SHA384 + * - FT-FILS-SHA256 + * - FT-FILS-SHA384 + * - OWE + * - DPP + * - UNKNOWN + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_string(key_mgmt, data->key_mgmt()) + + /** + * @brief Connection pairwise cipher [conditional]. + * + * This fields describes the connection pairwise cipher. The pairwise + * cipher is used for unicast (point-to-point) traffic. + * + * The following strings are some of the possible values: + * - NONE + * - WEP-40 + * - WEP-104 + * - TKIP + * - CCMP + * - CCMP+TKIP + * - GCMP + * - GCMP-256 + * - CCMP-256 + * - BIP + * - BIP-GMAC-128 + * - BIP-GMAC-256 + * - BIP-CMAC-256 + * - GTK_NOT_USED + * - UNKNOWN + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_string(pairwise_cipher, data->pairwise_cipher()) + + /** + * @brief Connection group cipher [conditional]. + * + * This fields describes the connection group cipher. The group cipher + * is used for multicast traffic. + * + * The possible values are the same as pairwise_cipher. + * + * This field is only valid when result == 0 (Succeeded) and is + * otherwise undefined and must be discarded. + */ + ctf_string(group_cipher, data->group_cipher()) + + /** + * @brief The Wi-Fi status code of the connection attempt [conditional]. + * + * This fields describes the Wi-Status code of the connection attempt. + * The values are defined in IEEE Std 802.11-2016, Section 9.4.1.9, + * Table 9-46. @see WifiStatusCode for a more detailed description. + * + * This field is only valid when: + * result == 2 (AuthenticationRejected) OR + * result == 3 (AssociationRejected) + * and is otherwise undefined and must be discarded + */ + ctf_integer(uint16_t, status_code, data->status_code()) + + /** + * @brief The connection authentication type [conditional]. + * + * This field describes the general authentication type of the + * connnection attempt. + * + * The following strings are some of the possible values: + * + * - unknown + * - open + * - wpa-psk + * - shared + * - wpa + * - wpa2 + * - wpa2-psk + * + * This field is only valid when result == 2 + * (AuthenticationRejected) and is otherwise undefined and must be + * discarded. + */ + ctf_string(authentication_type, data->authentication_type()))) + +/** + * @brief Event for a connection drop from an access point or other wireless peer. + * + * This event fires any time an established wireless connection is severred or + * dropped, regardless of whether it was a healthy/expected disconnection or an + * unhealthy/unexpected one. + */ +TRACEPOINT_EVENT( + wifi_station, + connection_drop, + TP_ARGS( + const char*, interface, + const char*, bssid, + uint16_t, reason_code, + int, locally_generated), + TP_FIELDS( + /** + * @brief Interface name [required]. + * + * This fields describes the name of the wireless interface. It is + * always included in the telemetry event. The interface may be virtual + * or physical; no indication is provided in the event. + * + * The data is formatted as a free-form string, consisting of + * printable ASCII characters. + * + * @example wlan0 + * @example wlp4s0 + */ + ctf_string(interface, interface) + + /** + * @brief The Basic Service Set Identifier (BSSID) of the connection + * attempt peer [required]. + * + * This field describes the BSSID of the wireless device peer that was + * disconnected. + * + * The data is formatted as a 48-bit sequence of hexadecimal pairs, + * separated by a colon (:). + * + * @example 70:66:55:09:b6:6f + * @example 72:66:55:27:2c:1b + */ + ctf_string(bssid, bssid) + + /** + * @brief The reason code for disassociation [required]. + * + * This field describes the reason for disassociation (connection + * drop). The values are defined in IEEE Std 802.11-2016, Section + * 9.4.1.4, Table 9-45. @see WifiDeauthenticationReasonCode for a more + * detailed description. + */ + ctf_integer(uint16_t, reason_code, reason_code) + + /** + * @brief Local generation/request indicator [required]. + * + * This field describes whether the connection drop originated from the + * client (locally_generated != 0) or originated from the peer + * (locally_generated == 0). + */ + ctf_integer(int, locally_generated, locally_generated))) + +#endif // !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_STATION_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ + +#include diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.cxx b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.cxx new file mode 100644 index 0000000..0a3f3d5 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.cxx @@ -0,0 +1,5 @@ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +#include "wifi_telemetry_lttng_provider_wifi.hpp" diff --git a/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.hpp b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.hpp new file mode 100644 index 0000000..67c1275 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_lttng_provider_wifi.hpp @@ -0,0 +1,164 @@ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER wifi + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./wifi_telemetry_lttng_provider_wifi.hpp" + +#if !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_WIFI_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_LTTNG_UST_TRACEF_PROVIDER_H + +#define __WIFI_TELEMETRY_LTTNG_PROVIDER_WIFI_HPP__ + +#include +#include +#include + +#include "wifi_device_info.hpp" + +/** + * @brief Event for wifi device/chipset information. + * + * This event fires periodically to report information about the wifi chipset + * and driver being used for a particular interface. The event will fire each + * start() is called on a WifiTelemetryMonitor instance. All telemetry events + * generated from the monitor can then be associated with the device + * information reported at the beginning of the session. + */ +TRACEPOINT_EVENT( + wifi, + device_info, + TP_ARGS( + const struct WifiDeviceInfo*, device), + TP_FIELDS( + /** + * @brief Interface name [required]. + * + * This fields describes the name of the wireless interface. It is + * always included in the telemetry event. The interface may be virtual + * or physical; no indication is provided in the event. + * + * The data is formatted as a free-form string, consisting of + * printable ASCII characters. + * + * @example wlan0 + * @example wlp4s0 + */ + ctf_string(interface, device->interface.c_str()) + + /** + * @brief Wi-Fi Basic Service Set Identifier (BSSID) [required]. + * + * This field describes the BSSID of the wireless device, which + * uniquely identifies an 802.11-based wireless device. + * + * The data is formatted as a 48-bit sequence of hexadecimal pairs, + * separated by a colon (:). + * + * @example 70:66:55:09:b6:6f + * @example 72:66:55:27:2c:1b + */ + ctf_string(bssid, device->bssid.c_str()) + + /** + * @brief Device subsystem [required]. + * + * This fields describes the kernel subsystem that is used to host the + * device. This typically describes the device's bus. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example pci + * @example usb + */ + ctf_string(subsystem, device->subsystem.c_str()) + + /** + * @brief Kernel driver (module) name [required]. + * + * This field describes the name of the kernel driver (module) used to + * control the device. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example ath10k + * @example rtl88x2ce + */ + ctf_string(kernel_driver, device->kernel_driver.c_str()) + + /** + * @brief Kernel driver (module) version [required]. + * + * This fields describes the version string of the kernel driver + * (module). Each module may define this string arbitrarily; it may + * contain numerical version information. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example v5.9.0.3_36433.20200310_COEX20200103-1717 + */ + ctf_string(kernel_driver_version, device->kernel_driver_version.c_str()) + + /** + * @brief Device vendor name [optional]. + * + * This fields describes the vendor of the device. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example Intel Corporation + * @example Realtek Semiconductor Co., Ltd. + */ + ctf_string(vendor, device->vendor.value_or("").c_str()) + + /** + * @brief Device model name [optional]. + * + * This fields describes the device name or model of the device, + * differentiating it from other devices manufactured by the vendor. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example Device c822 + */ + ctf_string(device, device->device.value_or("").c_str()) + + /** + * @brief Device subsystem vendor [optional]. + * + * This field describes the name of the subsystem-specific vendor of + * the device. This can be different from the device vendor field when + * the bus portion of the device is implemented using different IHV + * hardware components. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + * + * @example AzureWave + */ + ctf_string(subsystem_vendor, device->subsystem_vendor.value_or("").c_str()) + + /** + * @brief Device subsystem model name [optional]. + * + * This field describes the name of the subsystem-specific vendor of + * the device, differentiating it from other devices manufactured by + * the subsystem vendor. This can be different from the device model + * field when the bus potion of the device is implemented using + * different IHV hardware components. + * + * The data is formatted as a free-form string, consisting of printable + * ASCII characters. + + * @example Device 3750 + */ + ctf_string(subsystem_device, device->subsystem_device.value_or("").c_str()))) + +#endif // !defined(__WIFI_TELEMETRY_LTTNG_PROVIDER_WIFI_HPP__) || defined(TRACEPOINT_HEADER_MULTI_READ) + +#include diff --git a/src/lib/lttng-providers/wifi_telemetry_trace.cxx b/src/lib/lttng-providers/wifi_telemetry_trace.cxx new file mode 100644 index 0000000..545af46 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace.cxx @@ -0,0 +1,29 @@ + +#include "wifi_telemetry_trace.hpp" +#include "wifi_telemetry_trace_dpp.hpp" +#include "wifi_telemetry_trace_station.hpp" +#include "wifi_telemetry_trace_wifi.hpp" + +#ifdef TRACE_USE_TRACELOGGING +#include "wifi_telemetry_tracelogging.hpp" +#endif // !TRACE_USE_TRACELOGGING + +void +WifiTelemetryTraceProvider::RegisterAll(void) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingRegister(g_TraceloggingProviderWifi); + TraceLoggingRegister(g_TraceloggingProviderWifiDpp); + TraceLoggingRegister(g_TraceloggingProviderWifiStation); +#endif +} + +void +WifiTelemetryTraceProvider::UnregisterAll(void) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingUnregister(g_TraceloggingProviderWifi); + TraceLoggingUnregister(g_TraceloggingProviderWifiDpp); + TraceLoggingUnregister(g_TraceloggingProviderWifiStation); +#endif +} diff --git a/src/lib/lttng-providers/wifi_telemetry_trace.hpp b/src/lib/lttng-providers/wifi_telemetry_trace.hpp new file mode 100644 index 0000000..9884e78 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace.hpp @@ -0,0 +1,21 @@ + +#ifndef __WIFI_TELEMETRY_TRACE_HPP__ +#define __WIFI_TELEMETRY_TRACE_HPP__ + +struct WifiTelemetryTraceProvider +{ + /** + * @brief Registers all Tracelogging-based providers with LLTNG. This must + * be called prior to generating any tracelogging-based telemetry events. + */ + static void + RegisterAll(void); + + /** + * @brief Unregisters all Tracelogging-based providers from LTTNG. + */ + static void + UnregisterAll(void); +}; + +#endif //__WIFI_TELEMETRY_TRACEPROVIDERS_HPP__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_dpp.cxx b/src/lib/lttng-providers/wifi_telemetry_trace_dpp.cxx new file mode 100644 index 0000000..1a8d7bf --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_dpp.cxx @@ -0,0 +1,91 @@ + +#include "wifi_telemetry_trace_dpp.hpp" +#include "wifi_telemetry_lttng_provider_dpp.hpp" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wgnu-statement-expression" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif //__clang__ + +#ifdef TRACE_USE_TRACELOGGING +// Provider UUID: 91adedb1-4f70-405f-9771-00415a5e375e +TRACELOGGING_DEFINE_PROVIDER( + g_TraceloggingProviderWifiDpp, + "Microsoft.Azure.Device.Wifi.Dpp", + (0x91adedb1, 0x4f70, 0x405f, 0x97, 0x71, 0x00, 0x41, 0x5a, 0x5e, 0x37, 0x5e)); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_dpp_exchange_enrollee(const char* interface, const char* state, const char* role, uint64_t duration_milliseconds, size_t chirp_frequencies_count, const uint32_t* chirp_frequencies, const char* failure_type, const char* failure_details) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifiDpp, + "DppExchangeEnrollee", + TraceLoggingString(interface, "interface"), + TraceLoggingString(state, "exchangeState"), + TraceLoggingString(role, "dppDeviceRole"), + TraceLoggingUInt64(duration_milliseconds, "durationMilliseconds"), + TraceLoggingUInt32Array(chirp_frequencies, static_cast(chirp_frequencies_count), "chirpFrequenciesMhz"), + TraceLoggingString(failure_type, "failureType"), + TraceLoggingString(failure_details, "failureDetails")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi_dpp, + dpp_exchange_enrollee, + interface, + role, + state, + duration_milliseconds, + chirp_frequencies_count, + chirp_frequencies, + failure_type, + failure_details); +#endif // !TRACE_USE_TRACELOGGING +} + +void +trace_dpp_exchange_configurator(const char* interface, const char* state, const char* role, uint64_t duration_milliseconds, const char* bssid, uint32_t frequency, const char* failure_type, const char* failure_details) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifiDpp, + "DppExchangeConfigurator", + TraceLoggingString(interface, "interface"), + TraceLoggingString(state, "exchangeState"), + TraceLoggingString(role, "dppDeviceRole"), + TraceLoggingUInt64(duration_milliseconds, "durationMilliseconds"), + TraceLoggingString(bssid, "bssid"), + TraceLoggingUInt32(frequency, "frequencyMHz"), + TraceLoggingString(failure_type, "failureType"), + TraceLoggingString(failure_details, "failureDetails")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi_dpp, + dpp_exchange_configurator, + interface, + role, + state, + duration_milliseconds, + bssid, + frequency, + failure_type, + failure_details); +#endif // !TRACE_USE_TRACELOGGING +} + +void +trace_dpp_device_roles_changed(const char* device_roles) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifiDpp, + "DppDeviceRolesChanged", + TraceLoggingString(device_roles, "dppDeviceRolesActive")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi_dpp, + dpp_device_roles_changed, + device_roles); +#endif // !TRACE_USE_TRACELOGGING +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif //__clang__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_dpp.hpp b/src/lib/lttng-providers/wifi_telemetry_trace_dpp.hpp new file mode 100644 index 0000000..1ec6087 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_dpp.hpp @@ -0,0 +1,25 @@ + +#ifndef __WIFI_TELEMETRY_TRACE_DPP_HPP__ +#define __WIFI_TELEMETRY_TRACE_DPP_HPP__ + +#include +#include + +#ifdef TRACE_USE_TRACELOGGING +#include "wifi_telemetry_tracelogging.hpp" +#endif // !TRACE_USE_TRACELOGGING + +#ifdef TRACE_USE_TRACELOGGING +TRACELOGGING_DECLARE_PROVIDER(g_TraceloggingProviderWifiDpp); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_dpp_exchange_enrollee(const char* interface, const char* state, const char* role, uint64_t duration_milliseconds, size_t chirp_frequencies_count, const uint32_t* chirp_frequencies, const char* failure_type, const char* failure_details); + +void +trace_dpp_exchange_configurator(const char* interface, const char* state, const char* role, uint64_t duration_milliseconds, const char* bssid, uint32_t frequency, const char* failure_type, const char* failure_details); + +void +trace_dpp_device_roles_changed(const char* device_roles); + +#endif //__WIFI_TELEMETRY_TRACE_DPP_HPP__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_station.cxx b/src/lib/lttng-providers/wifi_telemetry_trace_station.cxx new file mode 100644 index 0000000..d436671 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_station.cxx @@ -0,0 +1,70 @@ + +#include "wifi_telemetry_trace_station.hpp" +#include "wifi_telemetry_lttng_provider_station.hpp" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wgnu-statement-expression" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif //__clang__ + +#ifdef TRACE_USE_TRACELOGGING +// Provider UUID: 00bb2910-064f-41b3-8ead-ad6d608a10d0 +TRACELOGGING_DEFINE_PROVIDER( + g_TraceloggingProviderWifiStation, + "Microsoft.Azure.Device.Wifi.Station", + (0x00bb2910, 0x064f, 0x41b3, 0x8e, 0xad, 0xad, 0x6d, 0x60, 0x8a, 0x10, 0xd0)); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_station_connection_attempt(const char* interface, const WifiTelemetryInputStationConnectionAttempt* data) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifiStation, + "ConnectionAttempt", + TraceLoggingString(interface, "interface"), + TraceLoggingUInt32(data->result(), "result"), + TraceLoggingString(data->bssid(), "bssid"), + TraceLoggingString(data->ssid(), "ssid"), + TraceLoggingInt32(data->rssi(), "rssi"), + TraceLoggingInt32(data->noise(), "noiseDb"), + TraceLoggingInt32(data->link_speed(), "linkSpeedMbps"), + TraceLoggingUInt32(data->frequency(), "frequencyMHz"), + TraceLoggingUInt32(data->wifi_generation(), "wifiGeneration"), + TraceLoggingString(data->key_mgmt(), "keyManagement"), + TraceLoggingString(data->pairwise_cipher(), "pairwiseCipher"), + TraceLoggingString(data->group_cipher(), "groupCipher"), + TraceLoggingUInt16(data->status_code(), "statusCode"), + TraceLoggingString(data->authentication_type(), "authenticationType")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi_station, + connection_attempt, + interface, + data); +#endif // !TRACE_USE_TRACELOGGING +} + +void +trace_station_connection_drop(const char* interface, const char* bssid, uint16_t reason_code, int locally_generated) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifiStation, + "ConnectionDrop", + TraceLoggingString(interface, "interface"), + TraceLoggingString(bssid, "bssid"), + TraceLoggingUInt16(reason_code, "reasonCode"), + TraceLoggingBoolean(!!locally_generated, "locallyGenerated")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi_station, + connection_drop, + interface, + bssid, + reason_code, + locally_generated); +#endif // !TRACE_USE_TRACELOGGING +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif //__clang__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_station.hpp b/src/lib/lttng-providers/wifi_telemetry_trace_station.hpp new file mode 100644 index 0000000..6133eae --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_station.hpp @@ -0,0 +1,20 @@ + +#ifndef __WIFI_TELEMETRY_TRACE_STATION_HPP__ +#define __WIFI_TELEMETRY_TRACE_STATION_HPP__ + +#ifdef TRACE_USE_TRACELOGGING +#include "wifi_telemetry_tracelogging.hpp" +#endif // !TRACE_USE_TRACELOGGING +#include "wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp" + +#ifdef TRACE_USE_TRACELOGGING +TRACELOGGING_DECLARE_PROVIDER(g_TraceloggingProviderWifiStation); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_station_connection_attempt(const char *interface, const WifiTelemetryInputStationConnectionAttempt *data); + +void +trace_station_connection_drop(const char *interface, const char *bssid, uint16_t reason_code, int locally_generated); + +#endif //__WIFI_TELEMETRY_TRACE_STATION_HPP__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_wifi.cxx b/src/lib/lttng-providers/wifi_telemetry_trace_wifi.cxx new file mode 100644 index 0000000..f21edc8 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_wifi.cxx @@ -0,0 +1,44 @@ + +#include "wifi_telemetry_trace_wifi.hpp" +#include "wifi_telemetry_lttng_provider_wifi.hpp" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wgnu-statement-expression" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#endif //__clang__ + +#ifdef TRACE_USE_TRACELOGGING +// Provider UUID: d4238472-0bb9-4b64-af1e-14f3f7a5411b +TRACELOGGING_DEFINE_PROVIDER( + g_TraceloggingProviderWifi, + "Microsoft.Azure.Device.Wifi", + (0xd4238472, 0x0bb9, 0x4b64, 0xaf, 0x1e, 0x14, 0xf3, 0xf7, 0xa5, 0x41, 0x1b)); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_wifi_device_info(const struct WifiDeviceInfo* device) +{ +#ifdef TRACE_USE_TRACELOGGING + TraceLoggingWrite(g_TraceloggingProviderWifi, + "DeviceInfo", + TraceLoggingString(device->interface.c_str(), "interface"), + TraceLoggingString(device->bssid.c_str(), "bssid"), + TraceLoggingString(device->subsystem.c_str(), "subsystem"), + TraceLoggingString(device->kernel_driver.c_str(), "kernelDriver"), + TraceLoggingString(device->kernel_driver_version.c_str(), "kernelDriverVersion"), + TraceLoggingString(device->vendor.value_or("").c_str(), "vendor"), + TraceLoggingString(device->device.value_or("").c_str(), "model"), + TraceLoggingString(device->subsystem_vendor.value_or("").c_str(), "subsystemVendor"), + TraceLoggingString(device->subsystem_device.value_or("").c_str(), "subsystemModel")); +#else // TRACE_USE_TRACELOGGING + tracepoint(wifi, + device_info, + device); +#endif // !TRACE_USE_TRACELOGGING +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif //__clang__ diff --git a/src/lib/lttng-providers/wifi_telemetry_trace_wifi.hpp b/src/lib/lttng-providers/wifi_telemetry_trace_wifi.hpp new file mode 100644 index 0000000..cd14289 --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_trace_wifi.hpp @@ -0,0 +1,17 @@ + +#ifndef __WIFI_TELEMETRY_TRACEPROVIDER_WIFI_HPP__ +#define __WIFI_TELEMETRY_TRACEPROVIDER_WIFI_HPP__ + +#include "wifi_device_info.hpp" +#ifdef TRACE_USE_TRACELOGGING +#include "wifi_telemetry_tracelogging.hpp" +#endif // !TRACE_USE_TRACELOGGING + +#ifdef TRACE_USE_TRACELOGGING +TRACELOGGING_DECLARE_PROVIDER(g_TraceloggingProviderWifi); +#endif // !TRACE_USE_TRACELOGGING + +void +trace_wifi_device_info(const struct WifiDeviceInfo* info); + +#endif //__WIFI_TELEMETRY_TRACEPROVIDER_WIFI_HPP__ diff --git a/src/lib/lttng-providers/wifi_telemetry_tracelogging.hpp b/src/lib/lttng-providers/wifi_telemetry_tracelogging.hpp new file mode 100644 index 0000000..c882faa --- /dev/null +++ b/src/lib/lttng-providers/wifi_telemetry_tracelogging.hpp @@ -0,0 +1,22 @@ +/** + * This file should be used in place of #include'ing TraceLoggingProvider.h + * directly because it re-defines as macros what LTTNG has defined as an enum + * :-( + */ +#include + +#undef TRACE_EMERG +#undef TRACE_ALERT +#undef TRACE_CRIT +#undef TRACE_ERR +#undef TRACE_WARNING +#undef TRACE_NOTICE +#undef TRACE_INFO +#undef TRACE_DEBUG_SYSTEM +#undef TRACE_DEBUG_PROGRAM +#undef TRACE_DEBUG_PROCESS +#undef TRACE_DEBUG_MODULE +#undef TRACE_DEBUG_UNIT +#undef TRACE_DEBUG_FUNCTION +#undef TRACE_DEBUG_LINE +#undef TRACE_DEBUG diff --git a/src/lib/wifi_device_info.cxx b/src/lib/wifi_device_info.cxx new file mode 100644 index 0000000..8bc56df --- /dev/null +++ b/src/lib/wifi_device_info.cxx @@ -0,0 +1,131 @@ + +#include +#include +#include +#include +#include + +extern "C" { + #include +} + +#include "wifi_device_info.hpp" + +WifiDeviceInfo::WifiDeviceInfo( + const std::string& interface_, + const std::string& bssid_, + const std::string& subsystem_, + const std::string& kernel_driver_, + const std::string& kernel_driver_version_) : +interface(interface_), +bssid(bssid_), +subsystem(subsystem_), +kernel_driver(kernel_driver_), +kernel_driver_version(kernel_driver_version_) +{} + +static uint16_t +hexstr_to_uint16(const char *str) +{ + if (!str) + return 0; + + unsigned long value = strtoul(str, nullptr, 16); + if (value > UINT16_MAX) + value = 0; + + return static_cast(value); +} + +static void +fill_ids_pci(WifiDeviceInfo& device_info, struct udev_device *parent) +{ + const char *vendor_str = udev_device_get_sysattr_value(parent, "vendor"); + const char *device_str = udev_device_get_sysattr_value(parent, "device"); + const char *subsystem_device_str = udev_device_get_sysattr_value(parent, "subsystem_device"); + const char *subsystem_vendor_str = udev_device_get_sysattr_value(parent, "subsystem_vendor"); + + uint16_t vendor = hexstr_to_uint16(vendor_str); + uint16_t device = hexstr_to_uint16(device_str); + uint16_t svendor = hexstr_to_uint16(subsystem_vendor_str); + uint16_t sdevice = hexstr_to_uint16(subsystem_device_str); + + struct pci_access *pacc = pci_alloc(); + char pci_vendor[128]; + char pci_device[128]; + char pci_svendor[128]; + char pci_sdevice[128]; + + pci_init(pacc); + + device_info.vendor = pci_lookup_name(pacc, pci_vendor, sizeof pci_vendor, PCI_LOOKUP_VENDOR, vendor, device); + device_info.device = pci_lookup_name(pacc, pci_device, sizeof pci_device, PCI_LOOKUP_DEVICE, vendor, device); + device_info.subsystem_vendor = pci_lookup_name(pacc, pci_svendor, sizeof pci_svendor, PCI_LOOKUP_VENDOR | PCI_LOOKUP_SUBSYSTEM, svendor); + device_info.subsystem_device = pci_lookup_name(pacc, pci_sdevice, sizeof pci_sdevice, PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM, vendor, device, svendor, sdevice); + + pci_cleanup(pacc); +} + +static void +fill_ids_generic(WifiDeviceInfo& device_info, struct udev_device *parent) +{ + const char *vendor = udev_device_get_property_value(parent, "ID_VENDOR_FROM_DATABASE"); + const char *model = udev_device_get_property_value(parent, "ID_MODEL_FROM_DATABASE"); + + if (!vendor) + vendor = udev_device_get_property_value(parent, "ID_VENDOR"); + if (!model) + model = udev_device_get_property_value(parent, "ID_MODEL"); + + if (vendor) + device_info.vendor = vendor; + if (model) + device_info.device = model; +} + +static void +fill_ids(WifiDeviceInfo& device_info, struct udev_device *parent) +{ + if (device_info.subsystem == "pci") + fill_ids_pci(device_info, parent); + else + fill_ids_generic(device_info, parent); +} + +WifiDeviceInfo +WifiDeviceInfo::from_interface(const std::string& interface) +{ + const std::filesystem::path sysfs_path = std::filesystem::path("/sys/class/net") / interface; + struct udev *udev = udev_new(); + struct udev_device *device = udev_device_new_from_syspath(udev, sysfs_path.c_str()); + if (!device) + throw new std::invalid_argument(std::string("invalid sysfs path ") + sysfs_path.c_str()); + + // Ensure this is a wireless device. + const char *devtype = udev_device_get_devtype(device); + if (!devtype || strcmp(devtype, "wlan") != 0) + throw new std::invalid_argument(interface + " is not a wireless device"); + + struct udev_device *device_parent = udev_device_get_parent(device); + const char *kernel_driver_syspath = udev_device_get_syspath(device_parent); + const std::filesystem::path sysfs_path_kmodule = std::filesystem::path(kernel_driver_syspath) / "driver/module"; + struct udev_device *device_module = udev_device_new_from_syspath(udev, sysfs_path_kmodule.c_str()); + if (!device_module) + throw new std::invalid_argument("unable to obtain device kernel module"); + + const char *address = udev_device_get_sysattr_value(device, "address"); + const char *subsystem = udev_device_get_subsystem(device_parent); + const char *kernel_driver = udev_device_get_driver(device_parent); + const char *kernel_driver_version = udev_device_get_sysattr_value(device_module, "version"); + + WifiDeviceInfo device_info { interface, address, subsystem, kernel_driver, kernel_driver_version }; + + fill_ids(device_info, device_parent); + + udev_device_unref(device_module); + udev_device_unref(device_parent); + udev_device_unref(device); + udev_unref(udev); + + return device_info; +} diff --git a/src/lib/wifi_device_info.hpp b/src/lib/wifi_device_info.hpp new file mode 100644 index 0000000..a8954e6 --- /dev/null +++ b/src/lib/wifi_device_info.hpp @@ -0,0 +1,32 @@ + +#ifndef __WIFI_DEVICE_INFO_HPP__ +#define __WIFI_DEVICE_INFO_HPP__ + +#include +#include + +struct WifiDeviceInfo +{ + WifiDeviceInfo( + const std::string& interface, + const std::string& bssid, + const std::string& subsystem, + const std::string& kernel_driver, + const std::string& kernel_driver_version); + + const std::string interface; + const std::string bssid; + const std::string subsystem; + const std::string kernel_driver; + const std::string kernel_driver_version; + + std::optional vendor; + std::optional device; + std::optional subsystem_vendor; + std::optional subsystem_device; + + static WifiDeviceInfo + from_interface(const std::string& interface); +}; + +#endif // __WIFI_DEVICE_INFO_HPP__ diff --git a/src/lib/wifi_telemetry_instance_impl.cxx b/src/lib/wifi_telemetry_instance_impl.cxx new file mode 100644 index 0000000..af21efd --- /dev/null +++ b/src/lib/wifi_telemetry_instance_impl.cxx @@ -0,0 +1,29 @@ + +#include "wifi_telemetry_instance_impl.hpp" + +WifiTelemetrySourceInstanceImpl::WifiTelemetrySourceInstanceImpl(std::shared_ptr source_, const std::shared_ptr args_) : + WifiTelemetrySourceInstance(source_, args_) +{} + +int +WifiTelemetrySourceInstanceImpl::activate(void) +{ + if (m_activated) + return 0; + + int ret = source->activate(args); + if (ret == 0) + m_activated = true; + + return 0; +} + +void +WifiTelemetrySourceInstanceImpl::deactivate(void) +{ + if (!m_activated) + return; + + source->deactivate(); + m_activated = false; +} diff --git a/src/lib/wifi_telemetry_instance_impl.hpp b/src/lib/wifi_telemetry_instance_impl.hpp new file mode 100644 index 0000000..855d1c4 --- /dev/null +++ b/src/lib/wifi_telemetry_instance_impl.hpp @@ -0,0 +1,22 @@ + +#ifndef __WIFI_TELEMETRY_SOURCE_INSTANCE_IMPL_HPP__ +#define __WIFI_TELEMETRY_SOURCE_INSTANCE_IMPL_HPP__ + +#include "wifi-telemetry/wifi_telemetry_source_instance.hpp" + +struct WifiTelemetrySourceInstanceImpl : + public WifiTelemetrySourceInstance +{ + WifiTelemetrySourceInstanceImpl(std::shared_ptr source, const std::shared_ptr args); + + int + activate(void); + + void + deactivate(void); + +private: + bool m_activated = false; +}; + +#endif // __WIFI_TELEMETRY_SOURCE_INSTANCE_IMPL_HPP__ diff --git a/src/lib/wifi_telemetry_monitor.cxx b/src/lib/wifi_telemetry_monitor.cxx new file mode 100644 index 0000000..a7d760e --- /dev/null +++ b/src/lib/wifi_telemetry_monitor.cxx @@ -0,0 +1,110 @@ + +#include +#include +#include +#include + +#include "wifi-telemetry/wifi_telemetry_monitor.hpp" +#include "wifi_device_info.hpp" +#include "wifi_telemetry_instance_impl.hpp" +#include "wifi_telemetry_trace.hpp" +#include "wifi_telemetry_trace_wifi.hpp" + +WifiTelemetryMonitor::WifiTelemetryMonitor(void) +{ + WifiTelemetryTraceProvider::RegisterAll(); +} + +WifiTelemetryMonitor::~WifiTelemetryMonitor(void) +{ + WifiTelemetryTraceProvider::UnregisterAll(); +} + +void +WifiTelemetryMonitor::add_source_impl(const std::shared_ptr source, const std::shared_ptr activation_args) +{ + auto instance = std::make_unique(source, activation_args); + if (m_activated) + instance->activate(); + + m_sources.push_back(std::move(instance)); +} + +/** + * @brief Removes a telemetry source from the monitor. + * + * @param registration_token The registration token obtained from add_source(). + */ +void +WifiTelemetryMonitor::remove_source(const std::shared_ptr source) +{ + const auto& result = std::find_if(std::begin(m_sources), std::end(m_sources), [&](const auto& instance) { + return (instance->source == source); + }); + + if (result == std::end(m_sources)) + return; + + WifiTelemetrySourceInstanceImpl& instance = dynamic_cast(**result); + + instance.deactivate(); + m_sources.erase(result); +} + +std::size_t +WifiTelemetryMonitor::activate_sources(void) +{ + std::size_t num_activated = 0; + std::unordered_set interfaces; + + for (auto& instance : m_sources) { + int ret = dynamic_cast(*instance).activate(); + if (ret < 0) { + std::cerr << "failed to activate telemetry source '" << instance->source->name() << "' (" << ret << ")" << std::endl; + continue; + } + + // Generate a telemetry event for each unique interface being monitored. + if (instance->source->interface()) { + const auto& [interface, inserted] = interfaces.insert(instance->source->interface()->name); + if (inserted) { + try { + WifiDeviceInfo device_info = WifiDeviceInfo::from_interface(*interface); + trace_wifi_device_info(&device_info); + } catch (...) { + std::cout << "warning: failed to generate wifi device info telemetry event for " << *interface << std::endl; + } + } + } + + num_activated++; + std::cout << "activated telemetry source '" << instance->source->name() << "'" << std::endl; + } + + return num_activated; +} + +void +WifiTelemetryMonitor::deactivate_sources(void) +{ + for (auto& instance : m_sources) { + dynamic_cast(*instance).deactivate(); + } +} + +void +WifiTelemetryMonitor::start(void) +{ + std::size_t num_activated_desired = m_sources.size(); + std::size_t num_activated_actual = activate_sources(); + std::cout << "telemetry monitor started with " << num_activated_actual << " of " << num_activated_desired + << " telemetry sources" << std::endl; + m_activated = true; +} + +void +WifiTelemetryMonitor::stop(void) +{ + deactivate_sources(); + m_activated = false; +} diff --git a/src/lib/wifi_telemetry_source.cxx b/src/lib/wifi_telemetry_source.cxx new file mode 100644 index 0000000..60fca99 --- /dev/null +++ b/src/lib/wifi_telemetry_source.cxx @@ -0,0 +1,36 @@ + +#include "wifi-telemetry/wifi_telemetry_source.hpp" + +WifiTelemetrySource::WifiTelemetrySource(const std::optional& interface) : + m_interface(interface) +{} + +WifiTelemetrySource::WifiTelemetrySource(const std::optional& interface, const std::string& name) : + m_name(name), + m_interface(interface) +{} + +WifiTelemetrySource::~WifiTelemetrySource(void) +{} + +int +WifiTelemetrySource::activate(const std::shared_ptr /* args */) +{ + return 0; +} + +void +WifiTelemetrySource::deactivate(void) +{} + +const std::string_view +WifiTelemetrySource::name(void) const +{ + return m_name; +} + +const std::optional& +WifiTelemetrySource::interface(void) const +{ + return m_interface; +} diff --git a/src/lib/wifi_telemetry_source_wpa.cxx b/src/lib/wifi_telemetry_source_wpa.cxx new file mode 100644 index 0000000..2bc39e6 --- /dev/null +++ b/src/lib/wifi_telemetry_source_wpa.cxx @@ -0,0 +1,358 @@ + +#include +#include +#include + +#include "wifi-telemetry/wifi/wifi_80211_format_mac.hpp" +#include "wifi-telemetry/wifi_telemetry_source_wpa.hpp" +#include "wifi-telemetry/wpa/wpa_commands.hpp" +#include "wifi_telemetry_trace_dpp.hpp" +#include "wifi_telemetry_trace_station.hpp" + +WifiTelemetrySourceWpa::WifiTelemetrySourceWpa(const std::optional& interface) : + WifiTelemetrySource(interface, "wpa") +{ + if (!interface.has_value()) + throw new std::invalid_argument("WifiTelemetrySourceWpa requires interface configuration, but none was supplied"); +} + +WifiTelemetrySourceWpa::~WifiTelemetrySourceWpa(void) +{ + if (m_controller) { + m_controller->unregister_for_events(m_event_token); + m_controller = nullptr; + } +} + +int +WifiTelemetrySourceWpa::activate(const std::shared_ptr /* args */) +{ + WpaType type = WpaInterfaceInfo::TypeFromOperationalMode(m_interface->operational_mode); + + m_controller = std::make_unique(m_interface->name, type); + m_event_token = m_controller->register_for_events(weak_from_this()); + + return 0; +} + +void +WifiTelemetrySourceWpa::deactivate(void) +{ + m_controller = nullptr; +} + +bool +WifiTelemetrySourceWpa::trace_station(void) const +{ + return (m_interface->operational_mode == WifiOperationalMode::Station); +} + +bool +WifiTelemetrySourceWpa::trace_ap(void) const +{ + return (m_interface->operational_mode == WifiOperationalMode::AccessPoint); +} + +bool +WifiTelemetrySourceWpa::trace_dpp_enrollee(void) const +{ + // right now, assume all enrollees are wifi stations. + return trace_station(); +} + +bool +WifiTelemetrySourceWpa::trace_dpp_configurator(void) const +{ + // right now, assume all configurators are wifi access points. + return trace_ap(); +} + +bool +WifiTelemetrySourceWpa::trace_dpp(void) const +{ + return trace_dpp_enrollee() || trace_dpp_configurator(); +} + +bool +WifiTelemetrySourceWpa::is_dpp_exchange_in_progress(void) const +{ + return (m_dpp_exchange != nullptr); +} + +void +WifiTelemetrySourceWpa::on_disconnected(const WpaEventArgs& args) +{ + if (!trace_station()) + return; + + const auto& event = args.data(); + std::string bssid = wifi_80211_mac_to_string(event.bssid); + + trace_station_connection_drop( + m_interface->name.c_str(), + bssid.c_str(), + uint16_t(event.reason_code), + event.locally_generated); +} + +void +WifiTelemetrySourceWpa::complete_connection_attempt(const WifiTelemetryInputStationConnectionAttempt& input) +{ + trace_station_connection_attempt(m_interface->name.c_str(), &input); +} + +void +WifiTelemetrySourceWpa::on_connected(const WpaEventArgs& args) +{ + if (!trace_station()) + return; + + const auto& event = args.data(); + + struct WpaCommandSignalPoll signal_poll{}; + auto response_signal_poll = m_controller->send_command(signal_poll); + if (!response_signal_poll) { + std::cerr << "unable to obtain signal poll data" << std::endl; + return; + } + + struct WpaCommandStatus command_status{}; + auto response_status = m_controller->send_command(command_status); + if (!response_status) { + std::cerr << "unable to obtain interface status data" << std::endl; + return; + } + + auto connection_attempt_data = WifiTelemetryInputStationConnectionAttempt::succeeded( + event.bssid, + response_signal_poll, + response_status); + + complete_connection_attempt(connection_attempt_data); +} + +void +WifiTelemetrySourceWpa::on_association_rejected(const WpaEventArgs& args) +{ + if (!trace_station()) + return; + + const auto& event = args.data(); + auto connection_attempt_data = WifiTelemetryInputStationConnectionAttempt::association_rejected( + event.bssid, + event.status_code); + + complete_connection_attempt(connection_attempt_data); +} + +void +WifiTelemetrySourceWpa::on_authentication_rejected(const WpaEventArgs& args) +{ + if (!trace_station()) + return; + + const auto& event = args.data(); + auto connection_attempt_data = WifiTelemetryInputStationConnectionAttempt::authentication_rejected( + event.bssid, + event.status_code, + event.authentication_type); + + complete_connection_attempt(connection_attempt_data); +} + +void +WifiTelemetrySourceWpa::on_network_not_found(const WpaEventArgs& /* args */) +{ + if (!trace_station()) + return; + + auto connection_attempt_data = WifiTelemetryInputStationConnectionAttempt::network_not_found(); + complete_connection_attempt(connection_attempt_data); +} + +void +WifiTelemetrySourceWpa::complete_dpp_exchange_enrollee(std::shared_ptr& enrollee) +{ + enrollee->stop(); + + std::vector chirp_frequencies(enrollee->chirp_frequencies.begin(), enrollee->chirp_frequencies.end()); + uint64_t duration_milliseconds = enrollee->duration.has_value() + ? static_cast(enrollee->duration->count()) + : 0; + + trace_dpp_exchange_enrollee( + m_interface->name.c_str(), + WifiDppRoleToString(enrollee->role), + WifiDppExchangeEnrolleeStateToString(enrollee->state), + duration_milliseconds, + chirp_frequencies.size(), + chirp_frequencies.data(), + WifiDppFailureTypeToString(enrollee->failure_type), + enrollee->failure_details.value_or("").c_str()); + + m_dpp_exchange.reset(); +} + +void +WifiTelemetrySourceWpa::complete_dpp_exchange_configurator(std::shared_ptr& configurator) +{ + configurator->stop(); + + std::string bssid = wifi_80211_mac_to_string(configurator->peer_bssid); + uint64_t duration_milliseconds = configurator->duration.has_value() + ? static_cast(configurator->duration->count()) + : 0; + + trace_dpp_exchange_configurator( + m_interface->name.c_str(), + WifiDppRoleToString(configurator->role), + WifiDppExchangeConfiguratorStateToString(configurator->state), + duration_milliseconds, + bssid.c_str(), + configurator->frequency, + WifiDppFailureTypeToString(configurator->failure_type), + configurator->failure_details.value_or("").c_str()); + + m_dpp_exchange.reset(); +} + +void +WifiTelemetrySourceWpa::on_dpp_chirp_received(const WpaEventArgs& args) +{ + if (!trace_dpp_configurator()) + return; + + const auto& event = args.data(); + + // configurator doesn't know about this device yet (id == -1), or another exchange in progress. + if (event.id == -1 || is_dpp_exchange_in_progress()) + return; + + m_dpp_exchange = std::make_shared(event.id, event.bssid, event.frequency, WifiDppRole::Initiator); +} + +void +WifiTelemetrySourceWpa::on_dpp_authentication_init_failure(const WpaEventArgs& args) +{ + if (!trace_dpp() || !is_dpp_exchange_in_progress()) + return; + + const auto& event = args.data(); + m_dpp_exchange->failure_type = event.failure_type; + m_dpp_exchange->failure_details = event.failure_details; + + switch (m_dpp_exchange->device_role) { + case WifiDppDeviceRole::Enrollee: { + auto enrollee = resolve_dpp_exchange(); + enrollee->state = WifiDppExchangeEnrolleeState::Terminated; + complete_dpp_exchange_enrollee(enrollee); + break; + } + case WifiDppDeviceRole::Configurator: { + auto configurator = resolve_dpp_exchange(); + configurator->state = WifiDppExchangeConfiguratorState::Terminated; + complete_dpp_exchange_configurator(configurator); + break; + } + default: + return; + } +} + +void +WifiTelemetrySourceWpa::on_dpp_authentication_succeeded(const WpaEventArgs& /* args */) +{ + if (!trace_dpp() || !is_dpp_exchange_in_progress()) + return; + + switch (m_dpp_exchange->device_role) { + case WifiDppDeviceRole::Enrollee: { + auto enrollee = resolve_dpp_exchange(); + enrollee->state = WifiDppExchangeEnrolleeState::Authenticated; + break; + } + case WifiDppDeviceRole::Configurator: { + auto configurator = resolve_dpp_exchange(); + configurator->state = WifiDppExchangeConfiguratorState::Authenticated; + break; + } + default: + return; + } +} + +void +WifiTelemetrySourceWpa::on_dpp_configuration_received(const WpaEventArgs& /* args */) +{ + if (!trace_dpp_enrollee() || !is_dpp_exchange_in_progress()) + return; + + if (m_dpp_exchange->device_role != WifiDppDeviceRole::Enrollee) + return; + + auto enrollee = resolve_dpp_exchange(); + enrollee->state = WifiDppExchangeEnrolleeState::Provisioned; + complete_dpp_exchange_enrollee(enrollee); +} + +void +WifiTelemetrySourceWpa::on_dpp_configuration_sent(const WpaEventArgs& /* args */) +{ + if (!trace_dpp_configurator() || !is_dpp_exchange_in_progress()) + return; + + if (m_dpp_exchange->device_role != WifiDppDeviceRole::Configurator) + return; + + auto configurator = resolve_dpp_exchange(); + configurator->state = WifiDppExchangeConfiguratorState::Finished; + complete_dpp_exchange_configurator(configurator); +} + +void +WifiTelemetrySourceWpa::on_dpp_failure(const WpaEventArgs& args) +{ + if (!trace_dpp() || !is_dpp_exchange_in_progress()) + return; + + const auto& event = args.data(); + m_dpp_exchange->failure_type = event.failure_type; + m_dpp_exchange->failure_details = event.failure_details; + + switch (m_dpp_exchange->device_role) { + case WifiDppDeviceRole::Enrollee: { + auto enrollee = resolve_dpp_exchange(); + enrollee->state = WifiDppExchangeEnrolleeState::Terminated; + complete_dpp_exchange_enrollee(enrollee); + break; + } + case WifiDppDeviceRole::Configurator: { + auto configurator = resolve_dpp_exchange(); + configurator->state = WifiDppExchangeConfiguratorState::Terminated; + complete_dpp_exchange_configurator(configurator); + break; + } + default: + return; + } +} + +void +WifiTelemetrySourceWpa::on_dpp_frame_transmit_status(const WpaEventArgs& args) +{ + if (!trace_dpp_enrollee()) + return; + + const auto& event = args.data(); + bool is_success = (event.status == "SUCCESS"); + bool is_broadcast = is_wifi80211_broadcast_address(event.destination_bssid); + + if (is_broadcast) { // this indicates a chirp + if (!is_dpp_exchange_in_progress()) + m_dpp_exchange = std::make_shared(WifiDppRole::Responder); + + auto enrollee = resolve_dpp_exchange(); + if (is_success) + enrollee->chirp_frequencies.insert(event.frequency); + } +} diff --git a/src/lib/wifi_telemetry_source_ztpd.cxx b/src/lib/wifi_telemetry_source_ztpd.cxx new file mode 100644 index 0000000..71e3ea5 --- /dev/null +++ b/src/lib/wifi_telemetry_source_ztpd.cxx @@ -0,0 +1,56 @@ + +#include + +#include "wifi-telemetry/wifi_telemetry_source_ztpd.hpp" +#include "wifi_telemetry_trace_dpp.hpp" + +#define ZTP_DBUS_INTERFACE "com.microsoft.ztp1" +#define ZTP_DBUS_PATH "/com/microsoft/ztp1" + +WifiTelemetrySourceZtpd::WifiTelemetrySourceZtpd(const std::optional& interface) : + WifiTelemetrySource(interface, "ztpd"), + ProxyInterfaces(ZTP_DBUS_INTERFACE, ZTP_DBUS_PATH) +{} + +WifiTelemetrySourceZtpd::~WifiTelemetrySourceZtpd(void) +{} + +int +WifiTelemetrySourceZtpd::activate(const std::shared_ptr /* args */) +{ + registerProxy(); + return 0; +} + +void +WifiTelemetrySourceZtpd::deactivate(void) +{ + unregisterProxy(); +} + +void +WifiTelemetrySourceZtpd::onPropertiesChanged(const std::string& interfaceName, const std::map& changedProperties, const std::vector& /* invalidatedProperties */) +{ + if (interfaceName != ZTP_DBUS_INTERFACE) + return; + + const auto roles_it = changedProperties.find("Roles"); + if (roles_it == changedProperties.end()) + return; + + std::string roles{}; + const auto roles_changed = roles_it->second.get>(); + + if (!roles_changed.empty()) { + roles = std::accumulate( + /* start */ std::next(std::begin(roles_changed)), + /* end */ std::end(roles_changed), + /* init */ roles_changed.front(), + /* op */ [](std::string current, const std::string& next) + { + return std::move(current) + ',' + next; + }); + } + + trace_dpp_device_roles_changed(roles.c_str()); +} diff --git a/src/lib/wpa/CMakeLists.txt b/src/lib/wpa/CMakeLists.txt new file mode 100644 index 0000000..5315fc5 --- /dev/null +++ b/src/lib/wpa/CMakeLists.txt @@ -0,0 +1,39 @@ + +target_sources(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/wifi_telemetry_input_station_connection_attempt.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_bss.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_key_value_pair.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_response.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_response_parser.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_signal_poll.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_command_status.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_controller.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_association_rejected.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_authentication_rejected.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_connected.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_disconnected.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_auth_init_failure.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_auth_success.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_chirp_received.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_conf_received.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_conf_sent.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_failure.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_dpp_frame_transmit_status.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_handler.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_network_not_found.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_event_parser.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/wpa_state.cxx +) + +target_include_directories(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(wifi-telemetry + PRIVATE + Threads::Threads +) diff --git a/src/lib/wpa/wifi_telemetry_input_station_connection_attempt.cxx b/src/lib/wpa/wifi_telemetry_input_station_connection_attempt.cxx new file mode 100644 index 0000000..ebb2d54 --- /dev/null +++ b/src/lib/wpa/wifi_telemetry_input_station_connection_attempt.cxx @@ -0,0 +1,187 @@ + +#include + +#include "wifi-telemetry/wifi/wifi_80211_format_mac.hpp" +#include "wifi-telemetry/wpa/wifi_telemetry_input_station_connection_attempt.hpp" + +WifiTelemetryInputStationConnectionAttempt::WifiTelemetryInputStationConnectionAttempt( + const WifiConnectionAttemptResult result, + const std::optional bssid, + const std::shared_ptr signal_poll, + const std::shared_ptr status, + const WifiStatusCode status_code, + const WifiAuthenticationType authentication_type) : + m_result(result), + m_bssid(wifi_80211_mac_to_string(bssid.value_or(wifi_80211_any_address))), + m_signal_poll(signal_poll ? signal_poll : std::make_shared(0, 0, 0, 0)), + m_status(status ? status : std::make_shared()), + m_status_code(status_code), + m_authentication_type(WifiAuthenticationTypeToString(authentication_type)) +{ + // sanitize all fields used for telemetry event(s) to defaults + + if (!m_status->wifi_generation) + m_status->wifi_generation = 0; + + if (!m_status->key_mgmt) + m_status->key_mgmt = ""; + + if (!m_status->pairwise_cipher) + m_status->pairwise_cipher = ""; + + if (!m_status->group_cipher) + m_status->group_cipher = ""; + + if (!m_status->ssid) + m_status->ssid = ""; +} + +WifiTelemetryInputStationConnectionAttempt +WifiTelemetryInputStationConnectionAttempt::unspecified(void) +{ + return { + WifiConnectionAttemptResult::Unspecified, + std::nullopt, + nullptr, + nullptr, + WifiStatusCode::Success, + WifiAuthenticationType::Unknown + }; +} + +WifiTelemetryInputStationConnectionAttempt +WifiTelemetryInputStationConnectionAttempt::succeeded(const wifi_80211_mac& bssid, const std::shared_ptr signal_poll, const std::shared_ptr status) +{ + return { + WifiConnectionAttemptResult::Succeeded, + bssid, + signal_poll, + status, + WifiStatusCode::Success, + WifiAuthenticationType::Unknown + }; +} + +WifiTelemetryInputStationConnectionAttempt +WifiTelemetryInputStationConnectionAttempt::association_rejected(const std::optional& bssid, const WifiStatusCode status_code) +{ + return { + WifiConnectionAttemptResult::AssociationRejected, + bssid, + nullptr, + nullptr, + status_code, + WifiAuthenticationType::Unknown + }; +} + +WifiTelemetryInputStationConnectionAttempt +WifiTelemetryInputStationConnectionAttempt::authentication_rejected(const wifi_80211_mac& /* bssid */, const WifiStatusCode status_code, const WifiAuthenticationType authentication_type) +{ + return { + WifiConnectionAttemptResult::AuthenticationRejected, + std::nullopt, + nullptr, + nullptr, + status_code, + authentication_type + }; +} + +WifiTelemetryInputStationConnectionAttempt +WifiTelemetryInputStationConnectionAttempt::network_not_found(void) +{ + return { + WifiConnectionAttemptResult::NetworkNotFound, + std::nullopt, + nullptr, + nullptr, + WifiStatusCode::Success, + WifiAuthenticationType::Unknown + }; +} + +uint32_t +WifiTelemetryInputStationConnectionAttempt::result(void) const +{ + return uint32_t(m_result); +} + +const char* +WifiTelemetryInputStationConnectionAttempt::bssid(void) const +{ + return m_bssid.c_str(); +} + +const char* +WifiTelemetryInputStationConnectionAttempt::ssid(void) const +{ + return m_status->ssid.value().c_str(); +} + +int32_t +WifiTelemetryInputStationConnectionAttempt::noise(void) const +{ + return m_signal_poll->noise; +} + +int32_t +WifiTelemetryInputStationConnectionAttempt::rssi(void) const +{ + return m_signal_poll->rssi; +} + +int32_t +WifiTelemetryInputStationConnectionAttempt::link_speed(void) const +{ + return m_signal_poll->link_speed; +} + +uint32_t +WifiTelemetryInputStationConnectionAttempt::frequency(void) const +{ + return m_signal_poll->frequency; +} + +uint32_t +WifiTelemetryInputStationConnectionAttempt::wifi_generation(void) const +{ + return m_status->wifi_generation.value(); +} + +const char* +WifiTelemetryInputStationConnectionAttempt::key_mgmt(void) const +{ + return m_status->key_mgmt.value().c_str(); +} + +const char* +WifiTelemetryInputStationConnectionAttempt::pairwise_cipher(void) const +{ + return m_status->pairwise_cipher.value().c_str(); +} + +const char* +WifiTelemetryInputStationConnectionAttempt::group_cipher(void) const +{ + return m_status->group_cipher.value().c_str(); +} + +uint16_t +WifiTelemetryInputStationConnectionAttempt::status_code(void) const +{ + switch (m_result) { + case WifiConnectionAttemptResult::Succeeded: + case WifiConnectionAttemptResult::AssociationRejected: + case WifiConnectionAttemptResult::AuthenticationRejected: + return uint16_t(m_status_code); + default: + return std::numeric_limits::max(); + } +} + +const char* +WifiTelemetryInputStationConnectionAttempt::authentication_type(void) const +{ + return m_authentication_type.c_str(); +} diff --git a/src/lib/wpa/wpa_command.cxx b/src/lib/wpa/wpa_command.cxx new file mode 100644 index 0000000..3baa841 --- /dev/null +++ b/src/lib/wpa/wpa_command.cxx @@ -0,0 +1,15 @@ + +#include + +#include "wifi-telemetry/wpa/wpa_command.hpp" + +WpaCommand::WpaCommand(const std::string& name_) : + name(name_) +{} + +std::shared_ptr +WpaCommand::parse_response(const std::string_view payload) const +{ + std::unique_ptr parser = create_response_parser(payload); + return parser->parse(); +} diff --git a/src/lib/wpa/wpa_command_bss.cxx b/src/lib/wpa/wpa_command_bss.cxx new file mode 100644 index 0000000..0ab1244 --- /dev/null +++ b/src/lib/wpa/wpa_command_bss.cxx @@ -0,0 +1,57 @@ + +#include + +#include "wifi-telemetry/wifi/wifi_80211_format_mac.hpp" +#include "wifi-telemetry/wpa/wpa_command_bss.hpp" + +static constexpr char this_command_name[] = "BSS"; + +WpaCommandBss::WpaCommandBss(std::string bssid_) : + WpaCommand(this_command_name), + bssid(bssid_) +{ + std::stringstream ss(name); + ss << " " << bssid; + data = ss.str(); +} + +std::unique_ptr +WpaCommandBss::create_response_parser(const std::string_view payload) const +{ + return std::make_unique(payload); +} + +WpaCommandBssResponseParser::WpaCommandBssResponseParser(const std::string_view payload_) : + WpaCommandResponseParser(this_command_name, payload_, { + { "id=", WpaValueRequired }, + { "bssid=", WpaValueRequired }, + { "freq=", WpaValueRequired }, + { "beacon_int=", WpaValueRequired }, + { "capabilities=", WpaValueRequired }, + { "qual=", WpaValueRequired }, + { "noise=", WpaValueRequired }, + { "level=", WpaValueRequired }, + { "tsf=", WpaValueRequired }, + { "age=", WpaValueRequired }, + { "ie=", WpaValueRequired }, + { "flags=", WpaValueRequired }, + { "ssid=", WpaValueRequired }, + { "snr=", WpaValueRequired }, + { "est_throughput=", WpaValueRequired }, + { "update_idx=", WpaValueRequired }, + { "beacon_ie=", WpaValueRequired }, + }) +{} + +std::shared_ptr +WpaCommandBssResponseParser::parse_payload(void) const +{ + return nullptr; +} + +WpaCommandBss::WpaCommandBss(const wifi_80211_mac& bssid_) : + WpaCommandBss(wifi_80211_mac_to_string(bssid_)) +{} + +WpaCommandBssResponse::WpaCommandBssResponse() +{} diff --git a/src/lib/wpa/wpa_command_key_value_pair.cxx b/src/lib/wpa/wpa_command_key_value_pair.cxx new file mode 100644 index 0000000..bbb6c01 --- /dev/null +++ b/src/lib/wpa/wpa_command_key_value_pair.cxx @@ -0,0 +1,31 @@ + +#include + +#include "wifi-telemetry/wpa/wpa_command_key_value_pair.hpp" + +WpaCommandKeyValuePair::WpaCommandKeyValuePair(const char* key_, bool is_required_ = WpaValueOptional) : + key(key_), + key_length(strlen(key)), + value(std::nullopt), + is_required(is_required_) +{} + +const char* +WpaCommandKeyValuePair::operator()(void) const +{ + return value.value(); +} + +bool +WpaCommandKeyValuePair::resolve(const std::string_view input) +{ + value_pos = input.find(key); + + if (value_pos != input.npos) { + value_pos += key_length; + value = input.data() + value_pos; + return true; + } + + return false; +} diff --git a/src/lib/wpa/wpa_command_response.cxx b/src/lib/wpa/wpa_command_response.cxx new file mode 100644 index 0000000..7ef7818 --- /dev/null +++ b/src/lib/wpa/wpa_command_response.cxx @@ -0,0 +1,6 @@ + +#include "wifi-telemetry/wpa/wpa_command_response.hpp" + +WpaCommandResponseGeneric::WpaCommandResponseGeneric(const std::string payload_) : + payload(payload_) +{} diff --git a/src/lib/wpa/wpa_command_response_parser.cxx b/src/lib/wpa/wpa_command_response_parser.cxx new file mode 100644 index 0000000..bf4569d --- /dev/null +++ b/src/lib/wpa/wpa_command_response_parser.cxx @@ -0,0 +1,36 @@ + +#include + +#include "wifi-telemetry/wpa/wpa_command_response_parser.hpp" + +WpaCommandResponseParser::WpaCommandResponseParser(const std::string& command_name_, const std::string_view payload_, const std::initializer_list properties_) : + command_name(command_name_), + payload(payload_), + properties(properties_) +{} + +std::shared_ptr +WpaCommandResponseParser::parse(void) +{ + return resolve_properties() + ? parse_payload() + : nullptr; +} + +bool +WpaCommandResponseParser::resolve_properties(void) +{ + if (properties_resolved_result.has_value()) + return properties_resolved_result.value(); + + for (auto& property : properties) { + if (!property.resolve(payload) && property.is_required) { + std::cerr << "invalid " << command_name << " response (" << property.key << " not found)" << std::endl; + properties_resolved_result = false; + return false; + } + } + + properties_resolved_result = true; + return true; +} diff --git a/src/lib/wpa/wpa_command_signal_poll.cxx b/src/lib/wpa/wpa_command_signal_poll.cxx new file mode 100644 index 0000000..95dd15e --- /dev/null +++ b/src/lib/wpa/wpa_command_signal_poll.cxx @@ -0,0 +1,69 @@ + +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_command_signal_poll.hpp" + +static constexpr char this_command_name[] = "SIGNAL_POLL"; + +WpaCommandSignalPoll::WpaCommandSignalPoll(void) : + WpaCommand(this_command_name) +{ + data = name; +} + +std::unique_ptr +WpaCommandSignalPoll::create_response_parser(const std::string_view payload) const +{ + return std::make_unique(payload); +} + +WpaCommandSignalPollResponseParser::WpaCommandSignalPollResponseParser(const std::string_view payload_) : + WpaCommandResponseParser(this_command_name, payload_, { + { "RSSI=", WpaValueRequired }, + { "NOISE=", WpaValueRequired }, + { "LINKSPEED=", WpaValueRequired }, + { "FREQUENCY=", WpaValueRequired }, + { "CENTER_FRQ1=", WpaValueOptional }, + { "CENTER_FRQ2=", WpaValueOptional }, + { "WIDTH=", WpaValueOptional }, + { "AVG_RSSI=", WpaValueOptional }, + { "AVG_BEACON_RSSI=", WpaValueOptional }, + }) +{} + +std::shared_ptr +WpaCommandSignalPollResponseParser::parse_payload() const +{ + int32_t rssi = static_cast(strtol(properties[0](), nullptr, 10)); + int32_t noise = static_cast(strtol(properties[1](), nullptr, 10)); + int32_t link_speed = static_cast(strtol(properties[2](), nullptr, 10)); + uint32_t frequency = static_cast(strtoul(properties[3](), nullptr, 10)); + + std::shared_ptr response = std::make_shared(noise, rssi, link_speed, frequency); + + if (properties[4].value) + response->center_frequency_1 = static_cast(strtol(properties[4](), nullptr, 10)); + if (properties[5].value) + response->center_frequency_2 = static_cast(strtol(properties[5](), nullptr, 10)); + if (properties[6].value) { + std::size_t value_pos = properties[6].value_pos; + std::size_t value_pos_end = payload.find_first_of('\n', value_pos); + if (value_pos_end != payload.npos) + response->channel_width = payload.substr(value_pos, value_pos_end - value_pos); + } + if (properties[7].value) + response->rssi_average = static_cast(strtol(properties[7](), nullptr, 10)); + if (properties[8].value) + response->rssi_average_beacon = static_cast(strtol(properties[8](), nullptr, 10)); + + return response; +} + +WpaCommandSignalPollResponse::WpaCommandSignalPollResponse(int32_t noise_, int32_t rssi_, int32_t link_speed_, uint32_t frequency_) : + noise(noise_), + rssi(rssi_), + link_speed(link_speed_), + frequency(frequency_) +{} diff --git a/src/lib/wpa/wpa_command_status.cxx b/src/lib/wpa/wpa_command_status.cxx new file mode 100644 index 0000000..f6978d8 --- /dev/null +++ b/src/lib/wpa/wpa_command_status.cxx @@ -0,0 +1,83 @@ + +#include + +#include "wifi-telemetry/wifi/wifi_80211_format_mac.hpp" +#include "wifi-telemetry/wpa/wpa_command_status.hpp" + +static constexpr char this_command_name[] = "STATUS"; + +WpaCommandStatus::WpaCommandStatus(void) : + WpaCommand(this_command_name) +{ + data = name; +} + +std::unique_ptr +WpaCommandStatus::create_response_parser(const std::string_view payload) const +{ + return std::make_unique(payload); +} + +WpaCommandStatusResponseParser::WpaCommandStatusResponseParser(const std::string_view payload_) : + WpaCommandResponseParser(this_command_name, payload_, { + { "wpa_state=", WpaValueRequired }, + { "ssid=", WpaValueOptional }, + { "bssid=", WpaValueOptional }, + { "pairwise_cipher=", WpaValueOptional }, + { "group_cipher=", WpaValueOptional }, + { "key_mgmt=", WpaValueOptional }, + { "wifi_generation=", WpaValueOptional }, + { "freq=", WpaValueOptional }, + }) +{} + +std::shared_ptr +WpaCommandStatusResponseParser::parse_payload(void) const +{ + std::string state = properties[0](); + state.erase(state.find_first_of('\n')); + + auto response = std::make_shared(wpa_state_from_string(state.c_str())); + + if (properties[1].value) { + std::string ssid = properties[1](); + ssid.erase(ssid.find_first_of('\n')); + response->ssid = std::move(ssid); + } + + if (properties[2].value) { + std::string bssid = properties[2](); + bssid.erase(bssid.find_first_of('\n')); + response->bssid = wifi_80211_mac_from_string(bssid); + } + + if (properties[3].value) { + std::string pairwise_cipher = properties[3](); + pairwise_cipher.erase(pairwise_cipher.find_first_of('\n')); + response->pairwise_cipher = std::move(pairwise_cipher); + } + + if (properties[4].value) { + std::string group_cipher = properties[4](); + group_cipher.erase(group_cipher.find_first_of('\n')); + response->group_cipher = std::move(group_cipher); + } + + if (properties[5].value) { + std::string key_mgmt = properties[5](); + key_mgmt.erase(key_mgmt.find_first_of('\n')); + response->key_mgmt = std::move(key_mgmt); + } + + if (properties[6].value) + response->wifi_generation = static_cast(strtoul(properties[6](), nullptr, 10)); + + if (properties[7].value) + response->frequency = static_cast(strtoul(properties[7](), nullptr, 10)); + + return response; +} + +WpaCommandStatusResponse::WpaCommandStatusResponse(WpaState state_) : + state(state_) +{} diff --git a/src/lib/wpa/wpa_controller.cxx b/src/lib/wpa/wpa_controller.cxx new file mode 100644 index 0000000..d107c55 --- /dev/null +++ b/src/lib/wpa/wpa_controller.cxx @@ -0,0 +1,312 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_controller.hpp" +#include "wpa_event_parser.hpp" + +WpaController::WpaController(const std::string_view interface, WpaType type) : + WpaController(interface, type, WpaController::default_control_socket_path(type)) +{ +} + +WpaController::WpaController(const std::string_view interface, WpaType type, const std::string_view control_socket_path) : + m_type(type), + m_interface(interface), + m_ctrl_socket_path(control_socket_path) +{ + m_ctrl_event_thread = std::thread([this](void) + { + process_wpa_events(); + }); +} + +WpaController::~WpaController(void) +{ + m_terminate_pending = true; + + if (m_ctrl_event_fd_stop != -1) { + uint64_t value = 1; + ssize_t num_written = write(m_ctrl_event_fd_stop, &value, sizeof value); + if (num_written != sizeof value) { + int ret = (num_written < 0) ? errno : -ERANGE; + std::cerr << "failed to write exit signal descriptor value (" << ret << ")" << std::endl; + } + } + + auto lock_exclusive = std::scoped_lock(m_ctrl_command_gate); + if (m_ctrl_command) { + wpa_ctrl_close(m_ctrl_command); + m_ctrl_command = nullptr; + } + + m_ctrl_event_thread.join(); +} + +/** + * @brief Process a pending wpa event. + */ +void +WpaController::process_wpa_event_next(void) +{ + auto timestamp = std::chrono::system_clock::now(); + char buffer[WpaController::c_wpa_event_size_max]; + size_t buffer_length = sizeof buffer - 1; + + int ret = wpa_ctrl_recv(m_ctrl_event, buffer, &buffer_length); + if (ret < 0) { + std::cerr << "failed to receive message from wpa control socket" << std::endl; + return; + } + + buffer[buffer_length] = '\0'; + + std::shared_ptr event = WpaEventParser::parse(buffer); + if (!event) + return; + + // Make a copy of the event subscribers to minimize the time holding the + // lock. This is safe since the validity of each subscriber will later be + // attempted to be promoted to a full shared_ptr prior to using it. + decltype(m_event_subscribers) event_subscribers; + { + auto lock = std::scoped_lock(m_event_subscribers_gate); + event_subscribers = m_event_subscribers; + } + + for (const auto& [token, handler_weak] : event_subscribers) { + std::shared_ptr handler = handler_weak.lock(); + if (!handler) + continue; + + switch (event->type) { + case WpaEventType::Connected: + handler->on_connected({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::Disconnected: + handler->on_disconnected({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::AssociationRejected: + handler->on_association_rejected({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::AuthenticationRejected: + handler->on_authentication_rejected({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::NetworkNotFound: + handler->on_network_not_found({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppAuthenticationInitFailure: + handler->on_dpp_authentication_init_failure({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppAuthenticationSucceeded: + handler->on_dpp_authentication_succeeded({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppConfigurationReceived: + handler->on_dpp_configuration_received({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppConfigurationSent: + handler->on_dpp_configuration_sent({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppFailure: + handler->on_dpp_failure({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppChirpReceived: + handler->on_dpp_chirp_received({ timestamp, std::dynamic_pointer_cast(event) }); + break; + case WpaEventType::DppFrameTransmitStatus: + handler->on_dpp_frame_transmit_status({ timestamp, std::dynamic_pointer_cast(event) }); + break; + } + } +} + +void +WpaController::process_wpa_events(void) +{ + static constexpr int epoll_events_max = 2; + + int ret; + int fd_efd = -1; + int fd_wpa = -1; + bool terminate = false; + struct wpa_ctrl* ctrl = nullptr; + struct epoll_event events[epoll_events_max] = {}; + struct epoll_event* event_efd = &events[0]; + struct epoll_event* event_wpa = &events[1]; + const std::filesystem::path path = m_ctrl_socket_path / m_interface; + + int fd_epoll = epoll_create1(0); + if (fd_epoll < 0) { + ret = errno; + std::cerr << "failed to create epoll instance for wpa event listener (" << ret << ")" << std::endl; + return; + } + + // configure eventfd descriptor for thread stop signaling. + fd_efd = eventfd(0, 0); + if (fd_efd == -1) { + ret = errno; + std::cerr << "failed to create stop event fd (" << ret << ")" << std::endl; + goto cleanup; + } + + event_efd->events = EPOLLIN; + event_efd->data.fd = fd_efd; + + ret = epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_efd, event_efd); + if (ret < 0) { + ret = errno; + std::cerr << "failed to register epoll handler for event stop signaling (" << ret << ")" << std::endl; + goto cleanup; + } + + // configure control socket descriptor for wpa event signaling. + ctrl = wpa_ctrl_open(path.c_str()); + if (!ctrl) { + std::cerr << "failed to open wpa control socket " << path << std::endl; + goto cleanup; + } + + fd_wpa = wpa_ctrl_get_fd(ctrl); + event_wpa->events = EPOLLIN; + event_wpa->data.fd = fd_wpa; + + ret = epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_wpa, event_wpa); + if (ret < 0) { + ret = errno; + std::cerr << "failed to register epoll handler for wpa event listener (" << ret << ")" << std::endl; + goto cleanup; + } + + // attach to wpa event stream + ret = wpa_ctrl_attach(ctrl); + if (ret < 0) { + std::cerr << "failed to register event monitor on wpa control socket (" << ret << ")" << std::endl; + goto cleanup; + } + + m_ctrl_event = ctrl; + m_ctrl_event_fd_stop = fd_efd; + + while (!m_terminate_pending && !terminate) { + // wait for at least one event fd to be signaled + int num_events = epoll_wait(fd_epoll, events, epoll_events_max, -1); + if (num_events < 0) { + ret = errno; + std::cerr << "failed to wait on epoll event (" << ret << ")" << std::endl; + continue; + } else if (num_events == 0) { + continue; + } + + // determine which fd(s) were signaled and handle them. + for (int i = 0; i < epoll_events_max; i++) { + if (events[i].data.fd == fd_efd) { + uint64_t value; + ssize_t num_read = read(fd_efd, &value, sizeof value); + if (num_read != sizeof value) { + ret = errno; + std::cerr << "failed to read exit signal descriptor (" << ret << ")" << std::endl; + continue; + } else if (value > 0) { + terminate = true; + } + } else if (events[i].data.fd == fd_wpa) { + while (wpa_ctrl_pending(ctrl) > 0) + process_wpa_event_next(); + } + } + } + +cleanup: + if (ctrl) { + wpa_ctrl_detach(ctrl); + wpa_ctrl_close(ctrl); + } + + if (fd_efd != -1) + close(fd_efd); + + close(fd_epoll); + + m_ctrl_event_fd_stop = -1; + m_ctrl_event = nullptr; +} + +WpaEventToken +WpaController::register_for_events(std::weak_ptr handler) +{ + auto lock = std::scoped_lock(m_event_subscribers_gate); + WpaEventToken token = m_event_token_next++; + m_event_subscribers[token] = handler; + + return token; +} + +void +WpaController::unregister_for_events(WpaEventToken token) +{ + auto lock = std::scoped_lock(m_event_subscribers_gate); + m_event_subscribers.erase(token); +} + +struct wpa_ctrl* +WpaController::get_command_control_socket(void) +{ + const std::filesystem::path path = m_ctrl_socket_path / m_interface; + + // Check if the socket is already available. + { + auto lock_shared = std::shared_lock(m_ctrl_command_gate); + if (m_ctrl_command) + return m_ctrl_command; + } + + // Check if socket was updated between releasing shared lock and acquiring exclusive lock. + auto lock_exclusive = std::scoped_lock(m_ctrl_command_gate); + if (m_ctrl_command) + return m_ctrl_command; + + struct wpa_ctrl* ctrl = wpa_ctrl_open(path.c_str()); + if (!ctrl) { + std::cerr << "failed to open wpa control socket " << path << std::endl; + return nullptr; + } + + m_ctrl_command = ctrl; + return m_ctrl_command; +} + +std::shared_ptr +WpaController::send_command(const WpaCommand& command) +{ + struct wpa_ctrl* ctrl = get_command_control_socket(); + if (!ctrl) { + std::cerr << "failed to obtain wpa command control socket" << std::endl; + return nullptr; + } + + std::array reply; + std::size_t reply_length = reply.size(); + + int ret = wpa_ctrl_request(ctrl, command.data.c_str(), command.data.length(), reply.data(), &reply_length, nullptr); + switch (ret) { + case 0: + return command.parse_response(std::string_view(reply.data(), reply_length)); + case -2: + std::cerr << "failed to send/receive wpa command " << command.name << "(timeout)" << std::endl; + return nullptr; + case -1: + std::cerr << "failed to send/receive wpa command " << command.name << std::endl; + return nullptr; + default: + std::cerr << "failed to send/receive wpa command " << command.name << "(unspecified error " << ret << ")" << std::endl; + return nullptr; + } +} diff --git a/src/lib/wpa/wpa_event.cxx b/src/lib/wpa/wpa_event.cxx new file mode 100644 index 0000000..24040a2 --- /dev/null +++ b/src/lib/wpa/wpa_event.cxx @@ -0,0 +1,9 @@ + +#include +#include + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +WpaEvent::WpaEvent(WpaEventType type_) : + type(type_) +{} diff --git a/src/lib/wpa/wpa_event_association_rejected.cxx b/src/lib/wpa/wpa_event_association_rejected.cxx new file mode 100644 index 0000000..e7a319c --- /dev/null +++ b/src/lib/wpa/wpa_event_association_rejected.cxx @@ -0,0 +1,15 @@ + +#include "wifi-telemetry/wpa/wpa_event_association_rejected.hpp" + +WpaEventAssociationRejected::WpaEventAssociationRejected(WifiStatusCode status_code_) : + WpaEvent(WpaEventType::AssociationRejected), + status_code(status_code_) +{ +} + +WpaEventAssociationRejected::WpaEventAssociationRejected(WifiStatusCode status_code_, const wifi_80211_mac& bssid_) : + WpaEvent(WpaEventType::AssociationRejected), + status_code(status_code_), + bssid(bssid_) +{ +} diff --git a/src/lib/wpa/wpa_event_authentication_rejected.cxx b/src/lib/wpa/wpa_event_authentication_rejected.cxx new file mode 100644 index 0000000..14ee2c9 --- /dev/null +++ b/src/lib/wpa/wpa_event_authentication_rejected.cxx @@ -0,0 +1,10 @@ + +#include "wifi-telemetry/wpa/wpa_event_authentication_rejected.hpp" + +WpaEventAuthenticationRejected::WpaEventAuthenticationRejected(const wifi_80211_mac& bssid_, WifiStatusCode status_code_, WifiAuthenticationType authentication_type_) : + WpaEvent(WpaEventType::AuthenticationRejected), + bssid(bssid_), + status_code(status_code_), + authentication_type(authentication_type_) +{ +} diff --git a/src/lib/wpa/wpa_event_connected.cxx b/src/lib/wpa/wpa_event_connected.cxx new file mode 100644 index 0000000..b5e51f6 --- /dev/null +++ b/src/lib/wpa/wpa_event_connected.cxx @@ -0,0 +1,7 @@ + +#include "wifi-telemetry/wpa/wpa_event_connected.hpp" + +WpaEventConnected::WpaEventConnected(const wifi_80211_mac& bssid_) : + WpaEvent(WpaEventType::Connected), + bssid(bssid_) +{} diff --git a/src/lib/wpa/wpa_event_disconnected.cxx b/src/lib/wpa/wpa_event_disconnected.cxx new file mode 100644 index 0000000..0058b2c --- /dev/null +++ b/src/lib/wpa/wpa_event_disconnected.cxx @@ -0,0 +1,14 @@ + +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_event_disconnected.hpp" + +WpaEventDisconnected::WpaEventDisconnected(const wifi_80211_mac& bssid_, WifiDeauthenticationReasonCode reason_code_, bool locally_generated_) : + WpaEvent(WpaEventType::Disconnected), + bssid(bssid_), + reason_code(reason_code_), + locally_generated(locally_generated_) +{ +} diff --git a/src/lib/wpa/wpa_event_dpp_auth_init_failure.cxx b/src/lib/wpa/wpa_event_dpp_auth_init_failure.cxx new file mode 100644 index 0000000..b87ddc9 --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_auth_init_failure.cxx @@ -0,0 +1,6 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_auth_init_failure.hpp" + +WpaEventDppAuthenticationInitFailure::WpaEventDppAuthenticationInitFailure(void) : + WpaEventDppFailure(WpaEventType::DppAuthenticationInitFailure, WifiDppFailureType::AuthenticationTimeout) +{} diff --git a/src/lib/wpa/wpa_event_dpp_auth_success.cxx b/src/lib/wpa/wpa_event_dpp_auth_success.cxx new file mode 100644 index 0000000..872fe8e --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_auth_success.cxx @@ -0,0 +1,7 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_auth_success.hpp" + +WpaEventDppAuthenticationSucceeded::WpaEventDppAuthenticationSucceeded(int initiator_) : + WpaEvent(WpaEventType::DppAuthenticationSucceeded), + initiator(initiator_) +{} diff --git a/src/lib/wpa/wpa_event_dpp_chirp_received.cxx b/src/lib/wpa/wpa_event_dpp_chirp_received.cxx new file mode 100644 index 0000000..34670df --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_chirp_received.cxx @@ -0,0 +1,14 @@ + +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_event_dpp_chirp_received.hpp" + +WpaEventDppChirpReceived::WpaEventDppChirpReceived(int32_t id_, const wifi_80211_mac& bssid_, uint32_t frequency_) : + WpaEvent(WpaEventType::DppChirpReceived), + id(id_), + bssid(bssid_), + frequency(frequency_) +{ +} diff --git a/src/lib/wpa/wpa_event_dpp_conf_received.cxx b/src/lib/wpa/wpa_event_dpp_conf_received.cxx new file mode 100644 index 0000000..da6ed38 --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_conf_received.cxx @@ -0,0 +1,6 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_conf_received.hpp" + +WpaEventDppConfigurationReceived::WpaEventDppConfigurationReceived(void) : + WpaEvent(WpaEventType::DppConfigurationReceived) +{} diff --git a/src/lib/wpa/wpa_event_dpp_conf_sent.cxx b/src/lib/wpa/wpa_event_dpp_conf_sent.cxx new file mode 100644 index 0000000..7a72cd3 --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_conf_sent.cxx @@ -0,0 +1,6 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_conf_sent.hpp" + +WpaEventDppConfigurationSent::WpaEventDppConfigurationSent(void) : + WpaEvent(WpaEventType::DppConfigurationSent) +{} diff --git a/src/lib/wpa/wpa_event_dpp_failure.cxx b/src/lib/wpa/wpa_event_dpp_failure.cxx new file mode 100644 index 0000000..70df95e --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_failure.cxx @@ -0,0 +1,16 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_failure.hpp" + +WpaEventDppFailure::WpaEventDppFailure(WpaEventType type_, WifiDppFailureType failure_type_, const std::string& failure_details_) : + WpaEvent(type_), + failure_type(failure_type_), + failure_details(failure_details_) +{} + +WpaEventDppFailure::WpaEventDppFailure(WifiDppFailureType failure_type_, const std::string& failure_details_) : + WpaEventDppFailure(WpaEventType::DppFailure, failure_type_, failure_details_) +{} + +WpaEventDppFailure::WpaEventDppFailure(const std::string& failure_details_) : + WpaEventDppFailure(WifiDppFailureType::Unspecified, failure_details_) +{} diff --git a/src/lib/wpa/wpa_event_dpp_frame_transmit_status.cxx b/src/lib/wpa/wpa_event_dpp_frame_transmit_status.cxx new file mode 100644 index 0000000..f27b826 --- /dev/null +++ b/src/lib/wpa/wpa_event_dpp_frame_transmit_status.cxx @@ -0,0 +1,9 @@ + +#include "wifi-telemetry/wpa/wpa_event_dpp_frame_transmit_status.hpp" + +WpaEventDppFrameTransmitStatus::WpaEventDppFrameTransmitStatus(const wifi_80211_mac& destination_bssid_, unsigned int frequency_, const std::string& status_) : + WpaEvent(WpaEventType::DppFrameTransmitStatus), + destination_bssid(destination_bssid_), + frequency(frequency_), + status(status_) +{} diff --git a/src/lib/wpa/wpa_event_handler.cxx b/src/lib/wpa/wpa_event_handler.cxx new file mode 100644 index 0000000..a4be927 --- /dev/null +++ b/src/lib/wpa/wpa_event_handler.cxx @@ -0,0 +1,50 @@ + +#include "wifi-telemetry/wpa/wpa_event_handler.hpp" + +void +WpaEventHandler::on_connected(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_disconnected(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_association_rejected(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_authentication_rejected(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_network_not_found(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_authentication_init_failure(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_authentication_succeeded(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_configuration_received(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_configuration_sent(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_failure(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_chirp_received(const WpaEventArgs& /* args */) +{} + +void +WpaEventHandler::on_dpp_frame_transmit_status(const WpaEventArgs& /* args */) +{} diff --git a/src/lib/wpa/wpa_event_network_not_found.cxx b/src/lib/wpa/wpa_event_network_not_found.cxx new file mode 100644 index 0000000..2f0bb1f --- /dev/null +++ b/src/lib/wpa/wpa_event_network_not_found.cxx @@ -0,0 +1,7 @@ + +#include "wifi-telemetry/wpa/wpa_event_network_not_found.hpp" + +WpaEventNetworkNotFound::WpaEventNetworkNotFound(void) : + WpaEvent(WpaEventType::NetworkNotFound) +{ +} diff --git a/src/lib/wpa/wpa_event_parser.cxx b/src/lib/wpa/wpa_event_parser.cxx new file mode 100644 index 0000000..b9a9dc3 --- /dev/null +++ b/src/lib/wpa/wpa_event_parser.cxx @@ -0,0 +1,253 @@ + +#include +#include +#include +#include + +#include "wifi-telemetry/wpa/wpa_events.hpp" +#include "wpa_event_parser.hpp" + +static std::shared_ptr +parse_event_connected(const std::string_view message) +{ + wifi_80211_mac bssid; + + int ret = std::sscanf(message.data(), "- Connection to " WIFI_80211_MAC_FMT, STR_2_WIFI80211_MAC(bssid)); + if (ret < 0) { + std::cerr << "failed to parse wpa connected event message (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(bssid); +} + +static std::shared_ptr +parse_event_disconnected(const std::string_view message) +{ + static const char *fmt_without_locally_generated = "bssid=" WIFI_80211_MAC_FMT " reason=%d"; + static const char *fmt_with_locally_generated = "bssid=" WIFI_80211_MAC_FMT " reason=%d locally_generated=%1d"; + + int ret; + int reason_code; + int locally_generated; + wifi_80211_mac bssid; + + ret = std::sscanf(message.data(), fmt_with_locally_generated, STR_2_WIFI80211_MAC(bssid), &reason_code, &locally_generated); + if (ret != 8) { + locally_generated = 0; + ret = std::sscanf(message.data(), fmt_without_locally_generated, STR_2_WIFI80211_MAC(bssid), &reason_code); + if (ret != 7) { + std::cerr << "failed to parse wpa disconnected event message (invalid format)" << std::endl; + return nullptr; + } + } + + // TODO: below cast to WifiDeauthenticationReasonCode should really be checked for validity. + return std::make_shared(bssid, static_cast(reason_code), !!locally_generated); +} + +static std::shared_ptr +parse_event_association_rejected(const std::string_view message) +{ + int ret; + uint32_t status_code; + wifi_80211_mac bssid; + + ret = std::sscanf(message.data(), "bssid=" WIFI_80211_MAC_FMT " status_code=%u", STR_2_WIFI80211_MAC(bssid), &status_code); + if (ret != 7) { + ret = std::sscanf(message.data(), "status_code=%u", &status_code); + if (ret != 1) { + std::cerr << "failed to parse wpa association rejected event (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(WifiStatusCode(status_code)); + } + + return std::make_shared(WifiStatusCode(status_code), bssid); +} + +static std::shared_ptr +parse_event_authentication_rejected(const std::string_view message) +{ + int ret; + uint32_t auth_type = 0; + uint32_t status_code = 0; + wifi_80211_mac bssid = wifi_80211_any_address; + + ret = std::sscanf(message.data(), WIFI_80211_MAC_FMT " auth_type=%u status_code=%u", STR_2_WIFI80211_MAC(bssid), &status_code, &auth_type); + if (ret != 8) { + std::cerr << "failed to parse wpa authentication rejected event (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(bssid, WifiStatusCode(status_code), WifiAuthenticationType(auth_type)); +} + +static std::shared_ptr +parse_event_network_not_found(const std::string_view /* message */) +{ + return std::make_shared(); +} + +static std::shared_ptr +parse_event_dpp_auth_success(const std::string_view message) +{ + int initiator; + int ret = std::sscanf(message.data(), "init=%d", &initiator); + if (ret < 0) { + std::cerr << "failed to parse wpa dpp auth success event message (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(initiator); +} + +static std::shared_ptr +parse_event_dpp_conf_received(const std::string_view /* message */) +{ + return std::make_shared(); +} + +static std::shared_ptr +parse_event_dpp_conf_sent(const std::string_view /* message */) +{ + return std::make_shared(); +} + +static std::shared_ptr +parse_event_dpp_fail(const std::string_view message) +{ + const std::string details = message.data(); + return std::make_shared(details); +} + +static std::shared_ptr +parse_event_dpp_auth_init_fail(const std::string_view /* message */) +{ + return std::make_shared(); +} + +static std::shared_ptr +parse_event_dpp_chirp_rx(const std::string_view message) +{ + int id; + unsigned frequency; + wifi_80211_mac bssid; + + int ret = std::sscanf(message.data(), "id=%d src=" WIFI_80211_MAC_FMT " freq=%u hash=%*s", + &id, STR_2_WIFI80211_MAC(bssid), &frequency); + if (ret < 0) { + std::cerr << "failed to parse wpa dpp chirp rx event message (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(id, bssid, frequency); +} + +static std::shared_ptr +parse_event_dpp_tx_status(const std::string_view message) +{ + char status[128]; + unsigned frequency; + wifi_80211_mac bssid; + + int ret = std::sscanf(message.data(), "dst=" WIFI_80211_MAC_FMT " freq=%u result=%127s", + STR_2_WIFI80211_MAC(bssid), &frequency, status); + if (ret < 0) { + std::cerr << "failed to parse wpa dpp tx status event message (invalid format)" << std::endl; + return nullptr; + } + + return std::make_shared(bssid, frequency, status); +} + +typedef std::function(const std::string_view message)> wpa_event_parser; + +struct WpaEventMessageParser +{ + const std::string_view prefix; + std::function(const std::string_view message)> parse; +}; + +static const struct WpaEventMessageParser events[] = +{ + { + WPA_EVENT_CONNECTED, + &parse_event_connected + }, + { + WPA_EVENT_DISCONNECTED, + &parse_event_disconnected + }, + { + WPA_EVENT_ASSOC_REJECT, + &parse_event_association_rejected + }, + { + WPA_EVENT_AUTH_REJECT, + &parse_event_authentication_rejected + }, + { + WPA_EVENT_NETWORK_NOT_FOUND, + &parse_event_network_not_found + }, + { + DPP_EVENT_AUTH_SUCCESS, + &parse_event_dpp_auth_success + }, + { + DPP_EVENT_CONF_RECEIVED, + &parse_event_dpp_conf_received + }, + { + DPP_EVENT_CONF_SENT, + &parse_event_dpp_conf_sent + }, + { + DPP_EVENT_FAIL, + &parse_event_dpp_fail + }, + { + DPP_EVENT_AUTH_INIT_FAILED, + &parse_event_dpp_auth_init_fail + }, + { + DPP_EVENT_CHIRP_RX, + &parse_event_dpp_chirp_rx + }, + { + DPP_EVENT_TX_STATUS, + &parse_event_dpp_tx_status + } +}; + +std::shared_ptr +WpaEventParser::parse(const char *buffer) +{ + std::string_view message(buffer); + + std::size_t priority_pos = message.find_first_of(">"); + if (priority_pos == std::string_view::npos) { + std::cerr << "invalid wpa message (mising prefix)" << std::endl; + return nullptr; + } + + uint32_t priority; + if (std::sscanf(message.data(), "<%" PRIu32 ">", &priority) != 1) { + std::cerr << "unable to parse wpa message priority" << std::endl; + return nullptr; + } + + message.remove_prefix(priority_pos + 1); + + for (const auto& event : events) { + std::size_t name_pos = message.find(event.prefix); + if (name_pos != message.npos) { + message.remove_prefix(name_pos + event.prefix.length()); + return event.parse(message); + } + } + + return nullptr; +} diff --git a/src/lib/wpa/wpa_event_parser.hpp b/src/lib/wpa/wpa_event_parser.hpp new file mode 100644 index 0000000..9f0772e --- /dev/null +++ b/src/lib/wpa/wpa_event_parser.hpp @@ -0,0 +1,17 @@ + +#ifndef __WPA_EVENT_PARSER_HPP__ +#define __WPA_EVENT_PARSER_HPP__ + +#include +#include + +#include "wifi-telemetry/wpa/wpa_event.hpp" + +class WpaEventParser +{ +public: + static std::shared_ptr + parse(const char *buffer); +}; + +#endif //__WPA_EVENT_PARSER_HPP__ diff --git a/src/lib/wpa/wpa_state.cxx b/src/lib/wpa/wpa_state.cxx new file mode 100644 index 0000000..34241a8 --- /dev/null +++ b/src/lib/wpa/wpa_state.cxx @@ -0,0 +1,31 @@ + +#include + +#include "wifi-telemetry/wpa/wpa_state.hpp" + +WpaState +wpa_state_from_string(const char *state) +{ + if (strcmp(state, "DISCONNECTED") == 0) + return WpaState::Disconnected; + else if (strcmp(state, "INACTIVE") == 0) + return WpaState::Inactive; + else if (strcmp(state, "INTERFACE_DISABLED") == 0) + return WpaState::InterfaceDisabled; + else if (strcmp(state, "SCANNING") == 0) + return WpaState::Scanning; + else if (strcmp(state, "AUTHENTICATING") == 0) + return WpaState::Authenticating; + else if (strcmp(state, "ASSOCIATING") == 0) + return WpaState::Associating; + else if (strcmp(state, "ASSOCIATED") == 0) + return WpaState::Associated; + else if (strcmp(state, "4WAY_HANDSHAKE") == 0) + return WpaState::FourWayHandshake; + else if (strcmp(state, "GROUP_HANDSHAKE") == 0) + return WpaState::GroupHandshake; + else if (strcmp(state, "COMPLETED") == 0) + return WpaState::Completed; + else + return WpaState::Unknown; +} diff --git a/src/lib/ztpd/CMakeLists.txt b/src/lib/ztpd/CMakeLists.txt new file mode 100644 index 0000000..045fbee --- /dev/null +++ b/src/lib/ztpd/CMakeLists.txt @@ -0,0 +1,23 @@ + +target_include_directories(wifi-telemetry + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (BUILD_SDBUS_CPP_EXTERNAL) + add_dependencies(wifi-telemetry sdbus-cpp) + add_library(sdbus-cpp-external SHARED IMPORTED) + set_target_properties(sdbus-cpp-external PROPERTIES + IMPORTED_LOCATION ${SDBUS_CPP_EXTERNAL} + ) + + target_link_libraries(wifi-telemetry + PRIVATE + sdbus-cpp-external + ) +else() + target_link_libraries(wifi-telemetry + PRIVATE + SDBusCpp::sdbus-c++ + ) +endif()