From d6e984de304609f2694cd2232ab04e3b4681f133 Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:03:24 +0100 Subject: [PATCH 1/7] don't set the c++ standard in the .cmake file --- CMakeLists.txt | 1 + WBLib.cmake | 3 --- run_clang_format.sh | 32 ++++++++++++++++++++++---------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a18c09d..fe29b188 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.16.3) project(Wifibroadcast) +cmake_minimum_required(VERSION 3.16.3) set(CMAKE_CXX_STANDARD 17) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-address-use-after-scope -fsanitize=address") diff --git a/WBLib.cmake b/WBLib.cmake index 98779548..9072759f 100644 --- a/WBLib.cmake +++ b/WBLib.cmake @@ -2,9 +2,6 @@ # Defines WB_TARGET_LINK_LIBRARIES # which can then be used to integrate wifibroadcast into your project -cmake_minimum_required(VERSION 3.16.3) -set(CMAKE_CXX_STANDARD 17) - option(WB_ENABLE_SIMD_OPTIMIZATIONS "Enable NEON on ARM and SSSE3 on X86 if the platform/compiler supports it" ON) option(WB_USE_SPDLOG_EXTERNALLY "Do not find and link to spdlog installed on system" ON) #if(WIFIBROADCAST_LIBRARIES_ALREADY_BUILD) diff --git a/run_clang_format.sh b/run_clang_format.sh index 6f27c26c..82407ab3 100755 --- a/run_clang_format.sh +++ b/run_clang_format.sh @@ -1,24 +1,36 @@ #!/bin/bash -# From https://stackoverflow.com/a/65988393 +# OpenHD clang-format checking. +# Step1: Generate list of all .cpp / .h / .hpp files of this project +# (excluding subdirectories) +# Step 2: Run clang-format + + +function append_all_sources_headers() { + # We use .h, .cpp and .hpp in OpenHD + TMP_FILE_LIST="$(find "$1" | grep -E ".*(\.cpp|\.h|\.hpp)$")" + #TMP_FILE_LIST+='\n' + FILE_LIST+=$'\n' + FILE_LIST+=$TMP_FILE_LIST +} + THIS_PATH="$(realpath "$0")" THIS_DIR="$(dirname "$THIS_PATH")" -EXCLUDED_DIRECTORIES="external" -# Find all files in THIS_DIR which end in .ino, .cpp, etc., as specified -# in the regular expression just below -FILE_LIST="$(find "$THIS_DIR/src" -not -path "$THIS_DIR/src/external/*" | grep -E ".*(\.ino|\.cpp|\.c|\.h|\.hpp|\.hh)$")" +append_all_sources_headers "$THIS_DIR/wifibroadcast/inc" -echo -e "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\"" +echo "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\"" # Format each file. # - NB: do NOT put quotes around `$FILE_LIST` below or else the `clang-format` command will # mistakenly see the entire blob of newline-separated file names as a SINGLE file name instead # of as a new-line separated list of *many* file names! -#clang-format --verbose -i --style=file $FILE_LIST +clang-format --dry-run --Werror --verbose -i --style=file $FILE_LIST -# Create a directory where we can build with ninja & generate the list of files for clang tidy -cmake -G Ninja -S . -B build_clang_format -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -#clang-tidy -format-style=file -p build_clang_format $FILE_LIST \ No newline at end of file +if [ "$?" -eq "0" ]; then + echo "Everything formatted correctly" +else + echo "There are formatting errors ! Please fix first." +fi \ No newline at end of file From d74c0f508d80d244f7d440fe472c1abc476ea6b2 Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:49:13 +0100 Subject: [PATCH 2/7] apply clang-format. for this, we need a slightly different folder structure --- CMakeLists.txt | 30 +- run_clang_format.sh | 35 +- .../HelperSources/BlockSizeHelper.hpp | 100 -- .../HelperSources/DummyStreamGenerator.hpp | 80 -- .../HelperSources/EmulatedPacketDrop.hpp | 55 - wifibroadcast/HelperSources/Rates.hpp | 77 -- wifibroadcast/HelperSources/SocketHelper.hpp | 340 ------ wifibroadcast/HelperSources/StringHelper.hpp | 126 --- .../HelperSources/UINT16SeqNrHelper.hpp | 133 --- .../HelperSources/UINT64SeqNrHelper.hpp | 139 --- wifibroadcast/Ieee80211Header.hpp | 248 ----- WBLib.cmake => wifibroadcast/WBLib.cmake | 57 +- wifibroadcast/WBStreamRx.cpp | 164 --- wifibroadcast/WBStreamTx.cpp | 277 ----- wifibroadcast/WBTxRx.cpp | 862 ---------------- wifibroadcast/WBVideoStreamTx.cpp | 109 -- wifibroadcast/WBVideoStreamTx.h | 73 -- wifibroadcast/WiFiCard.h | 38 - {cmake => wifibroadcast/cmake}/FindPCAP.cmake | 0 .../cmake}/FindSodium.cmake | 0 wifibroadcast/dummy_link/DummyLink.cpp | 152 --- wifibroadcast/dummy_link/DummyLink.h | 53 - wifibroadcast/encryption/Encryption.cpp | 62 -- wifibroadcast/encryption/KeyPair.h | 44 - .../executables}/benchmark.cpp | 16 +- .../executables}/example_hello.cpp | 8 +- .../executables}/example_pollute.cpp | 10 +- .../executables}/example_udp.cpp | 16 +- .../executables}/injection_rate_test.cpp | 8 +- .../executables}/socket_helper_test.cpp | 2 +- .../executables}/test_dummy_link.cpp | 3 +- .../executables}/test_listen.cpp | 4 +- .../executables}/test_queue.cpp | 6 +- .../executables}/test_txrx.cpp | 8 +- .../executables}/udp_generator_validator.cpp | 8 +- .../executables}/udp_packet_drop_util.cpp | 4 +- .../executables}/unit_test.cpp | 20 +- .../executables}/wfb_keygen.cpp | 4 +- wifibroadcast/pcap_helper.hpp | 97 -- wifibroadcast/radiotap/RSSIAccumulator.hpp | 96 -- wifibroadcast/radiotap/RadiotapHeaderRx.hpp | 206 ---- .../radiotap/RadiotapRxRfAggregator.cpp | 80 -- .../radiotap/SignalQualityAccumulator.hpp | 53 - wifibroadcast/{ => src}/FunkyQueue.cpp | 0 wifibroadcast/{ => src}/FunkyQueue.h | 63 +- .../{ => src}/HelperSources/Benchmark.hpp | 76 +- .../src/HelperSources/BlockSizeHelper.hpp | 109 ++ .../HelperSources/DummyStreamGenerator.hpp | 86 ++ .../src/HelperSources/EmulatedPacketDrop.hpp | 56 + .../{ => src}/HelperSources/Helper.hpp | 161 +-- .../{ => src}/HelperSources/README.md | 0 .../HelperSources/RandomBufferPot.hpp | 49 +- wifibroadcast/src/HelperSources/Rates.hpp | 75 ++ .../HelperSources/SchedulingHelper.hpp | 35 +- .../HelperSources/SequenceNumberDebugger.hpp | 36 +- .../src/HelperSources/SocketHelper.hpp | 402 ++++++++ .../src/HelperSources/StringHelper.hpp | 127 +++ .../{ => src}/HelperSources/TimeHelper.hpp | 332 +++--- .../src/HelperSources/UINT16SeqNrHelper.hpp | 146 +++ .../src/HelperSources/UINT64SeqNrHelper.hpp | 155 +++ wifibroadcast/src/Ieee80211Header.hpp | 274 +++++ wifibroadcast/{ => src}/SimpleStream.hpp | 97 +- wifibroadcast/src/WBStreamRx.cpp | 172 ++++ wifibroadcast/{ => src}/WBStreamRx.h | 84 +- wifibroadcast/src/WBStreamTx.cpp | 310 ++++++ wifibroadcast/{ => src}/WBStreamTx.h | 160 +-- wifibroadcast/src/WBTxRx.cpp | 971 ++++++++++++++++++ wifibroadcast/{ => src}/WBTxRx.h | 22 +- wifibroadcast/src/WBVideoStreamTx.cpp | 118 +++ wifibroadcast/src/WBVideoStreamTx.h | 83 ++ wifibroadcast/src/WiFiCard.h | 41 + wifibroadcast/src/dummy_link/DummyLink.cpp | 158 +++ wifibroadcast/src/dummy_link/DummyLink.h | 52 + .../{ => src}/encryption/Decryptor.cpp | 31 +- .../{ => src}/encryption/Decryptor.h | 8 +- wifibroadcast/src/encryption/Encryption.cpp | 73 ++ .../{ => src}/encryption/Encryption.h | 50 +- .../encryption/EncryptionFsUtils.cpp | 31 +- .../{ => src}/encryption/EncryptionFsUtils.h | 5 +- .../{ => src}/encryption/Encryptor.cpp | 0 .../{ => src}/encryption/Encryptor.h | 0 .../{ => src}/encryption/KeyPair.cpp | 5 +- wifibroadcast/src/encryption/KeyPair.h | 45 + wifibroadcast/{ => src}/fec/FEC.cpp | 0 wifibroadcast/{ => src}/fec/FEC.h | 4 +- wifibroadcast/{ => src}/fec/FECConstants.hpp | 0 wifibroadcast/{ => src}/fec/FECDecoder.cpp | 0 wifibroadcast/{ => src}/fec/FECDecoder.h | 2 +- wifibroadcast/{ => src}/fec/FECEncoder.cpp | 2 +- wifibroadcast/{ => src}/fec/FECEncoder.h | 0 wifibroadcast/{ => src}/fec/FECPayloadHdr.hpp | 0 wifibroadcast/{ => src}/fec/RxBlock.cpp | 0 wifibroadcast/{ => src}/fec/RxBlock.h | 0 wifibroadcast/{ => src}/legacy/README.md | 0 .../{ => src}/legacy/WBStreamRxUDP.h | 0 .../{ => src}/legacy/WBStreamTxUDP.h | 0 wifibroadcast/src/pcap_helper.hpp | 118 +++ wifibroadcast/{ => src}/radiotap/README.md | 0 .../src/radiotap/RSSIAccumulator.hpp | 101 ++ .../src/radiotap/RadiotapHeaderRx.hpp | 221 ++++ .../{ => src}/radiotap/RadiotapHeaderTx.hpp | 139 ++- .../radiotap/RadiotapHeaderTxHolder.hpp | 51 +- .../src/radiotap/RadiotapRxRfAggregator.cpp | 92 ++ .../radiotap/RadiotapRxRfAggregator.h | 33 +- .../src/radiotap/SignalQualityAccumulator.hpp | 54 + .../{ => src}/radiotap/radiotap_util.hpp | 152 +-- wifibroadcast/{ => src}/raw_socket_helper.hpp | 41 +- wifibroadcast/{ => src}/tmp.txt | 0 .../{ => src}/wifibroadcast_spdlog.cpp | 0 .../{ => src}/wifibroadcast_spdlog.h | 5 +- 110 files changed, 5097 insertions(+), 4519 deletions(-) delete mode 100644 wifibroadcast/HelperSources/BlockSizeHelper.hpp delete mode 100644 wifibroadcast/HelperSources/DummyStreamGenerator.hpp delete mode 100644 wifibroadcast/HelperSources/EmulatedPacketDrop.hpp delete mode 100644 wifibroadcast/HelperSources/Rates.hpp delete mode 100644 wifibroadcast/HelperSources/SocketHelper.hpp delete mode 100644 wifibroadcast/HelperSources/StringHelper.hpp delete mode 100644 wifibroadcast/HelperSources/UINT16SeqNrHelper.hpp delete mode 100644 wifibroadcast/HelperSources/UINT64SeqNrHelper.hpp delete mode 100644 wifibroadcast/Ieee80211Header.hpp rename WBLib.cmake => wifibroadcast/WBLib.cmake (74%) delete mode 100644 wifibroadcast/WBStreamRx.cpp delete mode 100644 wifibroadcast/WBStreamTx.cpp delete mode 100644 wifibroadcast/WBTxRx.cpp delete mode 100644 wifibroadcast/WBVideoStreamTx.cpp delete mode 100644 wifibroadcast/WBVideoStreamTx.h delete mode 100644 wifibroadcast/WiFiCard.h rename {cmake => wifibroadcast/cmake}/FindPCAP.cmake (100%) rename {cmake => wifibroadcast/cmake}/FindSodium.cmake (100%) delete mode 100644 wifibroadcast/dummy_link/DummyLink.cpp delete mode 100644 wifibroadcast/dummy_link/DummyLink.h delete mode 100644 wifibroadcast/encryption/Encryption.cpp delete mode 100644 wifibroadcast/encryption/KeyPair.h rename {executables => wifibroadcast/executables}/benchmark.cpp (95%) rename {executables => wifibroadcast/executables}/example_hello.cpp (95%) rename {executables => wifibroadcast/executables}/example_pollute.cpp (90%) rename {executables => wifibroadcast/executables}/example_udp.cpp (93%) rename {executables => wifibroadcast/executables}/injection_rate_test.cpp (98%) rename {executables => wifibroadcast/executables}/socket_helper_test.cpp (97%) rename {executables => wifibroadcast/executables}/test_dummy_link.cpp (97%) rename {executables => wifibroadcast/executables}/test_listen.cpp (95%) rename {executables => wifibroadcast/executables}/test_queue.cpp (95%) rename {executables => wifibroadcast/executables}/test_txrx.cpp (94%) rename {executables => wifibroadcast/executables}/udp_generator_validator.cpp (97%) rename {executables => wifibroadcast/executables}/udp_packet_drop_util.cpp (92%) rename {executables => wifibroadcast/executables}/unit_test.cpp (96%) rename {executables => wifibroadcast/executables}/wfb_keygen.cpp (95%) delete mode 100644 wifibroadcast/pcap_helper.hpp delete mode 100644 wifibroadcast/radiotap/RSSIAccumulator.hpp delete mode 100644 wifibroadcast/radiotap/RadiotapHeaderRx.hpp delete mode 100644 wifibroadcast/radiotap/RadiotapRxRfAggregator.cpp delete mode 100644 wifibroadcast/radiotap/SignalQualityAccumulator.hpp rename wifibroadcast/{ => src}/FunkyQueue.cpp (100%) rename wifibroadcast/{ => src}/FunkyQueue.h (53%) rename wifibroadcast/{ => src}/HelperSources/Benchmark.hpp (59%) create mode 100644 wifibroadcast/src/HelperSources/BlockSizeHelper.hpp create mode 100644 wifibroadcast/src/HelperSources/DummyStreamGenerator.hpp create mode 100644 wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp rename wifibroadcast/{ => src}/HelperSources/Helper.hpp (52%) rename wifibroadcast/{ => src}/HelperSources/README.md (100%) rename wifibroadcast/{ => src}/HelperSources/RandomBufferPot.hpp (66%) create mode 100644 wifibroadcast/src/HelperSources/Rates.hpp rename wifibroadcast/{ => src}/HelperSources/SchedulingHelper.hpp (55%) rename wifibroadcast/{ => src}/HelperSources/SequenceNumberDebugger.hpp (62%) create mode 100644 wifibroadcast/src/HelperSources/SocketHelper.hpp create mode 100644 wifibroadcast/src/HelperSources/StringHelper.hpp rename wifibroadcast/{ => src}/HelperSources/TimeHelper.hpp (55%) create mode 100644 wifibroadcast/src/HelperSources/UINT16SeqNrHelper.hpp create mode 100644 wifibroadcast/src/HelperSources/UINT64SeqNrHelper.hpp create mode 100644 wifibroadcast/src/Ieee80211Header.hpp rename wifibroadcast/{ => src}/SimpleStream.hpp (52%) create mode 100644 wifibroadcast/src/WBStreamRx.cpp rename wifibroadcast/{ => src}/WBStreamRx.h (59%) create mode 100644 wifibroadcast/src/WBStreamTx.cpp rename wifibroadcast/{ => src}/WBStreamTx.h (53%) create mode 100644 wifibroadcast/src/WBTxRx.cpp rename wifibroadcast/{ => src}/WBTxRx.h (97%) create mode 100644 wifibroadcast/src/WBVideoStreamTx.cpp create mode 100644 wifibroadcast/src/WBVideoStreamTx.h create mode 100644 wifibroadcast/src/WiFiCard.h create mode 100644 wifibroadcast/src/dummy_link/DummyLink.cpp create mode 100644 wifibroadcast/src/dummy_link/DummyLink.h rename wifibroadcast/{ => src}/encryption/Decryptor.cpp (80%) rename wifibroadcast/{ => src}/encryption/Decryptor.h (94%) create mode 100644 wifibroadcast/src/encryption/Encryption.cpp rename wifibroadcast/{ => src}/encryption/Encryption.h (52%) rename wifibroadcast/{ => src}/encryption/EncryptionFsUtils.cpp (51%) rename wifibroadcast/{ => src}/encryption/EncryptionFsUtils.h (73%) rename wifibroadcast/{ => src}/encryption/Encryptor.cpp (100%) rename wifibroadcast/{ => src}/encryption/Encryptor.h (100%) rename wifibroadcast/{ => src}/encryption/KeyPair.cpp (77%) create mode 100644 wifibroadcast/src/encryption/KeyPair.h rename wifibroadcast/{ => src}/fec/FEC.cpp (100%) rename wifibroadcast/{ => src}/fec/FEC.h (98%) rename wifibroadcast/{ => src}/fec/FECConstants.hpp (100%) rename wifibroadcast/{ => src}/fec/FECDecoder.cpp (100%) rename wifibroadcast/{ => src}/fec/FECDecoder.h (100%) rename wifibroadcast/{ => src}/fec/FECEncoder.cpp (99%) rename wifibroadcast/{ => src}/fec/FECEncoder.h (100%) rename wifibroadcast/{ => src}/fec/FECPayloadHdr.hpp (100%) rename wifibroadcast/{ => src}/fec/RxBlock.cpp (100%) rename wifibroadcast/{ => src}/fec/RxBlock.h (100%) rename wifibroadcast/{ => src}/legacy/README.md (100%) rename wifibroadcast/{ => src}/legacy/WBStreamRxUDP.h (100%) rename wifibroadcast/{ => src}/legacy/WBStreamTxUDP.h (100%) create mode 100644 wifibroadcast/src/pcap_helper.hpp rename wifibroadcast/{ => src}/radiotap/README.md (100%) create mode 100644 wifibroadcast/src/radiotap/RSSIAccumulator.hpp create mode 100644 wifibroadcast/src/radiotap/RadiotapHeaderRx.hpp rename wifibroadcast/{ => src}/radiotap/RadiotapHeaderTx.hpp (52%) rename wifibroadcast/{ => src}/radiotap/RadiotapHeaderTxHolder.hpp (58%) create mode 100644 wifibroadcast/src/radiotap/RadiotapRxRfAggregator.cpp rename wifibroadcast/{ => src}/radiotap/RadiotapRxRfAggregator.h (69%) create mode 100644 wifibroadcast/src/radiotap/SignalQualityAccumulator.hpp rename wifibroadcast/{ => src}/radiotap/radiotap_util.hpp (61%) rename wifibroadcast/{ => src}/raw_socket_helper.hpp (52%) rename wifibroadcast/{ => src}/tmp.txt (100%) rename wifibroadcast/{ => src}/wifibroadcast_spdlog.cpp (100%) rename wifibroadcast/{ => src}/wifibroadcast_spdlog.h (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe29b188..3b2754ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,48 +7,48 @@ set(CMAKE_CXX_STANDARD 17) # Get spdlog from package manager for those tests set(WB_USE_SPDLOG_EXTERNALLY OFF) -include(WBLib.cmake) +include(wifibroadcast/WBLib.cmake) -add_executable(wfb_keygen executables/wfb_keygen.cpp) +add_executable(wfb_keygen wifibroadcast/executables/wfb_keygen.cpp) target_link_libraries(wfb_keygen PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(benchmark executables/benchmark.cpp) +add_executable(benchmark wifibroadcast/executables/benchmark.cpp) target_link_libraries(benchmark PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(udp_generator_validator executables/udp_generator_validator.cpp) +add_executable(udp_generator_validator wifibroadcast/executables/udp_generator_validator.cpp) target_link_libraries(udp_generator_validator PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(unit_test executables/unit_test.cpp) +add_executable(unit_test wifibroadcast/executables/unit_test.cpp) target_link_libraries(unit_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(socket_helper_test executables/socket_helper_test.cpp) +add_executable(socket_helper_test wifibroadcast/executables/socket_helper_test.cpp) target_link_libraries(socket_helper_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(udp_packet_drop_util executables/udp_packet_drop_util.cpp) +add_executable(udp_packet_drop_util wifibroadcast/executables/udp_packet_drop_util.cpp) target_link_libraries(udp_packet_drop_util PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(test_txrx executables/test_txrx.cpp) +add_executable(test_txrx wifibroadcast/executables/test_txrx.cpp) target_link_libraries(test_txrx PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(example_hello executables/example_hello.cpp) +add_executable(example_hello wifibroadcast/executables/example_hello.cpp) target_link_libraries(example_hello PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(example_udp executables/example_udp.cpp) +add_executable(example_udp wifibroadcast/executables/example_udp.cpp) target_link_libraries(example_udp PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(injection_rate_test executables/injection_rate_test.cpp) +add_executable(injection_rate_test wifibroadcast/executables/injection_rate_test.cpp) target_link_libraries(injection_rate_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(example_pollute executables/example_pollute.cpp) +add_executable(example_pollute wifibroadcast/executables/example_pollute.cpp) target_link_libraries(example_pollute PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(test_listen executables/test_listen.cpp) +add_executable(test_listen wifibroadcast/executables/test_listen.cpp) target_link_libraries(test_listen PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(test_dummy_link executables/test_dummy_link.cpp) +add_executable(test_dummy_link wifibroadcast/executables/test_dummy_link.cpp) target_link_libraries(test_dummy_link PRIVATE ${WB_TARGET_LINK_LIBRARIES}) -add_executable(test_queue executables/test_queue.cpp) +add_executable(test_queue wifibroadcast/executables/test_queue.cpp) target_link_libraries(test_queue PRIVATE ${WB_TARGET_LINK_LIBRARIES}) # When it is a static library, we don't need to install it. diff --git a/run_clang_format.sh b/run_clang_format.sh index 82407ab3..b9c18b23 100755 --- a/run_clang_format.sh +++ b/run_clang_format.sh @@ -14,23 +14,30 @@ function append_all_sources_headers() { FILE_LIST+=$TMP_FILE_LIST } - THIS_PATH="$(realpath "$0")" THIS_DIR="$(dirname "$THIS_PATH")" - -append_all_sources_headers "$THIS_DIR/wifibroadcast/inc" +append_all_sources_headers "$THIS_DIR/wifibroadcast/src" +append_all_sources_headers "$THIS_DIR/wifibroadcast/executables" echo "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\"" -# Format each file. -# - NB: do NOT put quotes around `$FILE_LIST` below or else the `clang-format` command will -# mistakenly see the entire blob of newline-separated file names as a SINGLE file name instead -# of as a new-line separated list of *many* file names! -clang-format --dry-run --Werror --verbose -i --style=file $FILE_LIST - -if [ "$?" -eq "0" ]; then - echo "Everything formatted correctly" -else - echo "There are formatting errors ! Please fix first." -fi \ No newline at end of file +# Checks for clang-format issues and returns error if they exist +function check_warning(){ + clang-format --dry-run --Werror --verbose -i --style=file $FILE_LIST + + if [ "$?" -eq "0" ]; then + echo "Everything formatted correctly" + else + echo "There are formatting errors ! Please fix first." + exit 1 + fi +} + +# fixes any issues (re-formats everything) +function fix_warnings() { + clang-format --verbose -i --style=file $FILE_LIST +} + +fix_warnings +#check_warning \ No newline at end of file diff --git a/wifibroadcast/HelperSources/BlockSizeHelper.hpp b/wifibroadcast/HelperSources/BlockSizeHelper.hpp deleted file mode 100644 index f5e8dc17..00000000 --- a/wifibroadcast/HelperSources/BlockSizeHelper.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// -// Created by consti10 on 07.12.22. -// - -#ifndef WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ -#define WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ - -#include -#include - -namespace blocksize{ - -// From https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c -static int div_ceil(int numerator, int denominator){ - std::div_t res = std::div(numerator, denominator); - return res.rem ? (res.quot + 1) : res.quot; -} - -// If a frame has more fragments than the max block size on this platform -// (Which usually depends on the compute power of the platform we are running on, since FEC blocks -// become exponentially increasing expensive the bigger they are) -// We need to split the frame into more than one block. Usually, this needs to be done only for key frames -// (which are much bigger than other frame(s) ), but depends on the platform compute and encoder bitrate, fps -static int calc_min_n_of_blocks(int fragments_in_this_frame,int max_block_size){ - return std::ceil(static_cast(fragments_in_this_frame)/static_cast(max_block_size)); -} - -// Algorithm: -// Given some amount of balls, fill the minimum amount of buckets as equally distributed as possible with balls -// such that each bucket has not more than max_block_size balls -static std::vector fill_buckets_evenly(int count,int max_size_of_bucket){ - if(count<=max_size_of_bucket){ - return {count}; - } - int consumed=0; - std::vector ret; - while (consumed calculate_best_fit_block_sizes(int fragments_in_this_frame,int max_block_size){ - if(fragments_in_this_frame<=max_block_size){ - // We can do this whole frame in one FEC block - return {static_cast(fragments_in_this_frame)}; - } - // Algorithm: - // Given some amount of balls, fill the minimum amount of buckets as equally distributed as possible with balls - // such that each bucket has not more than max_block_size balls - // We need at least this many buckets (blocks) - const int min_n_of_blocks= calc_min_n_of_blocks(fragments_in_this_frame,max_block_size); - std::vector ret; - ret.resize(min_n_of_blocks); - // Fill the buckets (blocks) with fragments, one after another, until we run out of balls (fragments) - int remaining=fragments_in_this_frame; - int index=0; - while (remaining>0){ - ret[index]++; - remaining--; - index++; - index = index % min_n_of_blocks; - } - return ret; -} - -static std::vector>>> split_frame_if_needed( - const std::vector>>& frame_fragments,int max_block_size){ - auto split= calculate_best_fit_block_sizes(frame_fragments.size(),max_block_size); - if(split.size()==1){ - return {frame_fragments}; - } - std::vector>>> ret; - ret.resize(split.size()); - int n_used_fragments=0; - for(int i=0;i -#include -#include -#include - -#include "RandomBufferPot.hpp" -#include "SchedulingHelper.hpp" - -/** - * Generates as close as possible a stream of data packets with a target packets per second packet rate. - */ -class DummyStreamGenerator{ - public: - typedef std::function OUTPUT_DATA_CALLBACK; - - DummyStreamGenerator(OUTPUT_DATA_CALLBACK cb,int packet_size) - : m_cb(std::move(cb)),m_packet_size(packet_size){ - m_random_buffer_pot=std::make_unique(1000,m_packet_size); - }; - ~DummyStreamGenerator(){ - stop(); - } - - void set_target_pps(int pps){ - m_target_pps=pps; - } - - void start(){ - m_terminate= false; - m_producer_thread=std::make_unique([this](){ - loop_generate_data(); - }); - } - void stop(){ - m_terminate= true; - if(m_producer_thread){ - m_producer_thread->join(); - m_producer_thread= nullptr; - } - } - void loop_generate_data(){ - SchedulingHelper::set_thread_params_max_realtime("DummyStreamGenerator"); - std::chrono::steady_clock::time_point last_packet=std::chrono::steady_clock::now(); - const uint64_t delay_between_packets_ns=1000*1000*1000 / m_target_pps; - const auto delay_between_packets=std::chrono::nanoseconds(delay_between_packets_ns); - wifibroadcast::log::get_default()->debug("Target pps:{} delta between packets:{}",m_target_pps,MyTimeHelper::R(delay_between_packets)); - while (!m_terminate){ - last_packet=std::chrono::steady_clock::now(); - //wifibroadcast::log::get_default()->debug("Delay between packets: {}",std::chrono::duration_cast(delay_between_packets).count()); - auto buff=m_random_buffer_pot->get_next_buffer(); - m_cb(buff->data(),buff->size()); - const auto next_packet_tp=last_packet+delay_between_packets-std::chrono::nanoseconds(200); // minus Xns to better hit the target - if(std::chrono::steady_clock::now()>=next_packet_tp){ - //wifibroadcast::log::get_default()->warn("Cannot keep up with the wanted tx pps"); - n_times_cannot_keep_up_wanted_pps++; - } - while (std::chrono::steady_clock::now() m_producer_thread; - std::unique_ptr m_random_buffer_pot; - bool m_terminate= false; -}; - - -#endif // WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP diff --git a/wifibroadcast/HelperSources/EmulatedPacketDrop.hpp b/wifibroadcast/HelperSources/EmulatedPacketDrop.hpp deleted file mode 100644 index 54f8d926..00000000 --- a/wifibroadcast/HelperSources/EmulatedPacketDrop.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef EMULATEDPACKETDROP_H -#define EMULATEDPACKETDROP_H - -#include -#include - -#include -#include - -// emulating packet drop "as" when using wifibroadcast (no matter weather it is with or without FEC) is not that easy. - -// Drops a specific percentage of packets, this doesn't eumlate the "big gaps" behaviour -class PacketDropEmulator{ -public: - PacketDropEmulator(int percentage_dropped_packets):m_percentage_dropped_packets(percentage_dropped_packets){ - - } - // Returns true if you should drop this packet, false otherwise - bool drop_packet(){ - std::lock_guard lock(m_mutex); - const auto number=next_random_number_0_100(); - //qDebug()<<"Number is:"<number){ - // drop packet - n_dropped_packets++; - log(); - return true; - } - n_forwarded_packets++; - log(); - return false; - } - void log(){ - const double perc_dropped=(double)n_dropped_packets / (n_totoal_packets)*100.0; - //std::cout<<"N dropped:"< lock(m_mutex); - m_percentage_dropped_packets=new_perc; - } -private: - std::mutex m_mutex; - int m_percentage_dropped_packets; - int n_dropped_packets=0; - int n_forwarded_packets=0; - int n_totoal_packets=0; - int next_random_number_0_100(){ - return m_dist100(m_mt); - } - std::mt19937 m_mt; - std::uniform_int_distribution<> m_dist100{0,100}; -}; - -#endif // EMULATEDPACKETDROP_H diff --git a/wifibroadcast/HelperSources/Rates.hpp b/wifibroadcast/HelperSources/Rates.hpp deleted file mode 100644 index 41a097dc..00000000 --- a/wifibroadcast/HelperSources/Rates.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// Created by consti10 on 25.07.23. -// - -#ifndef WIFIBROADCAST_RATES_H -#define WIFIBROADCAST_RATES_H - -#include - -namespace wifibroadcast{ - -// Theoretical rate(s) -struct Rate{ - int rate_20mhz_kbits; - int rate_40mhz_kbits; -}; - -// From https://mcsindex.com/ -static std::vector theoretical_rates_5G(){ - return { - Rate{6500 ,13500}, //mcs0 (VHT0) - Rate{13000,27000}, //mcs1 (VHT1) - Rate{19500,40500}, //mcs2 - Rate{26000,54000}, //mcs3 - Rate{39000,81000}, //mcs4 - Rate{52000,108000},//mcs5 - Rate{58500,121500},//mcs6 - Rate{65000,135000},//mcs7 - Rate{13000,27000}, //mcs8 (VHT0 + SS2) - Rate{26000,54000}, //mcs9 (VHT1 + SS2) - Rate{39000,81000}, //mcs10 (VHT2 + SS2) - Rate{52000,108000},//mcs11 (VHT3 + SS2) - }; -} -static Rate get_theoretical_rate_5G(int mcs){ - const auto rates=theoretical_rates_5G(); - if(mcs<0)return {-1,1}; - if(mcs openhd_rtl8812au_5G_practical_rates() { - return { - Rate{6100 ,11700}, // MCS 0 - Rate{11000,20600}, // MCS 1 - Rate{16000,28400}, // MCS 2 - Rate{20000,33400}, // MCS 3 - Rate{26000,36900}, // MCS 4 - Rate{28000,43500}, // MCS 5 - Rate{33000,48000}, // MCS 6 - Rate{38000,53000}, // MCS 7 - Rate{11700,16700}, // MCS 8 (VHT0 + SS2) - Rate{20000,30000}, // MCS 9 (VHT1 + SS2) - Rate{25000,37000}, // MCS 10 (VHT2 + SS2) - Rate{30000,51000}, // MCS 11 (VHT3 + SS2) - }; -} - -static Rate get_practical_rate_5G(int mcs){ - const auto rates=openhd_rtl8812au_5G_practical_rates(); - if(mcs<0)return {-1,1}; - if(mcs -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "StringHelper.hpp" -#include "TimeHelper.hpp" - -#include -#ifdef DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES -#include "openhd_spdlog.h" -#else -#include "../wifibroadcast_spdlog.h" -#endif // DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES - -namespace SocketHelper { - -static std::shared_ptr get_console(){ -#ifdef DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES - return openhd::log::get_default(); -#else - return wifibroadcast::log::get_default(); -#endif -} - -struct UDPConfig{ - const std::string ip; - const int port; -}; - -static std::string ip_port_as_string(const std::string ip,int port){ - return ip+":"+std::to_string(port); -} - -static const std::string ADDRESS_LOCALHOST = "127.0.0.1"; -static const std::string ADDRESS_ANY="0.0.0.0"; - -// returns the current socket receive timeout -static std::chrono::nanoseconds get_current_socket_send_rcv_timeout(int socketFd,bool send=false) { - timeval tv{}; - socklen_t len = sizeof(tv); - auto res = getsockopt(socketFd, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, &tv, &len); - assert(res == 0); - assert(len == sizeof(tv)); - return MyTimeHelper::timevalToDuration(tv); -} -// Returns size in bytes -static int get_socket_send_rcv_buff_size(int socketFd,bool send= false) { - int recvBufferSize=0; - socklen_t len=sizeof(recvBufferSize); - getsockopt(socketFd, SOL_SOCKET, send ? SO_SNDBUF : SO_RCVBUF, &recvBufferSize, &len); - return recvBufferSize; -} -static void set_socket_send_rcv_buffsize(int socketFd,int buffsize_wanted_bytes,bool send= false) { - const auto buffsize_before_bytes=get_socket_send_rcv_buff_size(socketFd,send); - const auto res=setsockopt(socketFd, SOL_SOCKET, send ? SO_SNDBUF : SO_RCVBUF, &buffsize_wanted_bytes, sizeof(buffsize_wanted_bytes)); - if (res < 0) { - get_console()->warn("Cannot set socket {} buffsize from {} to {}", - send ? "send":"recv",StringHelper::memorySizeReadable(buffsize_before_bytes), - StringHelper::memorySizeReadable(buffsize_wanted_bytes)); - } -} -// set the receive timeout on the socket -// throws runtime exception if this step fails (should never fail on linux) -static void set_socket_send_rcv_timeout(int socketFd, const std::chrono::nanoseconds timeout,bool send= false) { - const auto currentTimeout = get_current_socket_send_rcv_timeout(socketFd,send); - if (currentTimeout != timeout) { - auto tv = MyTimeHelper::durationToTimeval(timeout); - if (setsockopt(socketFd, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - get_console()->warn("Cannot set socket {} timeout from {} to {}", - send ? "send":"recv",MyTimeHelper::R(currentTimeout),MyTimeHelper::R(timeout)); - } - } -} -static void debug_send_rcv_timeout(int sockfd,std::shared_ptr& console){ - const auto send_timeout= get_current_socket_send_rcv_timeout(sockfd, true); - const auto recv_timeout= get_current_socket_send_rcv_timeout(sockfd, false); - console->debug("Socket rcv timeout:{} send timeout:{}",MyTimeHelper::R(recv_timeout),MyTimeHelper::R(send_timeout)); -} -static void debug_send_rcv_buffsize(int sockfd,std::shared_ptr& console){ - const auto send_buffsize= get_socket_send_rcv_buff_size(sockfd, true); - const auto recv_buffsize= get_socket_send_rcv_buff_size(sockfd, false); - console->debug("Socket buff size rcv:{} send:{}",StringHelper::memorySizeReadable(recv_buffsize),StringHelper::memorySizeReadable(send_buffsize)); -} - -// Set the reuse flag on the socket, so it doesn't care if there is a broken down process -// still on the socket or not. -static void setSocketReuse(int sockfd) { - int enable = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { - get_console()->warn("Cannot set socket reuse"); - } -} -// increase the UDP receive buffer size, needed for high bandwidth (at ~>20MBit/s the "default" -// udp receive buffer size is often not enough and the OS might (silently) drop packets on localhost) -static void increase_socket_recv_buff_size(int sockfd, const int wanted_rcvbuff_size_bytes) { - int recvBufferSize=0; - socklen_t len=sizeof(recvBufferSize); - getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &len); - { - std::stringstream ss; - ss<<"Default UDP socket recv buffer size:"<warn(ss.str()); - } - // We never decrease the socket receive buffer size, only increase it when neccessary - if(wanted_rcvbuff_size_bytes>(size_t)recvBufferSize){ - int wanted_size=wanted_rcvbuff_size_bytes; - recvBufferSize=wanted_rcvbuff_size_bytes; - if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &wanted_size,len)) { - get_console()->warn("Cannot increase UDP buffer size to {}",StringHelper::memorySizeReadable(wanted_rcvbuff_size_bytes)); - } - // Fetch it again to double check - recvBufferSize=-1; - getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &len); - get_console()->warn("UDP Wanted {} Set {}",StringHelper::memorySizeReadable(wanted_rcvbuff_size_bytes),StringHelper::memorySizeReadable(recvBufferSize)); - } -} -// Open the specified port for udp receiving -// sets SO_REUSEADDR to true if possible -// throws a runtime exception if opening the socket fails -static int openUdpSocketForReceiving(const std::string& address,const int port) { - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0){ - std::stringstream ss; - ss<<"Error opening socket "<warn(ss.str()); - return -1; - } - setSocketReuse(fd); - struct sockaddr_in saddr{}; - bzero((char *) &saddr, sizeof(saddr)); - saddr.sin_family = AF_INET; - //saddr.sin_addr.s_addr = htonl(INADDR_ANY); - inet_aton(address.c_str(), (in_addr *) &saddr.sin_addr.s_addr); - saddr.sin_port = htons((unsigned short) port); - if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { - std::stringstream ss; - ss<<"Bind error on socket "<warn(ss.str()); - return -1; - } - return fd; -} -// Wrapper around an UDP port you can send data to -// opens port on construction, closes port on destruction -class UDPForwarder { - public: - explicit UDPForwarder(std::string client_addr1, int client_udp_port1) : - client_addr(std::move(client_addr1)), client_udp_port(client_udp_port1) { - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - std::stringstream message; - message << "Error opening socket:" << strerror(errno) << "\n"; - get_console()->warn(message.str()); - } - //set up the destination - bzero((char *) &saddr, sizeof(saddr)); - saddr.sin_family = AF_INET; - //saddr.sin_addr.s_addr = inet_addr(client_addr.c_str()); - inet_aton(client_addr.c_str(), (in_addr *) &saddr.sin_addr.s_addr); - saddr.sin_port = htons((uint16_t) client_udp_port); - get_console()->info("UDPForwarder::configured for {} {}",client_addr,client_udp_port); - } - UDPForwarder(const UDPForwarder &) = delete; - UDPForwarder &operator=(const UDPForwarder &) = delete; - ~UDPForwarder() { - close(sockfd); - } - void forwardPacketViaUDP(const uint8_t *packet, const std::size_t packetSize) const { - //send(sockfd,packet,packetSize, MSG_DONTWAIT); - const auto ret=sendto(sockfd, packet, packetSize, 0, (const struct sockaddr *) &saddr, - sizeof(saddr)); - if(ret <0 || ret != packetSize){ - get_console()->warn("Error sending packet of size:{} to {} code:{} {}", - packetSize,ip_port_as_string(client_addr,client_udp_port),ret, strerror(errno)); - } - } - private: - struct sockaddr_in saddr{}; - int sockfd; - public: - const std::string client_addr; - const int client_udp_port; -}; - -/** - * Similar to UDP forwarder, but allows forwarding the same data to 0 or more IP::Port tuples - */ -class UDPMultiForwarder { - public: - /** - * Start forwarding data to another IP::Port tuple - */ - void addForwarder(const std::string &client_addr, int client_udp_port) { - std::lock_guard guard(udpForwardersLock); - // check if we already forward data to this IP::Port tuple - for (const auto &udpForwarder: udpForwarders) { - if (udpForwarder->client_addr == client_addr && udpForwarder->client_udp_port == client_udp_port) { - get_console()->info("UDPMultiForwarder: already forwarding to: {}:{}",client_addr,client_udp_port); - return; - } - } - get_console()->info("UDPMultiForwarder: add forwarding to: {}:{}",client_addr,client_udp_port); - udpForwarders.emplace_back(std::make_unique(client_addr, client_udp_port)); - } - /** - * Remove an already existing udp forwarding instance. - * Do nothing if such an instance is not found. - */ - void removeForwarder(const std::string &client_addr, int client_udp_port) { - std::lock_guard guard(udpForwardersLock); - udpForwarders.erase(std::find_if(udpForwarders.begin(), - udpForwarders.end(), - [&client_addr, &client_udp_port](const auto &udpForwarder) { - return udpForwarder->client_addr == client_addr - && udpForwarder->client_udp_port == client_udp_port; - })); - } - /** - * Forward data to all added IP::Port tuples via UDP - */ - void forwardPacketViaUDP(const uint8_t *packet, const std::size_t packetSize) { - std::lock_guard guard(udpForwardersLock); - for (const auto &udpForwarder: udpForwarders) { - udpForwarder->forwardPacketViaUDP(packet, packetSize); - } - } - [[nodiscard]] const std::list> &getForwarders() const { - return udpForwarders; - } - private: - // list of host::port tuples where we send the data to. - std::list> udpForwarders; - // modifying the list of forwarders must be thread-safe - std::mutex udpForwardersLock; -}; - -class UDPReceiver { - public: - typedef std::function OUTPUT_DATA_CALLBACK; - static constexpr const size_t UDP_PACKET_MAX_SIZE = 65507; - /** - * Receive data from socket and forward it via callback until stopLooping() is called - */ - explicit UDPReceiver(std::string client_addr, int client_udp_port, OUTPUT_DATA_CALLBACK cb,std::optional wanted_recv_buff_size=std::nullopt) - :mCb(std::move(cb)),m_wanted_recv_buff_size(wanted_recv_buff_size) { - mSocket = SocketHelper::openUdpSocketForReceiving(client_addr,client_udp_port); - if(m_wanted_recv_buff_size!=std::nullopt){ - increase_socket_recv_buff_size(mSocket,m_wanted_recv_buff_size.value()); - } - get_console()->info("UDPReceiver created with {}:{}",client_addr,client_udp_port); - } - ~UDPReceiver(){ - stopBackground(); - } - void loopUntilError() { - const auto buff = std::make_unique>(); - //sockaddr_in source; - //socklen_t sourceLen= sizeof(sockaddr_in); - while (receiving) { - //const ssize_t message_length = recvfrom(mSocket,buff->data(),UDP_PACKET_MAX_SIZE, MSG_WAITALL,(sockaddr*)&source,&sourceLen); - const ssize_t message_length = recv(mSocket, buff->data(), buff->size(), MSG_WAITALL); - if (message_length > 0) { - mCb(buff->data(), (size_t) message_length); - } else { - // this can also come from the shutdown, in which case it is not an error. - // But this way we break out of the loop. - if(receiving){ - get_console()->warn("Got message length of: {}",message_length); - } - } - } - get_console()->debug("UDP end"); - } - // Now this one is kinda special - for mavsdk we need to send messages from the port we are listening on - // to a specific IP::PORT tuple (such that the source address of the then received packet matches the address we are listening on). - void forwardPacketViaUDP(const std::string& destIp,const int destPort,const uint8_t *packet, const std::size_t packetSize) const { - //set up the destination - struct sockaddr_in saddr{}; - bzero((char *) &saddr, sizeof(saddr)); - saddr.sin_family = AF_INET; - //saddr.sin_addr.s_addr = inet_addr(client_addr.c_str()); - inet_aton(destIp.c_str(), (in_addr *) &saddr.sin_addr.s_addr); - saddr.sin_port = htons((uint16_t) destPort); - // send from the currently bound UDP port to the destination address - const auto ret=sendto(mSocket, packet, packetSize, 0, (const struct sockaddr *) &saddr, - sizeof(saddr)); - if(ret <0 || ret != packetSize){ - get_console()->warn("Error sending packet of size:{} to {} code:{} {}", - packetSize,ip_port_as_string(destIp,destPort),ret, strerror(errno)); - } - } - void stopLooping() { - receiving = false; - // from https://github.com/mavlink/MAVSDK/blob/main/src/mavsdk/core/udp_connection.cpp#L102 - shutdown(mSocket, SHUT_RDWR); - close(mSocket); - } - void runInBackground() { - if (receiverThread) { - get_console()->warn("Receiver thread is already running or has not been properly stopped"); - return; - } - receiving= true; - receiverThread = std::make_unique(&UDPReceiver::loopUntilError, this); - } - void stopBackground() { - stopLooping(); - if (receiverThread && receiverThread->joinable()) { - receiverThread->join(); - } - receiverThread = nullptr; - } - private: - const OUTPUT_DATA_CALLBACK mCb; - bool receiving = true; - int mSocket; - std::unique_ptr receiverThread = nullptr; - const std::optional m_wanted_recv_buff_size; -}; -} - -#endif //WIFIBROADCAST_SOCKETHELPER_HPP diff --git a/wifibroadcast/HelperSources/StringHelper.hpp b/wifibroadcast/HelperSources/StringHelper.hpp deleted file mode 100644 index f00bf9c5..00000000 --- a/wifibroadcast/HelperSources/StringHelper.hpp +++ /dev/null @@ -1,126 +0,0 @@ -// -// Created by Constantin on 09.10.2017. -// - -#ifndef OSDTESTER_STRINGHELPER_H -#define OSDTESTER_STRINGHELPER_H - -#include -#include -#include -#include - -class StringHelper { - public: - template - static std::string vectorAsString(const std::vector &v) { - std::stringstream ss; - ss << "["; - for(int i=0;i& v){ - std::stringstream ss; - ss << "["; - for(int i=0;i - static std::string arrayAsString(const std::array &a) { - std::stringstream ss; - ss << "["; - for(int i=0;i 1024 * 1024) { - float sizeMB = (float) sizeBytes / 1024.0 / 1024.0; - return std::to_string(sizeMB) + "mB"; - } - // more than one KB - if (sizeBytes > 1024) { - float sizeKB = (float) sizeBytes / 1024.0; - return std::to_string(sizeKB) + "kB"; - } - return std::to_string(sizeBytes) + "B"; - } - - static std::string float_to_string_with_precision(float value,int precision=-1){ - if(precision==-1){ - return std::to_string(value); - } - std::stringstream ss; - ss.precision(precision); - ss << std::fixed << value; - return ss.str(); - } - - static std::string bitrate_readable(int64_t bits_per_second){ - if(bits_per_second<=0){ - return std::to_string(bits_per_second)+" Bit/s"; - } - if(bits_per_second>1024*1024){ - float mBitsPerSecond = (float) bits_per_second / 1000.0 / 1000.0; - return float_to_string_with_precision(mBitsPerSecond,2) + "mBit/s"; - } - if(bits_per_second>1024){ - float kBitsPerSecond = (float) bits_per_second / 1000.0; - return float_to_string_with_precision(kBitsPerSecond,2) + "kBit/s"; - } - return float_to_string_with_precision(bits_per_second,2) + "Bit/s"; - } - -}; - -#endif //OSDTESTER_STRINGHELPER_H diff --git a/wifibroadcast/HelperSources/UINT16SeqNrHelper.hpp b/wifibroadcast/HelperSources/UINT16SeqNrHelper.hpp deleted file mode 100644 index cf4288ed..00000000 --- a/wifibroadcast/HelperSources/UINT16SeqNrHelper.hpp +++ /dev/null @@ -1,133 +0,0 @@ -// -// Created by consti10 on 21.12.22. -// - -#ifndef WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ -#define WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ - -#include -#include -#include - -#include "../wifibroadcast_spdlog.h" -#include -#include "../HelperSources/StringHelper.hpp" - -// UINT16SeqNrHelper for calculating statistics for a link with a rolling (wrap around) uint16_t sequence number -class UINT16SeqNrHelper { - public: - UINT16SeqNrHelper(){ - m_gaps.reserve(MAX_N_STORED_GAPS); - m_curr_packet_loss=-1; - } - void reset(){ - m_last_seq_nr=-1; - //m_curr_packet_loss=-1; - //m_curr_gaps_counter=-1; - } - int16_t get_current_loss_percent(){ - return m_curr_packet_loss; - } - int16_t get_current_gaps_counter(){ - return m_curr_gaps_counter; - } - void on_new_sequence_number(uint16_t seq_nr){ - if(m_last_seq_nr==-1){ - // first ever packet - m_last_seq_nr=seq_nr; - return; - } - const auto diff=diff_between_packets_rolling_uint16_t(m_last_seq_nr, seq_nr); - if(diff>1){ - const auto gap_size=diff-1; - // as an example, a diff of 2 means one packet is missing. - m_n_missing_packets+=gap_size; - m_n_received_packets++; - // can be usefully for debugging - if(m_store_and_debug_gaps && gap_size>1){ - store_debug_gap(gap_size); - } - //store_gap(diff-1); - //m_console->debug("Diff:{}",diff); - store_gap2(diff); - }else{ - m_n_received_packets++; - } - m_last_seq_nr=seq_nr; - recalculate_loss_if_needed(); - } - void set_store_and_debug_gaps(bool enable){ - m_store_and_debug_gaps=enable; - } - private: - // recalculate the loss in percentage in fixed intervals - // resets the received and missing packet count - void recalculate_loss_if_needed(){ - if(std::chrono::steady_clock::now()-m_last_loss_perc_recalculation>std::chrono::seconds(2)){ - m_last_loss_perc_recalculation=std::chrono::steady_clock::now(); - const auto n_total_packets=m_n_received_packets+m_n_missing_packets; - //m_console->debug("x_n_missing_packets:{} x_n_received_packets:{} n_total_packets:{}",x_n_missing_packets,x_n_received_packets,n_total_packets); - if(n_total_packets>=1){ - const double loss_perc=static_cast(m_n_missing_packets)/static_cast(n_total_packets)*100.0; - //m_curr_packet_loss=static_cast(std::lround(loss_perc)); - // we always round up the packet loss - m_curr_packet_loss=static_cast(std::ceil(loss_perc)); - //wifibroadcast::log::get_default()->debug("Packet loss:{} % {} %",m_curr_packet_loss,loss_perc); - }else{ - // We did not get any packets in the last x seconds - m_curr_packet_loss=-1; - } - m_n_received_packets=0; - m_n_missing_packets=0; - } - } - void store_debug_gap(int gap_size){ - m_gaps.push_back(gap_size); - const auto elasped=std::chrono::steady_clock::now()-m_last_gap_log; - if(elasped>std::chrono::seconds(1) || m_gaps.size()>=MAX_N_STORED_GAPS){ - wifibroadcast::log::get_default()->debug("Gaps: {}",StringHelper::vectorAsString(m_gaps)); - m_gaps.resize(0); - m_last_gap_log=std::chrono::steady_clock::now(); - } - } - void store_gap2(int gap_size){ - if(gap_size>=GAP_SIZE_COUNTS_AS_BIG_GAP){ - m_n_big_gaps++; - } - const auto elapsed=std::chrono::steady_clock::now()-m_last_big_gaps_counter_recalculation; - if(elapsed>=std::chrono::seconds(1)){ - m_curr_gaps_counter=(int16_t)m_n_big_gaps; - m_n_big_gaps=0; - m_last_big_gaps_counter_recalculation=std::chrono::steady_clock::now(); - } - } - static int diff_between_packets_rolling_uint16_t(int last_packet,int curr_packet){ - if(last_packet==curr_packet){ - wifibroadcast::log::get_default()->debug("Duplicate in seq nr {}-{}, invalid usage",last_packet,curr_packet); - } - if(curr_packet m_gaps; - static constexpr int GAP_SIZE_COUNTS_AS_BIG_GAP=10; - private: - int m_n_received_packets=0; - int m_n_missing_packets=0; - int m_n_big_gaps=0; - std::chrono::steady_clock::time_point m_last_gap_log; - std::chrono::steady_clock::time_point m_last_loss_perc_recalculation=std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point m_last_big_gaps_counter_recalculation=std::chrono::steady_clock::now(); - std::atomic m_curr_packet_loss{}; - std::atomic m_curr_gaps_counter{}; - bool m_store_and_debug_gaps= false; -}; - -#endif // WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ diff --git a/wifibroadcast/HelperSources/UINT64SeqNrHelper.hpp b/wifibroadcast/HelperSources/UINT64SeqNrHelper.hpp deleted file mode 100644 index aa54995f..00000000 --- a/wifibroadcast/HelperSources/UINT64SeqNrHelper.hpp +++ /dev/null @@ -1,139 +0,0 @@ -// -// Created by consti10 on 08.08.23. -// - -#ifndef WIFIBROADCAST_UINT64SEQNRHELPER_HPP -#define WIFIBROADCAST_UINT64SEQNRHELPER_HPP - -#include -#include -#include - -#include "../wifibroadcast_spdlog.h" -#include -#include "StringHelper.hpp" - -// UINT16SeqNrHelper for dealing with sequence number -// (calculate packet loss and more) -// Using a unique uint64_t nonce -class UINT64SeqNrHelper { - public: - int16_t get_current_loss_percent(){ - return m_curr_loss_perc.load(); - } - int16_t get_current_gaps_counter(){ - return m_curr_gaps_counter; - } - // NOTE: Does no packet re-ordering, therefore can only be used per card ! - void on_new_sequence_number(uint64_t seq_nr){ - if(m_last_seq_nr==UINT64_MAX){ - m_last_seq_nr=seq_nr; - return ; - } - if(m_last_seq_nr>=seq_nr){ - // Nonce must be strictly increasing, otherwise driver is bugged and reorders packets - // Or - more likely - a tx was restarted - which is so rare that it is okay to log a warning here and just accept the new value - wifibroadcast::log::get_default()->warn("Invalid sequence number last:{} new:{}",m_last_seq_nr,seq_nr); - m_last_seq_nr=seq_nr; - return ; - } - const auto diff=seq_nr-m_last_seq_nr; - if(diff>10000){ - wifibroadcast::log::get_default()->warn("Unlikely high gap, diff {} last:{} new:{}",diff,m_last_seq_nr,seq_nr); - m_last_seq_nr=seq_nr; - return ; - } - if(diff>1){ - // There is a gap of X packets - const auto gap_size=diff-1; - // as an example, a diff of 2 means one packet is missing. - m_n_missing_packets+=gap_size; - m_n_received_packets++; - if(m_store_and_debug_gaps && gap_size>1){ - store_debug_gap(static_cast(gap_size)); - } - set_and_recalculate_big_gap_counter(static_cast(gap_size)); - }else{ - // There is no gap between last and current packet - m_n_received_packets++; - } - recalculate_loss_if_needed(); - m_last_seq_nr=seq_nr; - } - void reset(){ - m_last_seq_nr=UINT64_MAX; - m_curr_loss_perc=-1; - m_curr_gaps_counter=-1; - } - void set_store_and_debug_gaps(int card_idx,bool enable){ - m_store_and_debug_gaps=enable; - m_card_index=card_idx; - } - private: - // recalculate the loss in percentage in fixed intervals - // resets the received and missing packet count - void recalculate_loss_if_needed(){ - const auto elapsed=std::chrono::steady_clock::now()-m_last_loss_perc_recalculation; - // Recalculate once the following limit(s) are reached: - // 1) more than 500 packets (in openhd, air to ground) - // 2) after 2 seconds if at least 10 packets are received (low bandwidth, ground to air) - // 3) after 5 seconds, regardless of n packets - const bool recalculate=(m_n_received_packets>=500) || - (elapsed>std::chrono::seconds(2) && m_n_received_packets>=10)|| - (elapsed>std::chrono::seconds(5)); - if(recalculate){ - m_last_loss_perc_recalculation=std::chrono::steady_clock::now(); - const auto n_total_packets=m_n_received_packets+m_n_missing_packets; - //m_console->debug("x_n_missing_packets:{} x_n_received_packets:{} n_total_packets:{}",x_n_missing_packets,x_n_received_packets,n_total_packets); - if(n_total_packets>=1){ - const double loss_perc=static_cast(m_n_missing_packets)/static_cast(n_total_packets)*100.0; - //m_curr_packet_loss=static_cast(std::lround(loss_perc)); - // we always round up the packet loss - m_curr_loss_perc=static_cast(std::ceil(loss_perc)); - //wifibroadcast::log::get_default()->debug("Packet loss:{} % {} %",m_curr_packet_loss,loss_perc); - }else{ - // We did not get any packets in the last x seconds - m_curr_loss_perc=-1; - } - m_n_received_packets=0; - m_n_missing_packets=0; - } - } - void store_debug_gap(int gap_size){ - m_gaps.push_back(gap_size); - const auto elasped=std::chrono::steady_clock::now()-m_last_gap_log; - if(elasped>std::chrono::seconds(1) || m_gaps.size()>=MAX_N_STORED_GAPS){ - wifibroadcast::log::get_default()->debug("Card{} Gaps: {}",m_card_index,StringHelper::vectorAsString(m_gaps)); - m_gaps.resize(0); - m_last_gap_log=std::chrono::steady_clock::now(); - } - } - void set_and_recalculate_big_gap_counter(int gap_size){ - if(gap_size>=GAP_SIZE_COUNTS_AS_BIG_GAP){ - m_n_big_gaps++; - } - const auto elapsed=std::chrono::steady_clock::now()-m_last_big_gaps_counter_recalculation; - if(elapsed>=std::chrono::seconds(1)){ - m_curr_gaps_counter=(int16_t)m_n_big_gaps; - m_n_big_gaps=0; - m_last_big_gaps_counter_recalculation=std::chrono::steady_clock::now(); - } - } - private: - std::chrono::steady_clock::time_point m_last_loss_perc_recalculation=std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point m_last_gap_log=std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point m_last_big_gaps_counter_recalculation=std::chrono::steady_clock::now(); - uint64_t m_last_seq_nr=UINT64_MAX; - std::atomic m_curr_loss_perc=-1; - std::atomic m_curr_gaps_counter{}; - int m_n_received_packets=0; - int m_n_missing_packets=0; - int m_n_big_gaps=0; - static constexpr int MAX_N_STORED_GAPS=1000; - std::vector m_gaps; - static constexpr int GAP_SIZE_COUNTS_AS_BIG_GAP=10; - bool m_store_and_debug_gaps= false; - int m_card_index=0; -}; - -#endif // WIFIBROADCAST_UINT64SEQNRHELPER_HPP diff --git a/wifibroadcast/Ieee80211Header.hpp b/wifibroadcast/Ieee80211Header.hpp deleted file mode 100644 index 5c57e834..00000000 --- a/wifibroadcast/Ieee80211Header.hpp +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef __WIFIBROADCAST_IEEE80211_HEADER_HPP__ -#define __WIFIBROADCAST_IEEE80211_HEADER_HPP__ - -#include - -#include -#include -#include -#include -#include - -#include "HelperSources/StringHelper.hpp" -#include "wifibroadcast_spdlog.h" -#include - -// UINT16SeqNrHelper for dealing with the IEEE80211 header in wifibroadcast / openhd -// Usefully references: -// https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ -// https://en.wikipedia.org/wiki/802.11_Frame_Types -// https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h - -// NOTE: THIS IS THE LAYOUT OF A NORMAL IEEE80211 header -// | 2 bytes | 2 bytes | 6 bytes | 6 bytes | 6 bytes | 2 bytes | -// | control field | duration | MAC of AP | SRC MAC | DST MAC | Sequence control | -static constexpr auto IEEE80211_HEADER_SIZE_BYTES = 24; - -// UINT16SeqNrHelper for control field - we do not touch it -struct ControlField{ - uint8_t part1=0x08; - uint8_t part2=0x01; -}__attribute__ ((packed)); -static_assert(sizeof(ControlField) == 2); - -// UINT16SeqNrHelper for sequence control field -//https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ -//Sequence Control: Contains a 4-bit fragment number subfield, used for fragmentation and reassembly, and a 12-bit sequence number used to number -//frames sent between a given transmitter and receiver. -struct SequenceControl { - uint8_t subfield: 4; - uint16_t sequence_nr: 12; - std::string as_debug_string()const{ - std::stringstream ss; - ss << "SequenceControl["<<"" << (int)subfield << ":" << (int) sequence_nr<<"]"; - return ss.str(); - } -}__attribute__ ((packed)); -static_assert(sizeof(SequenceControl) == 2); - -// We use as many bytes of this header for useful purposes as possible - might be a bit hard to understand for beginners why we use stuff this way, -// but optimizing on a byte level is complicated and we have to account for driver quirks - -static constexpr auto OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR=0x01; -static constexpr auto OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND=0x02; - -struct Ieee80211HeaderOpenHD{ - // We do not touch the control field (driver) - //ControlField control_field{}; - uint8_t control_field_part1=0x08; - uint8_t control_field_part2=0x01; - // We do not touch the duration field (driver) - uint8_t duration1=0x00; - uint8_t duration2=0x00; - // We do not touch this MAC (driver) - and set it to broadcast such that the monitor mode driver accepts it - std::array mac_ap={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - // We can and do use this mac - 1 byte for unique identifier (air/gnd), 4 bytes for part 1 of nonce, last byte for radio port - uint8_t mac_src_unique_id_part=OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; - std::array mac_src_nonce_part1={}; - uint8_t mac_src_radio_port=0; - // We can and do use this mac - 1 byte for unique identifier (air/gnd), 4 bytes for part 2 of nonce, last byte for radio port - uint8_t mac_dst_unique_id_part=OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; - std::array mac_dst_nonce_part2={}; - uint8_t mac_dst_radio_port=0; - // iee80211 sequence control ( 2 bytes ) - might be overridden by the driver, and/or even repurposed - uint16_t sequence_control=0; - // ----------------------------------- DATA LAYOUT END ----------------------------------- - /** - * We use some of the available bytes for a 8 bytes "nonce" - */ - void write_nonce(const uint64_t nonce){ - memcpy((uint8_t*)&mac_src_nonce_part1,(uint8_t*)&nonce,4); - memcpy((uint8_t*)&mac_dst_nonce_part2,((uint8_t*)&nonce)+4,4); - // From https://stackoverflow.com/questions/2810280/how-to-store-a-64-bit-integer-in-two-32-bit-integers-and-convert-back-again - //mac_src_nonce_part1 = static_cast(nonce >> 32); - //mac_dst_nonce_part2 = static_cast(nonce); - } - uint64_t get_nonce()const{ - uint64_t nonce=0; - memcpy(((uint8_t*)&nonce),(uint8_t*)&mac_src_nonce_part1,4); - memcpy(((uint8_t*)&nonce)+4,(uint8_t*)&mac_dst_nonce_part2,4); - return nonce; - } - /** - * NOTE: We write the radio port 2 times - this way we have a pretty reliable way to check if this is an openhd packet or packet from someone else - */ - void write_radio_port_src_dst(uint8_t radio_port){ - mac_src_radio_port=radio_port; - mac_dst_radio_port=radio_port; - } - /* - * We also write the unique id 2 times - same reason like with radio port - */ - void write_unique_id_src_dst(uint8_t id){ - mac_src_unique_id_part=id; - mac_dst_unique_id_part=id; - } - // Check - first byte of scr and dst mac needs to mach (unique air / gnd id) - bool has_valid_air_gnd_id()const{ - return mac_src_unique_id_part==mac_dst_unique_id_part; - } - // Check - last byte of wifibroadcast and dst mac needs to match (radio port) - bool has_valid_radio_port()const{ - return mac_src_radio_port==mac_dst_radio_port; - } - // validate before use (matching) - uint8_t get_valid_air_gnd_id()const{ - return mac_src_unique_id_part; - } - // validate before use (matching) - uint8_t get_valid_radio_port()const{ - return mac_src_radio_port; - } - bool is_data_frame() const { - return control_field_part1 == 0x08 && control_field_part2 == 0x01; - } - std::string debug_radio_ports()const{ - return fmt::format("{}:{}",(int)mac_src_radio_port,(int)mac_dst_radio_port); - } - std::string debug_unique_ids()const{ - return fmt::format("{}:{}",(int)mac_src_unique_id_part,(int)mac_dst_unique_id_part); - } - std::string debug_control_field()const{ - return fmt::format("{}:{}",StringHelper::byte_as_hex(control_field_part1),StringHelper::byte_as_hex(control_field_part2)); - } - // Only for testing ! - void dirty_write_dummy_fixed_src_dest_mac(){ - uint8_t* src_mac=&mac_src_unique_id_part; - uint8_t* dst_mac=&mac_dst_unique_id_part; - static constexpr std::array dummy_mac1={0x00, 0x00, 0x01, 0x01, 0x02, 0x02}; - static constexpr std::array dummy_mac2={0x02, 0x02, 0x01, 0x01, 0x00, 0x00}; - memcpy(src_mac,dummy_mac1.data(),6); - memcpy(dst_mac,dummy_mac2.data(),6); - } - // Dirty - void write_ieee80211_seq_nr(const uint16_t seq_nr){ - uint8_t seq_nr_buf[2]; - seq_nr_buf[0] = seq_nr & 0xff; - seq_nr_buf[1] = (seq_nr >> 8) & 0xff; - memcpy((uint8_t*)&sequence_control,seq_nr_buf,2); - } -}__attribute__ ((packed)); -static_assert(sizeof(Ieee80211HeaderOpenHD)==IEEE80211_HEADER_SIZE_BYTES); - - -// Wrapper around the Ieee80211 header (declared as raw array initially) -// info https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ -// In the way this is declared it is an IEE80211 data frame -// https://en.wikipedia.org/wiki/802.11_Frame_Types -//TODO maybe use https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h -class Ieee80211HeaderRaw { - public: - static constexpr auto SIZE_BYTES = 24; - //the last byte of the mac address is recycled as a port number - static constexpr const auto SRC_MAC_LASTBYTE = 15; - static constexpr const auto DST_MAC_LASTBYTE = 21; - static constexpr const auto FRAME_SEQ_LB = 22; - static constexpr const auto FRAME_SEQ_HB = 23; - // raw data buffer - std::array data = { - 0x08, 0x01, // first 2 bytes control fiels - 0x00, 0x00, // 2 bytes duration (has this even an effect ?!) - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // something MAC ( 6 bytes), I think Receiver address (MAC of AP) - 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, // something MAC ( 6 bytes), I think SRC MAC (mac of source STA)- last byte is used as radio port - 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, // something MAC ( 6 bytes), I think DEST MAC (mac of dest STA) - last byte is als used as radio port - 0x00, 0x00, // iee80211 sequence control ( 2 bytes ) - }; - // default constructor - Ieee80211HeaderRaw() = default; - /*static std::string mac_as_string(const uint8_t* mac_6bytes){ - return StringHelper::bytes_as_string_hex(mac_6bytes,6); - } - std::string header_as_string()const{ - std::stringstream ss; - ss< - -WBStreamRx::WBStreamRx(std::shared_ptr txrx,Options options1) - : m_txrx(txrx), - m_options(options1) -{ - assert(m_txrx); - if(m_options.opt_console){ - m_console=m_options.opt_console; - }else{ - m_console=wifibroadcast::log::create_or_get("wb_rx"+std::to_string(m_options.radio_port)); - } - if(m_options.enable_fec){ - m_fec_decoder = std::make_unique(m_options.fec_rx_queue_depth,MAX_TOTAL_FRAGMENTS_PER_BLOCK, - m_options.enable_fec_debug_log, - m_options.forward_gapped_fragments); - auto cb=[this](const uint8_t *data, int data_len){ - on_decoded_packet(data,data_len); - }; - m_fec_decoder->mSendDecodedPayloadCallback = cb; - }else{ - m_fec_disabled_decoder = std::make_unique(); - auto cb=[this](const uint8_t *data, int data_len){ - on_decoded_packet(data,data_len); - }; - m_fec_disabled_decoder->mSendDecodedPayloadCallback = cb; - } - auto cb_packet=[this](uint64_t nonce,int wlan_index,const uint8_t *data, const int data_len){ - this->on_new_packet(nonce,wlan_index,data,data_len); - }; - auto cb_sesssion=[this](){ - this->on_new_session(); - }; - auto handler=std::make_shared(m_options.radio_port,cb_packet,cb_sesssion); - m_txrx->rx_register_stream_handler(handler); - if(m_options.enable_threading){ - m_packet_queue=std::make_unique(m_options.packet_queue_size); - m_process_data_thread_run=true; - m_process_data_thread=std::make_unique(&WBStreamRx::loop_process_data, this); - } -} - -WBStreamRx::~WBStreamRx() { - m_txrx->rx_unregister_stream_handler(m_options.radio_port); - if(m_options.enable_threading){ - m_process_data_thread_run= false; - if(m_process_data_thread->joinable()){ - m_process_data_thread->join(); - } - } -} - -void WBStreamRx::set_callback( - WBStreamRx::OUTPUT_DATA_CALLBACK output_data_callback) { - m_out_cb=std::move(output_data_callback); -} - -void WBStreamRx::on_new_packet(uint64_t nonce, int wlan_index, const uint8_t *data,const int data_len) { - m_n_input_packets++; - m_n_input_bytes+=data_len; - if(m_options.enable_threading){ - auto item=std::make_shared(); - item->data=std::make_shared>(data,data+data_len); - const bool res= m_packet_queue->try_enqueue(item); - if(!res){ - // would hint at too high cpu usage - m_console->warn("Cannot enqueue packet"); - } - }else{ - if(m_options.enable_fec){ - if(!FECDecoder::validate_packet_size(data_len)){ - m_console->debug("invalid fec packet size {}",data_len); - return ; - } - m_fec_decoder->process_valid_packet(data,data_len); - }else{ - m_fec_disabled_decoder->process_packet(data,data_len); - } - } -} - -void WBStreamRx::on_new_session() { - if(m_fec_decoder){ - m_fec_decoder->reset_rx_queue(); - } - if(m_fec_disabled_decoder){ - m_fec_disabled_decoder->reset_packets_map(); - } - reset_stream_stats(); -} - -void WBStreamRx::loop_process_data() { - std::shared_ptr packet; - static constexpr std::int64_t timeout_usecs=1000*1000; - while (m_process_data_thread_run) { - auto opt_packet=m_packet_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); - if(opt_packet.has_value()){ - auto packet=opt_packet.value(); - process_queued_packet(*packet); - } - } -} - -void WBStreamRx::process_queued_packet(const WBStreamRx::EnqueuedPacket &packet) { - assert(m_options.enable_threading== true); - if(m_options.enable_fec){ - if(!FECDecoder::validate_packet_size(packet.data->size())){ - m_console->debug("invalid fec packet size {}",packet.data->size()); - return ; - } - m_fec_decoder->process_valid_packet(packet.data->data(),packet.data->size()); - }else{ - m_fec_disabled_decoder->process_packet(packet.data->data(),packet.data->size()); - } -} - -void WBStreamRx::on_decoded_packet(const uint8_t *data, int data_len) { - m_n_output_bytes+=data_len; - if(m_out_cb){ - m_out_cb(data,data_len); - } -} - -WBStreamRx::Statistics WBStreamRx::get_latest_stats() { - WBStreamRx::Statistics ret; - ret.n_input_bytes=m_n_input_bytes; - ret.n_input_packets=m_n_input_packets; - ret.curr_in_packets_per_second= - m_input_packets_per_second_calculator.get_last_or_recalculate( - m_n_input_packets,std::chrono::seconds(2)); - ret.curr_in_bits_per_second=m_input_bitrate_calculator.get_last_or_recalculate( - m_n_input_bytes,std::chrono::seconds(2)); - ret.curr_out_bits_per_second=m_received_bitrate_calculator.get_last_or_recalculate( - m_n_output_bytes,std::chrono::seconds(2)); - return ret; -} - -WBStreamRx::FECRxStats2 WBStreamRx::get_latest_fec_stats() { - WBStreamRx::FECRxStats2 ret; - if(m_fec_decoder){ - auto stats=m_fec_decoder->stats; - ret.count_blocks_lost= stats.count_blocks_lost; - ret.count_blocks_recovered=stats.count_blocks_recovered; - ret.count_blocks_total=stats.count_blocks_total; - ret.count_fragments_recovered=stats.count_fragments_recovered; - ret.curr_fec_decode_time=stats.curr_fec_decode_time; - } - return ret; -} - -void WBStreamRx::reset_stream_stats() { - m_n_input_bytes=0; - m_n_input_packets=0; -} - -void WBStreamRx::set_on_fec_block_done_cb(WBStreamRx::ON_BLOCK_DONE_CB cb) { - m_fec_decoder->m_block_done_cb=cb; -} diff --git a/wifibroadcast/WBStreamTx.cpp b/wifibroadcast/WBStreamTx.cpp deleted file mode 100644 index 36163f5a..00000000 --- a/wifibroadcast/WBStreamTx.cpp +++ /dev/null @@ -1,277 +0,0 @@ -// -// Created by consti10 on 28.06.23. -// - -#include "WBStreamTx.h" - -#include - -#include "BlockSizeHelper.hpp" -#include "SchedulingHelper.hpp" - -WBStreamTx::WBStreamTx(std::shared_ptr txrx,Options options1,std::shared_ptr radiotap_header_holder) - :options(options1), - m_txrx(txrx), - m_radiotap_header_holder(std::move(radiotap_header_holder)) -{ - assert(m_txrx); - if(options.opt_console){ - m_console=options.opt_console; - }else{ - m_console=wifibroadcast::log::create_or_get("wb_tx"+std::to_string(options.radio_port)); - } - assert(m_console); - m_console->info("WBTransmitter radio_port: {} fec:{}", options.radio_port, options.enable_fec ? "Y" : "N"); - if(options.enable_fec){ - //m_block_queue=std::make_unique>>(options.block_data_queue_size); - m_block_queue=std::make_unique(options.block_data_queue_size); - m_fec_encoder = std::make_unique(); - auto cb=[this](const uint8_t* packet,int packet_len){ - send_packet(packet,packet_len); - }; - m_fec_encoder->m_out_cb =cb; - }else{ - //m_packet_queue=std::make_unique>>(options.packet_data_queue_size); - m_packet_queue=std::make_unique(options.packet_data_queue_size); - m_fec_disabled_encoder = std::make_unique(); - auto cb=[this](const uint8_t* packet,int packet_len){ - send_packet(packet,packet_len); - }; - m_fec_disabled_encoder->outputDataCallback=cb; - } - m_process_data_thread_run=true; - m_process_data_thread=std::make_unique(&WBStreamTx::loop_process_data, this); -} - -WBStreamTx::~WBStreamTx() { - m_process_data_thread_run= false; - if(m_process_data_thread && m_process_data_thread->joinable()){ - m_process_data_thread->join(); - } -} - -bool WBStreamTx::try_enqueue_packet(std::shared_ptr> packet,int n_injections) { - assert(!options.enable_fec); - m_n_input_packets++; - m_count_bytes_data_provided+=packet->size(); - auto item=std::make_shared(); - item->data=std::move(packet); - item->n_injections=n_injections; - const bool res= m_packet_queue->try_enqueue(item); - if(!res){ - m_n_dropped_packets++; - } - return res; -} - -int WBStreamTx::enqueue_packet_dropping( - std::shared_ptr> packet, int n_injections) { - assert(!options.enable_fec); - m_n_input_packets++; - m_count_bytes_data_provided+=packet->size(); - auto item=std::make_shared(); - item->data=std::move(packet); - item->n_injections=n_injections; - const int n_dropped= m_packet_queue->enqueue_or_clear_enqueue(item); - if(n_dropped>0){ - m_n_dropped_packets+=n_dropped; - } - return n_dropped; -} - -bool WBStreamTx::try_enqueue_block(std::vector>> fragments,int max_block_size, int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time) { - assert(options.enable_fec); - m_n_input_packets+=fragments.size(); - for(const auto& fragment:fragments){ - if (fragment->empty() || fragment->size() > FEC_PACKET_MAX_PAYLOAD_SIZE) { - m_console->warn("Fed fragment with incompatible size:{}",fragment->size()); - return false; - } - m_count_bytes_data_provided+=fragment->size(); - } - auto item=std::make_shared(); - item->fragments=fragments; - item->max_block_size=max_block_size; - item->fec_overhead_perc=fec_overhead_perc; - item->creation_time=creation_time; - const bool res= m_block_queue->try_enqueue(item); - if(!res){ - m_n_dropped_packets+=fragments.size(); - m_n_dropped_frames++; - //m_curr_seq_nr+=fragments.size(); - } - return res; -} - -int WBStreamTx::enqueue_block_dropping( - std::vector>> fragments, - int max_block_size, int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time) { - assert(options.enable_fec); - m_n_input_packets+=fragments.size(); - for(const auto& fragment:fragments){ - if (fragment->empty() || fragment->size() > FEC_PACKET_MAX_PAYLOAD_SIZE) { - m_console->warn("Fed fragment with incompatible size:{}",fragment->size()); - return false; - } - m_count_bytes_data_provided+=fragment->size(); - } - auto item=std::make_shared(); - item->fragments=fragments; - item->max_block_size=max_block_size; - item->fec_overhead_perc=fec_overhead_perc; - item->creation_time=creation_time; - const int ret=m_block_queue->enqueue_or_clear_enqueue(item); - if(ret!=0){ - m_n_dropped_packets+=fragments.size(); - m_n_dropped_frames+=ret; - } - return ret; -} - - -bool WBStreamTx::try_enqueue_frame( - std::shared_ptr> frame, int max_block_size, - int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time) { - assert(options.enable_fec); - m_n_input_packets+=1; - m_count_bytes_data_provided+=frame->size(); - auto item=std::make_shared(); - item->frame=frame; - item->max_block_size=max_block_size; - item->fec_overhead_perc=fec_overhead_perc; - item->creation_time=creation_time; - const bool res= m_block_queue->try_enqueue(item); - if(!res){ - m_n_dropped_packets+=1; - m_n_dropped_frames++; - //m_curr_seq_nr+=fragments.size(); - } - return res; -} - -WBStreamTx::FECStats WBStreamTx::get_latest_fec_stats() { - WBStreamTx::FECStats ret{}; - if(m_fec_encoder){ - ret.curr_fec_encode_time=m_fec_encoder->m_curr_fec_block_encode_time; - ret.curr_fec_block_length=m_fec_encoder->m_curr_fec_block_sizes; - } - return ret; -} - -WBStreamTx::Statistics WBStreamTx::get_latest_stats() { - WBStreamTx::Statistics ret{}; - ret.n_provided_bytes=m_count_bytes_data_provided; - ret.n_provided_packets=m_n_input_packets; - ret.n_injected_packets= m_n_injected_packets; - ret.n_injected_bytes=static_cast(m_count_bytes_data_injected); - ret.current_injected_bits_per_second=m_bitrate_calculator_injected_bytes.get_last_or_recalculate( - m_count_bytes_data_injected,std::chrono::seconds(2)); - ret.current_provided_bits_per_second= - m_bitrate_calculator_data_provided.get_last_or_recalculate( - m_count_bytes_data_provided,std::chrono::seconds(2)); - ret.n_dropped_packets=m_n_dropped_packets; - ret.n_dropped_frames=m_n_dropped_frames; - ret.current_injected_packets_per_second=m_packets_per_second_calculator.get_last_or_recalculate( - m_n_injected_packets,std::chrono::seconds(2)); - ret.curr_block_until_tx_min_us=m_curr_block_until_tx_min_max_avg_us.min; - ret.curr_block_until_tx_max_us=m_curr_block_until_tx_min_max_avg_us.max; - ret.curr_block_until_tx_avg_us=m_curr_block_until_tx_min_max_avg_us.avg; - return ret; -} - -void WBStreamTx::loop_process_data() { - if(options.dequeue_thread_max_realtime){ - SchedulingHelper::set_thread_params_max_realtime("WBStreamTx::loop_process_data"); - } - static constexpr std::int64_t timeout_usecs=100*1000; - if(options.enable_fec){ - while (m_process_data_thread_run){ - auto opt_frame=m_block_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); - if(opt_frame.has_value()){ - auto frame=opt_frame.value(); - // dequeued frame - m_queue_time_calculator.add(std::chrono::steady_clock::now()-frame->enqueue_time_point); - if(m_queue_time_calculator.get_delta_since_last_reset()>std::chrono::seconds(1)){ - if(options.log_time_spent_in_atomic_queue){ - m_console->debug("Time in queue {}",m_queue_time_calculator.getAvgReadable()); - } - m_queue_time_calculator.reset(); - } - process_enqueued_block(*frame); - const auto delta=std::chrono::steady_clock::now()-frame->creation_time; - m_block_until_tx_time.add(delta); - if(m_block_until_tx_time.get_delta_since_last_reset()>std::chrono::seconds(2)){ - if(options.log_time_blocks_until_tx){ - m_console->debug("Time until tx {}",m_block_until_tx_time.getAvgReadable()); - } - m_curr_block_until_tx_min_max_avg_us= min_max_avg_as_us(m_block_until_tx_time.getMinMaxAvg()); - m_block_until_tx_time.reset(); - } - } - } - }else{ - std::shared_ptr packet; - while (m_process_data_thread_run){ - auto opt_packet=m_packet_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); - if(opt_packet.has_value()){ - auto packet=opt_packet.value(); - m_queue_time_calculator.add(std::chrono::steady_clock::now()-packet->enqueue_time_point); - if(m_queue_time_calculator.get_delta_since_last_reset()>std::chrono::seconds(1)){ - if(options.log_time_spent_in_atomic_queue){ - m_console->debug("Time in queue {}",m_queue_time_calculator.getAvgReadable()); - } - m_queue_time_calculator.reset(); - } - process_enqueued_packet(*packet); - } - } - } -} - -void WBStreamTx::process_enqueued_packet(const WBStreamTx::EnqueuedPacket& packet) { - auto buff=m_fec_disabled_encoder->encode_packet_buffer(packet.data->data(),packet.data->size()); - for(int i=0;iencode_packet_cb(packet.data->data(),packet.data->size()); -} - -void WBStreamTx::process_enqueued_block(const WBStreamTx::EnqueuedBlock& block) { - if(block.frame!= nullptr){ - dirty_process_enqueued_frame(block); - return ; - } - auto blocks=blocksize::split_frame_if_needed(block.fragments,block.max_block_size); - for(auto& x_block :blocks){ - const auto n_secondary_f=calculate_n_secondary_fragments(x_block.size(),block.fec_overhead_perc); - m_fec_encoder->encode_block(x_block,n_secondary_f); - } -} - -void WBStreamTx::dirty_process_enqueued_frame( - const WBStreamTx::EnqueuedBlock& block) { - //TODO: Figure out the ideal fragment size for this frame - const int MTU=1440; - const int n_primary_fragments=blocksize::div_ceil(block.frame->size(),MTU); - const int n_secondary_fragments= calculate_n_secondary_fragments(n_primary_fragments,block.fec_overhead_perc); - m_fec_encoder->fragment_and_encode(block.frame->data(),block.frame->size(),n_primary_fragments, - n_secondary_fragments); -} - -void WBStreamTx::send_packet(const uint8_t* packet, int packet_len) { - const auto radiotap_header=m_radiotap_header_holder->thread_safe_get(); - const bool encrypt=m_enable_encryption.load(); - m_txrx->tx_inject_packet(options.radio_port,packet,packet_len,radiotap_header,encrypt); - m_n_injected_packets++; - m_count_bytes_data_injected+=packet_len; -} - -int WBStreamTx::get_tx_queue_available_size_approximate() { - //const auto ret=options.enable_fec ? m_block_queue->size_approx() : m_packet_queue->size_approx(); - //return (int)ret; - if(options.enable_fec)return m_block_queue->get_current_size(); - return m_packet_queue->get_current_size(); -} diff --git a/wifibroadcast/WBTxRx.cpp b/wifibroadcast/WBTxRx.cpp deleted file mode 100644 index eb91f7e5..00000000 --- a/wifibroadcast/WBTxRx.cpp +++ /dev/null @@ -1,862 +0,0 @@ -// -// Created by consti10 on 27.06.23. -// - -#include "WBTxRx.h" - -#include - -#include "SchedulingHelper.hpp" -#include "pcap_helper.hpp" -#include "radiotap/RadiotapHeaderRx.hpp" -#include "raw_socket_helper.hpp" -#include "radiotap/radiotap_util.hpp" - -WBTxRx::WBTxRx(std::vector wifi_cards1,Options options1,std::shared_ptr session_key_radiotap_header) - : m_options(options1), - m_wifi_cards(std::move(wifi_cards1)), - m_session_key_radiotap_header(std::move(session_key_radiotap_header)) -{ - assert(!m_wifi_cards.empty()); - m_console=wifibroadcast::log::create_or_get("WBTxRx"); - m_console->debug("[{}]", options_to_string(wifibroadcast::get_wifi_card_names(m_wifi_cards),m_options)); - // Common error - not run as root - if(!SchedulingHelper::check_root()){ - std::cerr<<"wifibroadcast needs root"<warn("wifibroadcast needs root"); - assert(false); - } - m_receive_pollfds.resize(m_wifi_cards.size()); - m_active_tx_card_data.resize(m_wifi_cards.size()); - for(int i=0;i(); - tmp->seq_nr.set_store_and_debug_gaps(i,m_options.debug_packet_gaps); - tmp->rf_aggregator.set_debug_invalid_values(m_options.debug_rssi>=1); - m_per_card_calc.push_back(tmp); - m_card_is_disconnected[i]=false; - } - for(int i=0;i(false); - }else if(wifi_card.type==wifibroadcast::WIFI_CARD_TYPE_EMULATE_AIR){ - m_optional_dummy_link=std::make_unique(true); - }else{ - PcapTxRx pcapTxRx{}; - // RX part - using pcap - pcapTxRx.rx=wifibroadcast::pcap_helper::open_pcap_rx(wifi_card.name); - if(m_options.pcap_rx_set_direction){ - const auto ret=pcap_setdirection(pcapTxRx.rx, PCAP_D_IN); - if(ret!=0){ - m_console->debug("pcap_setdirection() returned {}",ret); - } - } - auto rx_pollfd = pcap_get_selectable_fd(pcapTxRx.rx); - m_receive_pollfds[i].fd = rx_pollfd; - m_receive_pollfds[i].events = POLLIN; - // TX part - using raw socket or pcap - if(m_options.tx_without_pcap){ - pcapTxRx.tx_sockfd= open_wifi_interface_as_raw_socket(wifi_card.name); - }else{ - pcapTxRx.tx=wifibroadcast::pcap_helper::open_pcap_tx(wifi_card.name); - } - m_pcap_handles.push_back(pcapTxRx); - } - } - wb::KeyPairTxRx keypair{}; - if(m_options.secure_keypair.has_value()){ - keypair= m_options.secure_keypair.value(); - }else{ - keypair=wb::generate_keypair_from_bind_phrase(); - } - m_encryptor=std::make_unique(keypair.get_tx_key(!m_options.use_gnd_identifier)); - m_decryptor=std::make_unique(keypair.get_rx_key(!m_options.use_gnd_identifier)); - m_encryptor->makeNewSessionKey(m_tx_sess_key_packet.sessionKeyNonce,m_tx_sess_key_packet.sessionKeyData); - // next session key in delta ms if packets are being fed - m_session_key_next_announce_ts = std::chrono::steady_clock::now(); - // Per libsodium documentation, the first nonce should be chosen randomly - // This selects a random nonce in 32-bit range - we therefore have still 32-bit increasing indexes left, which means tx can run indefinitely - m_nonce=randombytes_random(); -} - -WBTxRx::~WBTxRx() { - stop_receiving(); - for(auto& fd: m_receive_pollfds){ - close(fd.fd); - } - for(auto& pcapTxRx:m_pcap_handles){ - if(pcapTxRx.rx==pcapTxRx.tx){ - pcap_close(pcapTxRx.rx); - pcapTxRx.rx= nullptr; - pcapTxRx.tx= nullptr; - }else{ - if(pcapTxRx.rx!= nullptr){ - pcap_close(pcapTxRx.rx); - } - if(pcapTxRx.tx!= nullptr){ - pcap_close(pcapTxRx.tx); - } - if(pcapTxRx.tx_sockfd!=-1){ - close(pcapTxRx.tx_sockfd); - } - } - //pcap_close(pcapTxRx.rx); - //pcap_close(pcapTxRx.tx); - } -} - -void WBTxRx::tx_inject_packet(const uint8_t stream_index,const uint8_t* data, int data_len,const RadiotapHeaderTx& tx_radiotap_header,bool encrypt) { - assert(data_len<=MAX_PACKET_PAYLOAD_SIZE); - assert(stream_index>= STREAM_INDEX_MIN && stream_index<= STREAM_INDEX_MAX); - std::lock_guard guard(m_tx_mutex); - // for openhd ground station functionality - if(m_disable_all_transmissions){ - return ; - } - announce_session_key_if_needed(); - // new wifi packet - const auto packet_size= - // Radiotap header comes first - RadiotapHeaderTx::SIZE_BYTES+ - // Then the Ieee80211 header - Ieee80211HeaderRaw::SIZE_BYTES+ - // actual data - data_len+ - // encryption suffix - crypto_aead_chacha20poly1305_ABYTES; - uint8_t* packet_buff=m_tx_packet_buff.data(); - // radiotap header comes first - memcpy(packet_buff, tx_radiotap_header.getData(), - RadiotapHeaderTx::SIZE_BYTES); - // Iee80211 header comes next - // Will most likely be overridden by the driver - const auto this_packet_ieee80211_seq=m_ieee80211_seq++; - m_tx_ieee80211_hdr_openhd.write_ieee80211_seq_nr(this_packet_ieee80211_seq); - // create a new nonce for this packet - const uint64_t this_packet_nonce =m_nonce++; - RadioPort this_packet_radio_port{encrypt,stream_index}; - m_tx_ieee80211_hdr_openhd.write_radio_port_src_dst(radio_port_to_uint8_t(this_packet_radio_port)); - const auto unique_tx_id= m_options.use_gnd_identifier ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; - m_tx_ieee80211_hdr_openhd.write_unique_id_src_dst(unique_tx_id); - m_tx_ieee80211_hdr_openhd.write_nonce(this_packet_nonce); - if(m_options.enable_non_openhd_mode){ - // dirty, just overwrite the mac and inject - m_tx_ieee80211_hdr_openhd.dirty_write_dummy_fixed_src_dest_mac(); - } - //m_console->debug("Test Nonce:{}/{} {} {} {}",this_packet_nonce,m_tx_ieee80211_hdr_openhd.get_nonce(),m_tx_ieee80211_hdr_openhd.has_valid_air_gnd_id(),m_tx_ieee80211_hdr_openhd.has_valid_radio_port(), - // m_tx_ieee80211_hdr_openhd.is_data_frame()); - memcpy(packet_buff+ RadiotapHeaderTx::SIZE_BYTES, - (uint8_t*)&m_tx_ieee80211_hdr_openhd, Ieee80211HeaderRaw::SIZE_BYTES); - // Then the encrypted / validated data (including encryption / validation suffix) - uint8_t* encrypted_data_p=packet_buff+ RadiotapHeaderTx::SIZE_BYTES+ Ieee80211HeaderRaw::SIZE_BYTES; - m_encryptor->set_encryption_enabled(encrypt); - const auto before_encrypt=std::chrono::steady_clock::now(); - const auto ciphertext_len= m_encryptor->authenticate_and_encrypt(this_packet_nonce, data, data_len, encrypted_data_p); - if(m_options.debug_encrypt_time){ - m_packet_encrypt_time.add(std::chrono::steady_clock::now()-before_encrypt); - if(m_packet_encrypt_time.get_delta_since_last_reset()>std::chrono::seconds(2)){ - m_console->debug("Encrypt/validate: {}",m_packet_encrypt_time.getAvgReadable()); - m_packet_encrypt_time.reset(); - } - } - // we allocate the right size in the beginning, but check if ciphertext_len is actually matching what we calculated - // (the documentation says 'write up to n bytes' but they probably mean (write exactly n bytes unless an error occurs) - assert(data_len+crypto_aead_chacha20poly1305_ABYTES == ciphertext_len); - // we inject the packet on whatever card has the highest rx rssi right now - const bool success= inject_radiotap_packet(m_curr_tx_card.load(),packet_buff,packet_size); - if(success){ - m_tx_stats.n_injected_bytes_excluding_overhead += data_len; - m_tx_stats.n_injected_bytes_including_overhead +=packet_size; - m_tx_stats.n_injected_packets++; - } -} - -bool WBTxRx::inject_radiotap_packet(int card_index,const uint8_t* packet_buff, int packet_size) { - // inject via pcap - int len_injected=0; - // we inject the packet on whatever card has the highest rx rssi right now - const auto before_inject=std::chrono::steady_clock::now(); - if(m_optional_dummy_link){ - m_optional_dummy_link->tx_radiotap(packet_buff,packet_size); - len_injected=packet_size; - }else if(m_options.tx_without_pcap){ - len_injected=(int)write(m_pcap_handles[card_index].tx_sockfd,packet_buff,packet_size); - }else{ - len_injected=pcap_inject(m_pcap_handles[card_index].tx, packet_buff, packet_size); - //const auto len_injected=write(m_receive_pollfds[card_index].fd,packet_buff,packet_size); - } - const auto delta_inject=std::chrono::steady_clock::now()-before_inject; - if(delta_inject>=m_options.max_sane_injection_time){ - m_tx_stats.count_tx_injections_error_hint++; - } - if(m_options.debug_tx_injection_time){ - m_tx_inject_time.add(delta_inject); - if(m_tx_inject_time.get_delta_since_last_reset()>std::chrono::seconds(2)){ - m_console->debug("packet injection time: {}",m_tx_inject_time.getAvgReadable()); - m_tx_inject_time.reset(); - } - if(delta_inject>m_options.max_sane_injection_time){ - m_console->debug("Injected packet ret:{} took:{}",len_injected,MyTimeHelper::R(delta_inject)); - } - } - if (len_injected != (int) packet_size) { - // This basically should never fail - if the tx queue is full, pcap seems to wait ?! - if(m_options.tx_without_pcap){ - m_console->warn("raw sock - unable to inject packet size:{} ret:{} err:[{}]",packet_size, len_injected, strerror(errno)); - }else{ - m_console->warn("pcap -unable to inject packet size:{} ret:{} err:[{}]",packet_size, len_injected, - pcap_geterr(m_pcap_handles[card_index].tx)); - } - m_tx_stats.count_tx_dropped_packets++; - return false; - } - return true; -} - -void WBTxRx::rx_register_callback(WBTxRx::OUTPUT_DATA_CALLBACK cb) { - m_output_cb=std::move(cb); -} - -void WBTxRx::rx_register_stream_handler(std::shared_ptr handler) { - assert(handler); - m_rx_handlers[handler->radio_port]=handler; -} - -void WBTxRx::rx_unregister_stream_handler(uint8_t radio_port) { - m_rx_handlers.erase(radio_port); -} - -void WBTxRx::loop_receive_packets() { - if(m_options.receive_thread_max_realtime){ - SchedulingHelper::set_thread_params_max_realtime("WBTxRx::loop_receive_packets"); - } - std::vector packets_per_card{}; - packets_per_card.resize(m_wifi_cards.size()); - while (keep_receiving){ - if(m_optional_dummy_link){ - auto packet=m_optional_dummy_link->rx_radiotap(); - if(packet){ - on_new_packet(0,packet->data(),packet->size()); - } - continue ; - } - const int timeoutMS = (int) std::chrono::duration_cast(std::chrono::seconds(1)).count(); - int rc = poll(m_receive_pollfds.data(), m_receive_pollfds.size(), timeoutMS); - - if (rc < 0) { - if (errno == EINTR || errno == EAGAIN) continue; - m_console->warn("Poll error: {}", strerror(errno)); - } - - if (rc == 0) { - // timeout expired - if(m_options.advanced_debugging_rx){ - m_console->debug("Timeout - no packet after 1 second"); - } - recalculate_pollution_perc(); - continue; - } - // TODO Optimization: If rc>1 we have data on more than one wifi card. It would be better to alternating process a couple of packets from card 1, then card 2 or similar - for (int i = 0; rc > 0 && i < m_receive_pollfds.size(); i++) { - //m_console->debug("Got data on {}",i); - if (m_receive_pollfds[i].revents & (POLLERR | POLLNVAL)) { - if(keep_receiving){ - // we should only get errors here if the card is disconnected - m_n_receiver_errors++; - m_card_is_disconnected[i]=true; - // limit logging here - const auto elapsed=std::chrono::steady_clock::now()-m_last_receiver_error_log; - if(elapsed>std::chrono::seconds(1)){ - m_console->warn("{} receiver errors on pcap fd {} (wlan {})",m_n_receiver_errors,i,m_wifi_cards[i].name); - m_last_receiver_error_log=std::chrono::steady_clock::now(); - } - }else{ - return; - } - } - if (m_receive_pollfds[i].revents & POLLIN) { - const auto n_packets= loop_iter_pcap(i); - packets_per_card[i]=n_packets; - rc -= 1; - }else{ - packets_per_card[i]=0; - } - } - if(m_options.debug_multi_rx_packets_variance){ - std::stringstream ss; - ss<<"Packets"; - for(int i=0;idebug("{}",ss.str()); - } - recalculate_pollution_perc(); - } -} - -int WBTxRx::loop_iter_pcap(const int rx_index) { - pcap_t* ppcap=m_pcap_handles[rx_index].rx; - // loop while incoming queue is not empty - int nPacketsPolledUntilQueueWasEmpty = 0; - for (;;) { - struct pcap_pkthdr hdr{}; - const uint8_t *pkt = pcap_next(ppcap, &hdr); - if (pkt == nullptr) { - if(m_options.advanced_latency_debugging_rx){ - m_n_packets_polled_pcap.add(nPacketsPolledUntilQueueWasEmpty); - if(m_n_packets_polled_pcap.get_delta_since_last_reset()>std::chrono::seconds(1)){ - m_console->debug("m_n_packets_polled_pcap: {}",m_n_packets_polled_pcap.getAvgReadable()); - m_n_packets_polled_pcap.reset(); - } - } - break; - } - if(m_options.advanced_latency_debugging_rx){ - const auto delta=std::chrono::system_clock::now()-MyTimeHelper::to_time_point_system_clock(hdr.ts); - m_packet_host_latency.add(delta); - if(m_packet_host_latency.get_delta_since_last_reset()>std::chrono::seconds(1)){ - m_console->debug("packet latency {}",m_packet_host_latency.getAvgReadable()); - m_packet_host_latency.reset(); - } - } - on_new_packet(rx_index,pkt,hdr.len); - nPacketsPolledUntilQueueWasEmpty++; - } - return nPacketsPolledUntilQueueWasEmpty; -} - -int WBTxRx::loop_iter_raw(const int rx_index) { - // loop while incoming queue is not empty - int nPacketsPolledUntilQueueWasEmpty = 0; - for (;;) { - auto buff=std::vector(PCAP_MAX_PACKET_SIZE); - //const int ret= read(0,buff.data(),buff.size()); - const int ret= recv(m_receive_pollfds[rx_index].fd,buff.data(),buff.size(),MSG_DONTWAIT); - if (ret<=0) { - if(m_options.advanced_latency_debugging_rx){ - m_n_packets_polled_pcap.add(nPacketsPolledUntilQueueWasEmpty); - if(m_n_packets_polled_pcap.get_delta_since_last_reset()>std::chrono::seconds(1)){ - m_console->debug("m_n_packets_polled_pcap: {}",m_n_packets_polled_pcap.getAvgReadable()); - m_n_packets_polled_pcap.reset(); - } - } - break; - } - on_new_packet(rx_index,buff.data(),ret); - nPacketsPolledUntilQueueWasEmpty++; - } - return nPacketsPolledUntilQueueWasEmpty; -} - -void WBTxRx::on_new_packet(const uint8_t wlan_idx,const uint8_t *pkt,const int pkt_len) { - if(m_options.log_all_received_packets){ - m_console->debug("Got packet {} {}",wlan_idx,pkt_len); - } - const auto parsedPacket = - radiotap::rx::process_received_radiotap_packet(pkt,pkt_len); - if (parsedPacket == std::nullopt) { - // Radiotap header malformed - should never happen - if(m_options.advanced_debugging_rx){ - m_console->warn("Discarding packet due to radiotap parsing error!"); - } - return; - } - if (parsedPacket->radiotap_f_bad_fcs) { - // Bad FCS - treat as not a usable packet - if(m_options.advanced_debugging_rx){ - m_console->debug("Discarding packet due to bad FCS!"); - } - return; - } - //m_console->debug("{}",radiotap::util::radiotap_header_to_string(pkt,pkt_len)); - //m_console->debug("{}",radiotap::rx::parsed_radiotap_to_string(parsedPacket.value())); - //m_per_card_calc[wlan_idx]->rf_aggregator.on_valid_openhd_packet(parsedPacket.value()); - const uint8_t *pkt_payload = parsedPacket->payload; - const size_t pkt_payload_size = parsedPacket->payloadSize; - m_rx_stats.count_p_any++; - m_rx_stats.count_bytes_any+=pkt_payload_size; - m_rx_stats_per_card[wlan_idx].count_p_any++; - if(wlan_idx==0){ - m_pollution_total_rx_packets++; - } - const auto& rx_iee80211_hdr_openhd=*((Ieee80211HeaderOpenHD*)parsedPacket->ieee80211Header); - //m_console->debug(parsedPacket->ieee80211Header->header_as_string()); - if (!rx_iee80211_hdr_openhd.is_data_frame()) { - if(m_options.advanced_debugging_rx){ - // we only process data frames - m_console->debug("Got packet that is not a data packet {}",rx_iee80211_hdr_openhd.debug_control_field()); - } - return; - } - // All these edge cases should NEVER happen if using a proper tx/rx setup and the wifi driver isn't complete crap - if (parsedPacket->payloadSize <= 0 || parsedPacket->payloadSize > RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE) { - m_console->warn("Discarding packet due to no actual payload !"); - return; - } - // Generic packet validation end - now to the openhd specific validation(s) - if (parsedPacket->payloadSize > RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE) { - m_console->warn("Discarding packet due to payload exceeding max {}",(int) parsedPacket->payloadSize); - return; - } - if(!rx_iee80211_hdr_openhd.has_valid_air_gnd_id()){ - if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet that has not a valid unique id {}",rx_iee80211_hdr_openhd.debug_unique_ids()); - } - return; - } - const auto unique_air_gnd_id=rx_iee80211_hdr_openhd.get_valid_air_gnd_id(); - const auto unique_tx_id= m_options.use_gnd_identifier ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; - const auto unique_rx_id= m_options.use_gnd_identifier ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR : OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND; - if(unique_air_gnd_id!=unique_rx_id){ - // Rare case - when multiple RX-es are used, we might get a packet we sent on this air / gnd unit - // And on AR9271, there is a bug where the card itself gives injected packets back to us - if(unique_air_gnd_id==unique_tx_id){ - // Packet (most likely) originated from this unit - if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet back on rx {} that was injected (bug or multi rx) {}",wlan_idx,rx_iee80211_hdr_openhd.debug_unique_ids()); - } - if(wlan_idx==0){ - m_pollution_total_rx_packets--; - } - }else{ - if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet with invalid unique air gnd id {}",rx_iee80211_hdr_openhd.debug_unique_ids()); - } - } - return; - } - if(!rx_iee80211_hdr_openhd.has_valid_radio_port()){ - if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet that has not a valid radio port{}",rx_iee80211_hdr_openhd.debug_radio_ports()); - } - return; - } - const auto radio_port_raw=rx_iee80211_hdr_openhd.get_valid_radio_port(); - const RadioPort& radio_port=*(RadioPort*)&radio_port_raw; - - //m_console->debug("Packet enc:{} stream_idx:{} nonce:{}",radio_port.encrypted,radio_port.multiplex_index,nonce); - // Quite likely an openhd packet (I'd say pretty much 100%) but not validated yet - m_rx_stats.curr_n_likely_openhd_packets++; - const auto nonce = rx_iee80211_hdr_openhd.get_nonce(); - if(radio_port.multiplex_index== STREAM_INDEX_SESSION_KEY_PACKETS){ - process_session_stream_packet( - wlan_idx, - radio_port, - parsedPacket, - pkt_payload_size, - nonce); - }else{ - process_common_stream_packet( - wlan_idx, - radio_port, - pkt, - pkt_len, - parsedPacket, - pkt_payload, - pkt_payload_size, - nonce); - } -} - -void WBTxRx::process_session_stream_packet( - const uint8_t wlan_idx, - const RadioPort& radio_port, - const std::optional& parsedPacket, - const size_t pkt_payload_size, - uint64_t nonce) { - // encryption bit must always be set to off on session key packets, since - // encryption serves no purpose here - if (radio_port.encrypted) { - if (m_options.advanced_debugging_rx) { - m_console->warn( - "Cannot be session key packet - encryption flag set to true"); - } - return; - } - - if (pkt_payload_size != sizeof(SessionKeyPacket)) { - if (m_options.advanced_debugging_rx) { - m_console->warn("Cannot be session key packet - size mismatch {}", - pkt_payload_size); - } - return; - } - const SessionKeyPacket& sessionKeyPacket = - *((SessionKeyPacket*)parsedPacket->payload); - const auto decrypt_res = m_decryptor->onNewPacketSessionKeyData( - sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData); - if (decrypt_res == wb::Decryptor::SESSION_VALID_NEW || - decrypt_res == wb::Decryptor::SESSION_VALID_NOT_NEW) { - if (wlan_idx == 0) { // Pollution is calculated only on card0 - m_pollution_openhd_rx_packets++; - } - m_likely_wrong_encryption_valid_session_keys++; - auto& seq_nr_for_card=m_per_card_calc.at(wlan_idx)->seq_nr; - seq_nr_for_card.on_new_sequence_number(nonce); - m_rx_stats_per_card.at(wlan_idx).curr_packet_loss=seq_nr_for_card.get_current_loss_percent(); - } else { - m_likely_wrong_encryption_invalid_session_keys++; - } - - // A lot of invalid session keys and no valid session keys hint at a bind - // phrase mismatch - const auto elapsed_likely_wrong_key = - std::chrono::steady_clock::now() - m_likely_wrong_encryption_last_check; - if (elapsed_likely_wrong_key > std::chrono::seconds(5)) { - // No valid session key(s) and at least one invalid session key - if (m_likely_wrong_encryption_valid_session_keys == 0 && - m_likely_wrong_encryption_invalid_session_keys >= 1) { - m_rx_stats.likely_mismatching_encryption_key = true; - } else { - m_rx_stats.likely_mismatching_encryption_key = false; - } - m_likely_wrong_encryption_last_check = std::chrono::steady_clock::now(); - m_likely_wrong_encryption_valid_session_keys = 0; - m_likely_wrong_encryption_invalid_session_keys = 0; - } - - if (decrypt_res == wb::Decryptor::SESSION_VALID_NEW) { - m_console->debug("Initializing new session."); - m_rx_stats.n_received_valid_session_key_packets++; - for (const auto& handler : m_rx_handlers) { - if (auto opt_cb_session = handler.second->cb_session) { - opt_cb_session(); - } - } - } -} - -void WBTxRx::process_common_stream_packet( - const uint8_t wlan_idx, - const WBTxRx::RadioPort& radio_port, - const uint8_t* pkt, - const int pkt_len, - const std::optional parsedPacket, - const uint8_t* pkt_payload, - const size_t pkt_payload_size, - const uint64_t nonce) { - // the payload needs to include at least one byte of actual payload and the encryption suffix - static constexpr auto MIN_PACKET_PAYLOAD_SIZE=1+crypto_aead_chacha20poly1305_ABYTES; - if(pkt_payload_sizedebug("Got packet with payload of {} (min:{})",pkt_payload_size,MIN_PACKET_PAYLOAD_SIZE); - } - return; - } - - const bool valid=process_received_data_packet(wlan_idx,radio_port.multiplex_index,radio_port.encrypted,nonce,pkt_payload,pkt_payload_size); - if(valid){ - if(m_options.rx_radiotap_debug_level==1 || m_options.rx_radiotap_debug_level==4){ - m_console->debug("{}",radiotap::util::radiotap_header_to_string(pkt,pkt_len)); - } - - if(m_options.rx_radiotap_debug_level==2 || m_options.rx_radiotap_debug_level==4){ - m_console->debug("{}",radiotap::rx::parsed_radiotap_to_string(parsedPacket.value())); - } - - m_rx_stats.count_p_valid++; - m_rx_stats.count_bytes_valid+=pkt_payload_size; - - // We only use known "good" packets for those stats. - auto &this_wifi_card_stats = m_rx_stats_per_card.at(wlan_idx); - PerCardCalculators& this_wifi_card_calc= *m_per_card_calc.at(wlan_idx); - if(m_options.debug_rssi>=2){ - m_console->debug("{}",radiotap::rx::all_rf_path_to_string(parsedPacket->rf_paths)); - } - - this_wifi_card_calc.rf_aggregator.on_valid_openhd_packet(parsedPacket.value()); - if(m_options.rx_radiotap_debug_level==3 || m_options.rx_radiotap_debug_level==4){ - this_wifi_card_calc.rf_aggregator.debug_every_one_second(); - } - - this_wifi_card_stats.count_p_valid++; - if(wlan_idx==0){ - m_pollution_openhd_rx_packets++; - } - { - // Same for iee80211 seq nr - //uint16_t iee_seq_nr=parsedPacket->ieee80211Header->getSequenceNumber(); - //m_seq_nr_helper_iee80211.on_new_sequence_number(iee_seq_nr); - //m_console->debug("IEE SEQ NR PACKET LOSS {}",m_seq_nr_helper_iee80211.get_current_loss_percent()); - } - switch_tx_card_if_needed(); - } -} - -void WBTxRx::switch_tx_card_if_needed() { - if(m_wifi_cards.size()>1 && m_options.enable_auto_switch_tx_card){ - const auto elapsed=std::chrono::steady_clock::now()-m_last_highest_rssi_adjustment_tp; - if(elapsed>=HIGHEST_RSSI_ADJUSTMENT_INTERVAL){ - m_last_highest_rssi_adjustment_tp=std::chrono::steady_clock::now(); - // NEW: Instead of dealing with RSSI issues, we just take whatever card - // received the most amount of packets - std::vector per_card_packet_delta; - per_card_packet_delta.reserve(m_wifi_cards.size()); - for(int i=0;i< m_wifi_cards.size();i++){ - RxStatsPerCard& this_card_stats=m_rx_stats_per_card.at(i); - // Check if this card is behaving "okay", aka receiving packets at the time - const auto delta_valid_packets=this_card_stats.count_p_valid-m_active_tx_card_data[i].last_received_n_valid_packets; - m_active_tx_card_data[i].last_received_n_valid_packets=this_card_stats.count_p_valid; - per_card_packet_delta.push_back(delta_valid_packets); - } - int64_t best_packet_delta=per_card_packet_delta[m_curr_tx_card]; - int idx_card_highest_packet_delta=m_curr_tx_card; - for(int i=0;ibest_packet_delta+50){ - best_packet_delta=per_card_packet_delta[i]; - idx_card_highest_packet_delta=i; - } - } - if(m_curr_tx_card!=idx_card_highest_packet_delta){ - m_console->debug("Switching to card {}",idx_card_highest_packet_delta); - m_curr_tx_card=idx_card_highest_packet_delta; - } - } - } -} - -bool WBTxRx::process_received_data_packet( - int wlan_idx, - uint8_t stream_index, - bool encrypted, - const uint64_t nonce, - const uint8_t *payload_and_enc_suffix, - int payload_and_enc_suffix_size) { - std::shared_ptr> decrypted=std::make_shared>(payload_and_enc_suffix_size-crypto_aead_chacha20poly1305_ABYTES); - // after that, we have the encrypted data (and the encryption suffix) - const uint8_t* encrypted_data_with_suffix=payload_and_enc_suffix; - const auto encrypted_data_with_suffix_len = payload_and_enc_suffix_size; - - const auto before_decrypt=std::chrono::steady_clock::now(); - bool res; - if (encrypted) { - res = m_decryptor->decrypt( - nonce, - encrypted_data_with_suffix, - encrypted_data_with_suffix_len, - decrypted->data()); - } else { - res = m_decryptor->authenticate( - nonce, - encrypted_data_with_suffix, - encrypted_data_with_suffix_len, - decrypted->data()); - } - - if(res){ - if(m_options.log_all_received_validated_packets){ - m_console->debug("Got valid packet nonce:{} wlan_idx:{} encrypted:{} stream_index:{} size:{}",nonce,wlan_idx,encrypted,stream_index,payload_and_enc_suffix_size); - } - if(m_options.debug_decrypt_time){ - m_packet_decrypt_time.add(std::chrono::steady_clock::now()-before_decrypt); - if(m_packet_decrypt_time.get_delta_since_last_reset()>std::chrono::seconds(2)){ - m_console->debug("Decrypt/Validate: {}",m_packet_decrypt_time.getAvgReadable()); - m_packet_decrypt_time.reset(); - } - } - on_valid_data_packet(nonce, wlan_idx, stream_index, decrypted->data(), - decrypted->size()); - // Calculate sequence number stats per card - auto& seq_nr_for_card=m_per_card_calc.at(wlan_idx)->seq_nr; - seq_nr_for_card.on_new_sequence_number(nonce); - m_rx_stats_per_card.at(wlan_idx).curr_packet_loss=seq_nr_for_card.get_current_loss_percent(); - // Update the main loss to whichever card reports the lowest loss - int lowest_loss=INT32_MAX; - for(auto& per_card_calc: m_per_card_calc){ - auto& card_loss=per_card_calc->seq_nr; - const auto loss=card_loss.get_current_loss_percent(); - if(loss<0){ - continue ; - } - if(lossdebug("Got non-wb packet {}",radio_port); - return false; -} - -void WBTxRx::on_valid_data_packet(uint64_t nonce,int wlan_index,const uint8_t stream_index,const uint8_t *data, const int data_len) { - if(m_output_cb!= nullptr){ - m_output_cb(nonce,wlan_index,stream_index,data,data_len); - } - // find a consumer for data of this radio port - auto handler=m_rx_handlers.find(stream_index); - if(handler!=m_rx_handlers.end()){ - StreamRxHandler& rxHandler=*handler->second; - rxHandler.cb_packet(nonce,wlan_index,data,data_len); - } -} - -void WBTxRx::start_receiving() { - keep_receiving= true; - m_receive_thread=std::make_unique(&WBTxRx::loop_receive_packets, this); -} - -void WBTxRx::stop_receiving() { - keep_receiving= false; - if(m_receive_thread!= nullptr){ - if(m_receive_thread->joinable()){ - m_receive_thread->join(); - } - m_receive_thread= nullptr; - } -} - -void WBTxRx::announce_session_key_if_needed() { - const auto cur_ts = std::chrono::steady_clock::now(); - if (cur_ts >= m_session_key_next_announce_ts) { - // Announce session key - send_session_key(); - m_session_key_next_announce_ts = cur_ts + m_options.session_key_packet_interval; - } -} - -void WBTxRx::send_session_key() { - RadiotapHeaderTx tmp_radiotap_header= m_session_key_radiotap_header->thread_safe_get(); - Ieee80211HeaderOpenHD tmp_tx_hdr{}; - const auto unique_tx_id= m_options.use_gnd_identifier ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; - tmp_tx_hdr.write_unique_id_src_dst(unique_tx_id); - RadioPort radioPort{false,STREAM_INDEX_SESSION_KEY_PACKETS}; - tmp_tx_hdr.write_radio_port_src_dst(radio_port_to_uint8_t(radioPort)); - tmp_tx_hdr.write_ieee80211_seq_nr(m_ieee80211_seq++); - tmp_tx_hdr.write_nonce(m_nonce++); - auto packet=RadiotapHelper::create_radiotap_wifi_packet(tmp_radiotap_header,*(Ieee80211HeaderRaw*)&tmp_tx_hdr, - (uint8_t *)&m_tx_sess_key_packet, sizeof(SessionKeyPacket)); - const int packet_size=(int)packet.size(); - // NOTE: Session key is always sent via card 0 since otherwise we might pick up the session key intended for the ground unit - // from the air unit ! - const bool success = inject_radiotap_packet(0,packet.data(),packet_size); - if(success){ - // These bytes only count as "including overhead" - m_tx_stats.n_injected_bytes_including_overhead +=packet_size; - m_tx_stats.n_injected_packets++; - } -} - -WBTxRx::TxStats WBTxRx::get_tx_stats() { - m_tx_stats.curr_bits_per_second_excluding_overhead= - m_tx_bitrate_calculator_excluding_overhead.get_last_or_recalculate(m_tx_stats.n_injected_bytes_excluding_overhead); - m_tx_stats.curr_bits_per_second_including_overhead= - m_tx_bitrate_calculator_including_overhead.get_last_or_recalculate(m_tx_stats.n_injected_bytes_including_overhead); - m_tx_stats.curr_packets_per_second=m_tx_packets_per_second_calculator.get_last_or_recalculate(m_tx_stats.n_injected_packets); - return m_tx_stats; -} - -WBTxRx::RxStats WBTxRx::get_rx_stats() { - WBTxRx::RxStats ret=m_rx_stats; - ret.curr_bits_per_second=m_rx_bitrate_calculator.get_last_or_recalculate(ret.count_bytes_valid); - ret.curr_packets_per_second=m_rx_packets_per_second_calculator.get_last_or_recalculate(ret.count_p_valid); - return ret; -} - -WBTxRx::RxStatsPerCard WBTxRx::get_rx_stats_for_card(int card_index) { - return m_rx_stats_per_card.at(card_index); -} - -void WBTxRx::rx_reset_stats() { - m_rx_stats=RxStats{}; - m_rx_bitrate_calculator.reset(); - m_rx_packets_per_second_calculator.reset(); - for(int i=0;ireset_all(); - } -} - -int WBTxRx::get_curr_active_tx_card_idx() { - return m_curr_tx_card; -} - -void WBTxRx::set_passive_mode(bool passive) { - m_disable_all_transmissions=passive; -} - -bool WBTxRx::get_card_has_disconnected(int card_idx) { - if(card_idx>=m_wifi_cards.size()){ - return true; - } - return m_card_is_disconnected[card_idx]; -} - -void WBTxRx::tx_reset_stats() { - m_tx_stats=TxStats{}; - m_tx_packets_per_second_calculator.reset(); - m_tx_bitrate_calculator_excluding_overhead.reset(); - m_tx_bitrate_calculator_including_overhead.reset(); -} - -void WBTxRx::recalculate_pollution_perc() { - const auto elapsed=std::chrono::steady_clock::now()-m_last_pollution_calculation; - if(elapsed<=std::chrono::seconds(1)){ - return ; - } - m_last_pollution_calculation=std::chrono::steady_clock::now(); - const auto non_openhd_packets=m_pollution_total_rx_packets-m_pollution_openhd_rx_packets; - if(m_pollution_total_rx_packets>0){ - double perc_non_openhd_packets=(double)non_openhd_packets/(double)m_pollution_total_rx_packets*100.0; - //m_console->debug("Link pollution: {}% [{}:{}]",perc_non_openhd_packets,non_openhd_packets,m_pollution_total_rx_packets); - m_rx_stats.curr_link_pollution_perc=std::ceil(perc_non_openhd_packets); - //curr_link_pollution_perc=std::ceil(); - }else{ - m_rx_stats.curr_link_pollution_perc=0; - } - const int elapsed_ms=static_cast(std::chrono::duration_cast(elapsed).count()); - m_rx_stats.curr_n_foreign_packets_pps=(int)non_openhd_packets * 1000 / elapsed_ms; - m_pollution_total_rx_packets=0; - m_pollution_openhd_rx_packets=0; -} - -std::string WBTxRx::tx_stats_to_string(const WBTxRx::TxStats& data) { - return fmt::format("TxStats[injected packets:{} bytes:{} tx error hint/dropped:{}:{} pps:{} bps:{}:{}]", - data.n_injected_packets,data.n_injected_bytes_including_overhead, - data.count_tx_injections_error_hint,data.count_tx_dropped_packets, - data.curr_packets_per_second, - StringHelper::bitrate_readable(data.curr_bits_per_second_excluding_overhead), - StringHelper::bitrate_readable(data.curr_bits_per_second_including_overhead)); -} -std::string WBTxRx::rx_stats_to_string(const WBTxRx::RxStats& data) { - return fmt::format("RxStats[packets any:{} session:{} valid:{} Loss:{}% pps:{} bps:{} foreign:{}%/{}pps likely_key_mismatch:{}]", - data.count_p_any,data.n_received_valid_session_key_packets,data.count_p_valid, - data.curr_lowest_packet_loss,data.curr_packets_per_second,data.curr_bits_per_second, - data.curr_link_pollution_perc,data.curr_n_foreign_packets_pps, - data.likely_mismatching_encryption_key); -} -std::string WBTxRx::rx_stats_per_card_to_string( - const WBTxRx::RxStatsPerCard& data) { - return fmt::format("RxStatsCard{}[packets total:{} valid:{}, loss:{}%]",data.card_index, - data.count_p_any,data.count_p_valid,data.curr_packet_loss); -} -std::string WBTxRx::options_to_string(const std::vector& wifi_cards,const WBTxRx::Options& options) { - return fmt::format("Id:{} Cards:{} Key:{} ",options.use_gnd_identifier ? "Ground":"Air",StringHelper::string_vec_as_string(wifi_cards), - options.secure_keypair.has_value() ? "Custom" : "Default(openhd)"); -} - -RadiotapRxRfAggregator::CardKeyRfIndicators WBTxRx::get_rx_rf_stats_for_card( - int card_index) { - return m_per_card_calc.at(card_index)->rf_aggregator.get_current(); -} - -std::shared_ptr WBTxRx::get_dummy_link() { - return m_optional_dummy_link; -} - -void WBTxRx::PerCardCalculators::reset_all() { - seq_nr.reset(); - rf_aggregator.reset(); -} diff --git a/wifibroadcast/WBVideoStreamTx.cpp b/wifibroadcast/WBVideoStreamTx.cpp deleted file mode 100644 index 53d0216b..00000000 --- a/wifibroadcast/WBVideoStreamTx.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// -// Created by consti10 on 06.01.24. -// - -#include "WBVideoStreamTx.h" - -#include "BlockSizeHelper.hpp" -#include "SchedulingHelper.hpp" - -struct CodecConfigPacket{ - uint8_t codec_type; - uint16_t config_data_len; - // config_data_len bytes follow -}; - -WBVideoStreamTx::WBVideoStreamTx( - std::shared_ptr txrx, WBVideoStreamTx::Options options1, - std::shared_ptr radiotap_header_holder) - :options(options1),m_txrx(txrx),m_radiotap_header_holder(std::move(radiotap_header_holder)) -{ - assert(m_txrx); - if(options.opt_console){ - m_console=options.opt_console; - }else{ - m_console=wifibroadcast::log::create_or_get("wb_tx"+std::to_string(options.radio_port)); - } - assert(m_console); - m_block_queue=std::make_unique(options.frame_queue_size); - m_fec_encoder = std::make_unique(); - auto cb=[this](const uint8_t* packet,int packet_len){ - send_packet(packet,packet_len); - }; - m_fec_encoder->m_out_cb =cb; - m_process_data_thread_run=true; - m_process_data_thread=std::make_unique(&WBVideoStreamTx::loop_process_data, this); -} - -WBVideoStreamTx::~WBVideoStreamTx() { - m_process_data_thread_run= false; - if(m_process_data_thread && m_process_data_thread->joinable()){ - m_process_data_thread->join(); - } -} - -void WBVideoStreamTx::set_config_data( - uint8_t codec_type, std::shared_ptr> config_buff) { - auto config=std::make_shared(); - config->codec_type=codec_type; - config->config_buff=config_buff; - std::lock_guard guard(m_codec_config_mutex); - m_codec_config=config; -} - -bool WBVideoStreamTx::enqueue_frame( - std::shared_ptr> frame, int max_block_size, - int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time) { - auto item=std::make_shared(); - item->frame=frame; - item->max_block_size=max_block_size; - item->fec_overhead_perc=fec_overhead_perc; - item->creation_time=creation_time; - const bool res= m_block_queue->try_enqueue(item); - return res; -} - -void WBVideoStreamTx::loop_process_data() { - if(options.dequeue_thread_max_realtime){ - SchedulingHelper::set_thread_params_max_realtime("WBVideoStreamTx::loop"); - } - std::chrono::steady_clock::time_point last_config=std::chrono::steady_clock::now(); - while (m_process_data_thread_run){ - auto opt_frame=m_block_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); - if(opt_frame.has_value()){ - auto frame=opt_frame.value(); - process_enqueued_frame(*frame); - } - const auto now=std::chrono::steady_clock::now(); - if(now-last_config>=options.codec_config_interval){ - if(send_video_config()){ - last_config=now; - } - } - } -} - -void WBVideoStreamTx::process_enqueued_frame(const EnqueuedFrame & enq_frame) { - //TODO: Figure out the ideal fragment size for this frame - const int n_primary_fragments=blocksize::div_ceil(enq_frame.frame->size(),FEC_PACKET_MAX_PAYLOAD_SIZE); - const int n_secondary_fragments= calculate_n_secondary_fragments(n_primary_fragments, enq_frame.fec_overhead_perc); - m_fec_encoder->fragment_and_encode( - enq_frame.frame->data(), enq_frame.frame->size(),n_primary_fragments, - n_secondary_fragments); -} - -void WBVideoStreamTx::send_packet(const uint8_t *packet, int packet_len) { - const auto radiotap_header=m_radiotap_header_holder->thread_safe_get(); - const bool encrypt=m_enable_encryption.load(); - m_txrx->tx_inject_packet(options.radio_port,packet,packet_len,radiotap_header,encrypt); -} - -bool WBVideoStreamTx::send_video_config() { - std::lock_guard guard(m_codec_config_mutex); - if(m_codec_config== nullptr)return false; - auto& config_buff=*m_codec_config->config_buff; - assert(!config_buff.empty() && config_buff.size()fragment_and_encode(config_buff.data(),config_buff.size(),1,0); - return true; -} diff --git a/wifibroadcast/WBVideoStreamTx.h b/wifibroadcast/WBVideoStreamTx.h deleted file mode 100644 index 9a6390e8..00000000 --- a/wifibroadcast/WBVideoStreamTx.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by consti10 on 06.01.24. -// - -#ifndef WIFIBROADCAST_WBVIDEOSTREAMTX_H -#define WIFIBROADCAST_WBVIDEOSTREAMTX_H - -#include -#include -#include - -#include "fec/FEC.h" -#include "SimpleStream.hpp" -#include "HelperSources/TimeHelper.hpp" -#include "WBTxRx.h" -#include "fec/FECEncoder.h" -#include "FunkyQueue.h" - -class WBVideoStreamTx { - public: - struct Options { - // needs to match the radio port of the corresponding rx - uint8_t radio_port = 0; - int frame_queue_size=2; - // overwrite the console used for logging - std::shared_ptr opt_console=nullptr; - // set sched_param = max realtime on the thread that dequeues and injects the packets - bool dequeue_thread_max_realtime= true; - std::chrono::milliseconds codec_config_interval=std::chrono::seconds(1); - }; - WBVideoStreamTx(std::shared_ptr txrx,Options options,std::shared_ptr radiotap_header_holder); - WBVideoStreamTx(const WBVideoStreamTx&) = delete; - WBVideoStreamTx&operator=(const WBVideoStreamTx&) = delete; - ~WBVideoStreamTx(); - void set_config_data(uint8_t codec_type,std::shared_ptr> config_buff); - bool enqueue_frame(std::shared_ptr> frame,int max_block_size,int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now()); - std::atomic_int32_t in_fps=0; - std::atomic_int32_t in_bps=0; - std::atomic_int32_t out_pps=0; - private: - struct EnqueuedFrame { - std::chrono::steady_clock::time_point enqueue_time_point=std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now(); - int max_block_size; - int fec_overhead_perc; - std::shared_ptr> frame= nullptr; - }; - struct CodecConfigData{ - uint8_t codec_type; - std::shared_ptr> config_buff=nullptr; - }; - private: - const Options options; - std::shared_ptr m_txrx; - std::shared_ptr m_radiotap_header_holder; - std::shared_ptr m_console; - // On the tx, either one of those two is active at the same time - std::unique_ptr m_fec_encoder = nullptr; - std::unique_ptr m_process_data_thread; - using FrameQueueType=FunkyQueue>; - std::unique_ptr m_block_queue; - bool m_process_data_thread_run=true; - void loop_process_data(); - void process_enqueued_frame(const EnqueuedFrame& enq_frame); - void send_packet(const uint8_t* packet,int packet_len); - bool send_video_config(); - std::shared_ptr m_codec_config= nullptr; - std::mutex m_codec_config_mutex; - std::atomic m_enable_encryption=true; -}; - -#endif // WIFIBROADCAST_WBVIDEOSTREAMTX_H diff --git a/wifibroadcast/WiFiCard.h b/wifibroadcast/WiFiCard.h deleted file mode 100644 index db397e82..00000000 --- a/wifibroadcast/WiFiCard.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Created by consti10 on 13.09.23. -// - -#ifndef WIFIBROADCAST_WIFICARD_H -#define WIFIBROADCAST_WIFICARD_H - -#include -#include -#include - -namespace wifibroadcast{ -// In OpenHD, we have a quite extensive WiFiCard abstraction - -// in wifibroadcast, we are a bit simpler -// (But we require info for quirks) -// RTL8812AU driver requires a quirk regarding rssi -static constexpr auto WIFI_CARD_TYPE_UNKNOWN=0; -static constexpr auto WIFI_CARD_TYPE_RTL8812AU=1; -static constexpr auto WIFI_CARD_TYPE_EMULATE_AIR=2; -static constexpr auto WIFI_CARD_TYPE_EMULATE_GND=3; -struct WifiCard{ - std::string name; - int type; -}; -static std::vector get_wifi_card_names(const std::vector& cards){ - std::vector ret; - for(const auto& card:cards){ - ret.push_back(card.name); - } - return ret; -} - -static WifiCard create_card_emulate(bool is_air_card){ - return WifiCard{is_air_card?"emu_air" : "emu_gnd",is_air_card ? WIFI_CARD_TYPE_EMULATE_AIR : WIFI_CARD_TYPE_EMULATE_GND}; -} -} - -#endif // WIFIBROADCAST_WIFICARD_H diff --git a/cmake/FindPCAP.cmake b/wifibroadcast/cmake/FindPCAP.cmake similarity index 100% rename from cmake/FindPCAP.cmake rename to wifibroadcast/cmake/FindPCAP.cmake diff --git a/cmake/FindSodium.cmake b/wifibroadcast/cmake/FindSodium.cmake similarity index 100% rename from cmake/FindSodium.cmake rename to wifibroadcast/cmake/FindSodium.cmake diff --git a/wifibroadcast/dummy_link/DummyLink.cpp b/wifibroadcast/dummy_link/DummyLink.cpp deleted file mode 100644 index e8047b93..00000000 --- a/wifibroadcast/dummy_link/DummyLink.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// -// Created by consti10 on 07.01.24. -// - -#include "DummyLink.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../HelperSources/SocketHelper.hpp" -#include "../HelperSources/SchedulingHelper.hpp" - -// From http://www.atakansarioglu.com/linux-ipc-inter-process-messaging-linux-domain-socket-fifo-pipe-shared-memory-shm-example/ - -static sockaddr_un create_adr(const std::string& name){ - // Unix domain socket file address. - struct sockaddr_un address; - address.sun_family = AF_UNIX; - strcpy(address.sun_path, name.c_str()); - return address; -} - -static int create_socket_read(const std::string& name){ - auto address= create_adr(name); - // Delete the old socket file. - unlink(name.c_str()); - // Create a unix domain socket. - int fd; - if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - std::cout << "Receiver: Cannot create socket" << std::endl; - return -1; - } - - // Bind the socket to the address. - if(bind(fd, (struct sockaddr *)&address, sizeof(sockaddr_un)) != 0) { - std::cout << "Receiver: Cannot bind socket" << std::endl; - return -1; - } - return fd; -} - -static int create_socket_send(){ - // Create a unix domain socket. - int fd; - if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - std::cout << "Sender: Cannot create socket" << std::endl; - return -1; - } - return fd; -} - -static void send_data(int fd,const std::string& name,const uint8_t* data,int data_len){ - auto address= create_adr(name); - if(sendto(fd, data,data_len, 0, (struct sockaddr *)&address, sizeof(sockaddr_un)) !=data_len) { - //std::cout << "Client: Cannot send" << std::endl; - } - //std::cout<<"Sent:"<( - 1000); - m_keep_receiving= true; - m_receive_thread=std::make_unique(&DummyLink::loop_rx, this); -} - -DummyLink::~DummyLink() { - m_keep_receiving= false; - shutdown(m_fd_rx, SHUT_RDWR); - close(m_fd_rx); - m_receive_thread->join(); - m_receive_thread= nullptr; - close(m_fd_tx); -} - -void DummyLink::tx_radiotap(const uint8_t *packet_buff, int packet_size) { - const bool drop= should_drop_packet(); - if(!drop){ - send_data(m_fd_tx,m_fn_tx,packet_buff,packet_size); - } -} - -std::shared_ptr> DummyLink::rx_radiotap() { - std::shared_ptr packet= nullptr; - static constexpr std::int64_t timeout_usecs=100*1000; - auto opt_packet=m_rx_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); - if(opt_packet.has_value()){ - // dequeued frame - return opt_packet.value()->buff; - } - return nullptr; -} - -void DummyLink::loop_rx() { - SchedulingHelper::set_thread_params_max_realtime("DummyLink::loop_rx"); - auto read_buffer=std::make_shared>(MAX_MTU_INCLUDING_HEADER); - while (m_keep_receiving){ - //auto packet= read_data(m_fd_rx); - //auto size=recvfrom(fd, buff->data(), buff->size(), MSG_DONTWAIT, NULL, NULL); - auto size = recv(m_fd_rx, read_buffer->data(), read_buffer->size(), MSG_WAITALL); - if(size>0){ - auto packet=std::make_shared>(read_buffer->data(),read_buffer->data()+size); - //std::cout<<"Got packet"<size()<(); - item->buff=packet; - const auto success=m_rx_queue->try_enqueue(item); - if(!success){ - // Should never happen - } - } - //std::cout<<"ARGH"< -#include -#include -#include -#include -#include -#include -#include -#include "../FunkyQueue.h" -#include - -// TODO: Write something that emulates a wb link (tx, rx) -// using linux shm or similar -class DummyLink { -public: - explicit DummyLink(bool is_air); - ~DummyLink(); - void tx_radiotap(const uint8_t* packet_buff, int packet_size); - std::shared_ptr> rx_radiotap(); - void set_drop_mode(int drop_mode); -private: - const bool m_is_air; - int m_fd_tx; - int m_fd_rx; - std::string m_fn_tx; - std::string m_fn_rx; - std::unique_ptr m_receive_thread; - void loop_rx(); - bool m_keep_receiving= true; - // Drop packets with a probability of 5% - bool should_drop_packet(); - int next_random_number_0_100(){ - return m_dist100(m_mt); - } - std::mt19937 m_mt; - std::uniform_int_distribution<> m_dist100{0,100}; - struct RxPacket{ - std::shared_ptr> buff; - }; - using RxPacketQueueType=FunkyQueue>; - std::unique_ptr m_rx_queue; - std::atomic_int m_drop_mode=0; -}; - - -#endif //OPENHD_DUMMYLINK_H diff --git a/wifibroadcast/encryption/Encryption.cpp b/wifibroadcast/encryption/Encryption.cpp deleted file mode 100644 index 90615a46..00000000 --- a/wifibroadcast/encryption/Encryption.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// Created by consti10 on 13.08.23. -// - -#include "Encryption.h" - -#include -#include -#include - -#include -#include "../wifibroadcast_spdlog.h" - -wb::KeyPairTxRx wb::generate_keypair_random() { - KeyPairTxRx ret{}; - crypto_box_keypair(ret.key_1.public_key.data(), ret.key_1.secret_key.data()); - crypto_box_keypair(ret.key_2.public_key.data(), ret.key_2.secret_key.data()); - return ret; -} - -// Salts generated once using https://www.random.org/cgi-bin/randbyte?nbytes=16&format=d -// We want deterministic seed from a pw, and are only interested in making it impossible to reverse the process (even though the salt is plain text) -static constexpr std::array OHD_SALT_AIR{192,189,216,102,56,153,154,92,228,26,49,209,157,7,128,207}; -static constexpr std::array OHD_SALT_GND{179,30,150,20,17,200,225,82,48,64,18,130,89,62,83,234}; - -std::array -wb::create_seed_from_password_openhd_salt(const std::string& pw, - bool use_salt_air) { - const auto salt = use_salt_air ? OHD_SALT_AIR : OHD_SALT_GND; - std::array key{}; - if (crypto_pwhash(key.data(), key.size(), pw.c_str(), pw.length(), salt.data(), - crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, - crypto_pwhash_ALG_DEFAULT) != 0) { - std::cerr<<"ERROR: cannot create_seed_from_password_openhd_salt"< wb::create_onetimeauth_subkey( - const uint64_t& nonce, const std::array& session_key) { - // sub-key for this packet - std::array subkey{}; - // We only have an 8 byte nonce, this should be enough entropy - std::array nonce_buf{0}; - memcpy(nonce_buf.data(),(uint8_t*)&nonce,8); - crypto_core_hchacha20(subkey.data(),nonce_buf.data(),session_key.data(), nullptr); - return subkey; -} \ No newline at end of file diff --git a/wifibroadcast/encryption/KeyPair.h b/wifibroadcast/encryption/KeyPair.h deleted file mode 100644 index 40adc7fe..00000000 --- a/wifibroadcast/encryption/KeyPair.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef KEY_HPP -#define KEY_HPP -#include - -#include -#include - -namespace wb { -// A wb key consists of a public and secret key -struct Key { - std::array public_key; - std::array secret_key; - int operator==(const Key& other)const{ - return std::equal(std::begin(public_key), std::end(public_key), std::begin(other.public_key)) && - std::equal(std::begin(secret_key), std::end(secret_key), std::begin(other.secret_key)); - } -}__attribute__ ((packed));; -static_assert(sizeof(Key)==crypto_box_PUBLICKEYBYTES+crypto_box_SECRETKEYBYTES); - -// A wb keypair are 2 keys, one for transmitting, one for receiving -// (Since both ground and air unit talk bidirectional) -// We use a different key for the down-link / uplink, respective -static constexpr const int KEYPAIR_RAW_SIZE=32*4; -struct KeyPairTxRx { - Key key_1; - Key key_2; - Key get_tx_key(bool is_air){ - return is_air ? key_1 : key_2; - } - Key get_rx_key(bool is_air){ - return is_air ? key_2 : key_1; - } - int operator==(const KeyPairTxRx& other)const{ - return key_1==other.key_1 && key_2==other.key_2; - } - static std::array as_raw(const KeyPairTxRx& keypair); - static KeyPairTxRx from_raw(const std::array& raw); -}__attribute__ ((packed)); -static_assert(sizeof(KeyPairTxRx)==2*sizeof(Key)); -static_assert(sizeof(KeyPairTxRx)==KEYPAIR_RAW_SIZE); - -} // namespace wb - -#endif // KEY_HPP diff --git a/executables/benchmark.cpp b/wifibroadcast/executables/benchmark.cpp similarity index 95% rename from executables/benchmark.cpp rename to wifibroadcast/executables/benchmark.cpp index e5a8c934..bed88823 100644 --- a/executables/benchmark.cpp +++ b/wifibroadcast/executables/benchmark.cpp @@ -17,7 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "../wifibroadcast/HelperSources/Benchmark.hpp" +#include "../src/HelperSources/Benchmark.hpp" #include @@ -27,13 +27,13 @@ #include #include -#include "../wifibroadcast/HelperSources/RandomBufferPot.hpp" -#include "../wifibroadcast/HelperSources/SchedulingHelper.hpp" -#include "../wifibroadcast/encryption/Decryptor.h" -#include "../wifibroadcast/encryption/Encryption.h" -#include "../wifibroadcast/encryption/Encryptor.h" -#include "../wifibroadcast/fec/FEC.h" -#include "../wifibroadcast/fec/FECEncoder.h" +#include "../src/HelperSources/RandomBufferPot.hpp" +#include "../src/HelperSources/SchedulingHelper.hpp" +#include "../src/encryption/Decryptor.h" +#include "../src/encryption/Encryption.h" +#include "../src/encryption/Encryptor.h" +#include "../src/fec/FEC.h" +#include "../src/fec/FECEncoder.h" // Test the FEC encoding / decoding and Encryption / Decryption performance // (throughput) of this system diff --git a/executables/example_hello.cpp b/wifibroadcast/executables/example_hello.cpp similarity index 95% rename from executables/example_hello.cpp rename to wifibroadcast/executables/example_hello.cpp index bd2fba68..33a0cea6 100644 --- a/executables/example_hello.cpp +++ b/wifibroadcast/executables/example_hello.cpp @@ -2,10 +2,10 @@ // Created by consti10 on 01.07.23. // -#include "../wifibroadcast/WBStreamRx.h" -#include "../wifibroadcast/WBStreamTx.h" -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/WBStreamRx.h" +#include "../src/WBStreamTx.h" +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" /** * Simple demo application that sends out hello messages and listens for hello diff --git a/executables/example_pollute.cpp b/wifibroadcast/executables/example_pollute.cpp similarity index 90% rename from executables/example_pollute.cpp rename to wifibroadcast/executables/example_pollute.cpp index 968e2cea..4caa7a1b 100644 --- a/executables/example_pollute.cpp +++ b/wifibroadcast/executables/example_pollute.cpp @@ -2,11 +2,11 @@ // Created by consti10 on 09.08.23. // -#include "../wifibroadcast/HelperSources/RandomBufferPot.hpp" -#include "../wifibroadcast/WBStreamRx.h" -#include "../wifibroadcast/WBStreamTx.h" -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/HelperSources/RandomBufferPot.hpp" +#include "../src/WBStreamRx.h" +#include "../src/WBStreamTx.h" +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" /** * Simple demo application that pollutes a given wifi space diff --git a/executables/example_udp.cpp b/wifibroadcast/executables/example_udp.cpp similarity index 93% rename from executables/example_udp.cpp rename to wifibroadcast/executables/example_udp.cpp index 21a73aac..94cda563 100644 --- a/executables/example_udp.cpp +++ b/wifibroadcast/executables/example_udp.cpp @@ -2,14 +2,14 @@ // Created by consti10 on 30.06.23. // -#include "../wifibroadcast/HelperSources/SocketHelper.hpp" -#include "../wifibroadcast/HelperSources/TimeHelper.hpp" -#include "../wifibroadcast/WBStreamRx.h" -#include "../wifibroadcast/WBStreamTx.h" -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/legacy/WBStreamRxUDP.h" -#include "../wifibroadcast/legacy/WBStreamTxUDP.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/HelperSources/SocketHelper.hpp" +#include "../src/HelperSources/TimeHelper.hpp" +#include "../src/WBStreamRx.h" +#include "../src/WBStreamTx.h" +#include "../src/WBTxRx.h" +#include "../src/legacy/WBStreamRxUDP.h" +#include "../src/legacy/WBStreamTxUDP.h" +#include "../src/wifibroadcast_spdlog.h" #include "RandomBufferPot.hpp" /** diff --git a/executables/injection_rate_test.cpp b/wifibroadcast/executables/injection_rate_test.cpp similarity index 98% rename from executables/injection_rate_test.cpp rename to wifibroadcast/executables/injection_rate_test.cpp index fb5ee4ad..f8d98b9b 100644 --- a/executables/injection_rate_test.cpp +++ b/wifibroadcast/executables/injection_rate_test.cpp @@ -2,10 +2,10 @@ // Created by consti10 on 25.07.23. // -#include "../wifibroadcast/WBStreamRx.h" -#include "../wifibroadcast/WBStreamTx.h" -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/WBStreamRx.h" +#include "../src/WBStreamTx.h" +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" #include "DummyStreamGenerator.hpp" #include "RandomBufferPot.hpp" #include "Rates.hpp" diff --git a/executables/socket_helper_test.cpp b/wifibroadcast/executables/socket_helper_test.cpp similarity index 97% rename from executables/socket_helper_test.cpp rename to wifibroadcast/executables/socket_helper_test.cpp index 4cffb35e..add127bf 100644 --- a/executables/socket_helper_test.cpp +++ b/wifibroadcast/executables/socket_helper_test.cpp @@ -2,7 +2,7 @@ // Created by consti10 on 21.04.22. // -#include "../wifibroadcast/HelperSources/SocketHelper.hpp" +#include "../src/HelperSources/SocketHelper.hpp" static void test_send_and_receive() { static constexpr auto XPORT = 5600; diff --git a/executables/test_dummy_link.cpp b/wifibroadcast/executables/test_dummy_link.cpp similarity index 97% rename from executables/test_dummy_link.cpp rename to wifibroadcast/executables/test_dummy_link.cpp index 3a5edc3c..67d79d41 100644 --- a/executables/test_dummy_link.cpp +++ b/wifibroadcast/executables/test_dummy_link.cpp @@ -4,8 +4,7 @@ #include -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/dummy_link/DummyLink.h" +#include "../src/WBTxRx.h" #include "Helper.hpp" static std::vector>> diff --git a/executables/test_listen.cpp b/wifibroadcast/executables/test_listen.cpp similarity index 95% rename from executables/test_listen.cpp rename to wifibroadcast/executables/test_listen.cpp index b1d28491..977fd34a 100644 --- a/executables/test_listen.cpp +++ b/wifibroadcast/executables/test_listen.cpp @@ -2,8 +2,8 @@ // Created by consti10 on 07.10.23. // Uses WBTxRx to listen to all (openhd and non openhd) traffic // -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" int main(int argc, char *const *argv) { std::string card = "wlxac9e17596103"; diff --git a/executables/test_queue.cpp b/wifibroadcast/executables/test_queue.cpp similarity index 95% rename from executables/test_queue.cpp rename to wifibroadcast/executables/test_queue.cpp index 52ce521a..c55ad849 100644 --- a/executables/test_queue.cpp +++ b/wifibroadcast/executables/test_queue.cpp @@ -8,9 +8,9 @@ #include #include -#include "../wifibroadcast/FunkyQueue.h" -#include "../wifibroadcast/HelperSources/Helper.hpp" -#include "../wifibroadcast/HelperSources/TimeHelper.hpp" +#include "../src//HelperSources/Helper.hpp" +#include "../src//HelperSources/TimeHelper.hpp" +#include "../src/FunkyQueue.h" struct TestElement { std::chrono::steady_clock::time_point tp; diff --git a/executables/test_txrx.cpp b/wifibroadcast/executables/test_txrx.cpp similarity index 94% rename from executables/test_txrx.cpp rename to wifibroadcast/executables/test_txrx.cpp index 43a63d26..853978db 100644 --- a/executables/test_txrx.cpp +++ b/wifibroadcast/executables/test_txrx.cpp @@ -2,10 +2,10 @@ // Created by consti10 on 27.06.23. // -#include "../wifibroadcast/WBStreamRx.h" -#include "../wifibroadcast/WBStreamTx.h" -#include "../wifibroadcast/WBTxRx.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src/WBStreamRx.h" +#include "../src/WBStreamTx.h" +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" #include "RandomBufferPot.hpp" int main(int argc, char *const *argv) { diff --git a/executables/udp_generator_validator.cpp b/wifibroadcast/executables/udp_generator_validator.cpp similarity index 97% rename from executables/udp_generator_validator.cpp rename to wifibroadcast/executables/udp_generator_validator.cpp index a2d025d1..f5b51513 100644 --- a/executables/udp_generator_validator.cpp +++ b/wifibroadcast/executables/udp_generator_validator.cpp @@ -23,10 +23,10 @@ #include #include -#include "../wifibroadcast/HelperSources/Helper.hpp" -#include "../wifibroadcast/HelperSources/RandomBufferPot.hpp" -#include "../wifibroadcast/HelperSources/SequenceNumberDebugger.hpp" -#include "../wifibroadcast/HelperSources/SocketHelper.hpp" +#include "../src/HelperSources/Helper.hpp" +#include "../src/HelperSources/RandomBufferPot.hpp" +#include "../src/HelperSources/SequenceNumberDebugger.hpp" +#include "../src/HelperSources/SocketHelper.hpp" struct Options { // size of each packet diff --git a/executables/udp_packet_drop_util.cpp b/wifibroadcast/executables/udp_packet_drop_util.cpp similarity index 92% rename from executables/udp_packet_drop_util.cpp rename to wifibroadcast/executables/udp_packet_drop_util.cpp index 5d4a618e..683a4d90 100644 --- a/executables/udp_packet_drop_util.cpp +++ b/wifibroadcast/executables/udp_packet_drop_util.cpp @@ -4,8 +4,8 @@ // #include -#include "../wifibroadcast/HelperSources/EmulatedPacketDrop.hpp" -#include "../wifibroadcast/HelperSources/SocketHelper.hpp" +#include "../src/HelperSources/EmulatedPacketDrop.hpp" +#include "../src/HelperSources/SocketHelper.hpp" static std::unique_ptr udp_receiver; static std::unique_ptr udp_forwarder; diff --git a/executables/unit_test.cpp b/wifibroadcast/executables/unit_test.cpp similarity index 96% rename from executables/unit_test.cpp rename to wifibroadcast/executables/unit_test.cpp index 6b09df08..a116e815 100644 --- a/executables/unit_test.cpp +++ b/wifibroadcast/executables/unit_test.cpp @@ -24,16 +24,16 @@ #include #include -#include "../wifibroadcast/HelperSources/Helper.hpp" -#include "../wifibroadcast/Ieee80211Header.hpp" -#include "../wifibroadcast/encryption/Decryptor.h" -#include "../wifibroadcast/encryption/Encryption.h" -#include "../wifibroadcast/encryption/EncryptionFsUtils.h" -#include "../wifibroadcast/encryption/Encryptor.h" -#include "../wifibroadcast/fec/FEC.h" -#include "../wifibroadcast/fec/FECDecoder.h" -#include "../wifibroadcast/fec/FECEncoder.h" -#include "../wifibroadcast/wifibroadcast_spdlog.h" +#include "../src//encryption/EncryptionFsUtils.h" +#include "../src/HelperSources/Helper.hpp" +#include "../src/Ieee80211Header.hpp" +#include "../src/encryption/Decryptor.h" +#include "../src/encryption/Encryption.h" +#include "../src/encryption/Encryptor.h" +#include "../src/fec/FEC.h" +#include "../src/fec/FECDecoder.h" +#include "../src/fec/FECEncoder.h" +#include "../src/wifibroadcast_spdlog.h" // Simple unit testing for the FEC lib that doesn't require wifi cards diff --git a/executables/wfb_keygen.cpp b/wifibroadcast/executables/wfb_keygen.cpp similarity index 95% rename from executables/wfb_keygen.cpp rename to wifibroadcast/executables/wfb_keygen.cpp index 4626d550..e6325aab 100644 --- a/executables/wfb_keygen.cpp +++ b/wifibroadcast/executables/wfb_keygen.cpp @@ -22,8 +22,8 @@ #include #include -#include "../wifibroadcast/encryption/Encryption.h" -#include "../wifibroadcast/encryption/EncryptionFsUtils.h" +#include "../src/encryption/Encryption.h" +#include "../src/encryption/EncryptionFsUtils.h" /** * Generates a new tx rx keypair and saves it to file for later use. diff --git a/wifibroadcast/pcap_helper.hpp b/wifibroadcast/pcap_helper.hpp deleted file mode 100644 index 9e44eb9a..00000000 --- a/wifibroadcast/pcap_helper.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// -// Created by consti10 on 17.12.22. -// - -#ifndef WIFIBROADCAST_SRC_PCAP_HELPER_H_ -#define WIFIBROADCAST_SRC_PCAP_HELPER_H_ - -#include - -#include - -namespace wifibroadcast::pcap_helper{ - -// debugging -static std::string tstamp_types_to_string(int* ts_types,int n){ - std::stringstream ss; - ss<<"["; - for(int i=0;idebug("TS types:{}", wifibroadcast::pcap_helper::tstamp_types_to_string(availableTimestamps,nTypes)); - //"N available timestamp types "<debug("Setting timestamp to host"); - pcap_set_tstamp_type(ppcap, PCAP_TSTAMP_HOST); - } - } - pcap_free_tstamp_types(availableTimestamps); -} - -// creates a pcap handle for the given wlan and sets common params for wb -// returns nullptr on failure, a valid pcap handle otherwise -static pcap_t *open_pcap_rx(const std::string &wlan) { - pcap_t *ppcap= nullptr; - char errbuf[PCAP_ERRBUF_SIZE]; - ppcap = pcap_create(wlan.c_str(), errbuf); - if (ppcap == nullptr) { - wifibroadcast::log::get_default()->error("Unable to open interface {} in pcap: {}", wlan.c_str(), errbuf); - return nullptr; - } - iteratePcapTimestamps(ppcap); - if (pcap_set_snaplen(ppcap, 4096) != 0) wifibroadcast::log::get_default()->error("set_snaplen failed"); - if (pcap_set_promisc(ppcap, 1) != 0) wifibroadcast::log::get_default()->error("set_promisc failed"); - //if (pcap_set_rfmon(ppcap, 1) !=0) wifibroadcast::log::get_default()->error("set_rfmon failed"); - if (pcap_set_timeout(ppcap, -1) != 0) wifibroadcast::log::get_default()->error("set_timeout failed"); - //if (pcap_set_buffer_size(ppcap, 2048) !=0) wifibroadcast::log::get_default()->error("set_buffer_size failed"); - // Important: Without enabling this mode pcap buffers quite a lot of packets starting with version 1.5.0 ! - // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html - if (pcap_set_immediate_mode(ppcap, true) != 0){ - wifibroadcast::log::get_default()->warn("pcap_set_immediate_mode failed: {}",pcap_geterr(ppcap)); - } - if (pcap_activate(ppcap) != 0){ - wifibroadcast::log::get_default()->error("pcap_activate failed: {}",pcap_geterr(ppcap)); - } - if (pcap_setnonblock(ppcap, 1, errbuf) != 0){ - wifibroadcast::log::get_default()->error("set_nonblock failed: {}",errbuf); - } - return ppcap; -} - -// copy paste from svpcom -static pcap_t *open_pcap_tx(const std::string &wlan) { - char errbuf[PCAP_ERRBUF_SIZE]; - pcap_t *p = pcap_create(wlan.c_str(), errbuf); - if (p == nullptr) { - wifibroadcast::log::get_default()->error("Unable to open interface {} in pcap: {}", wlan.c_str(), errbuf); - } - if (pcap_set_snaplen(p, 4096) != 0) wifibroadcast::log::get_default()->warn("set_snaplen failed"); - if (pcap_set_promisc(p, 1) != 0) wifibroadcast::log::get_default()->warn("set_promisc failed"); - //if (pcap_set_rfmon(p, 1) !=0) wifibroadcast::log::get_default()->warn("set_rfmon failed"; - // Used to be -1 at some point, which is undefined behaviour. -1 can cause issues on older kernels, according to @Pete - const int timeout_ms=10; - if (pcap_set_timeout(p, timeout_ms) != 0) wifibroadcast::log::get_default()->warn("set_timeout {} failed",timeout_ms); - //if (pcap_set_buffer_size(p, 2048) !=0) wifibroadcast::log::get_default()->warn("set_buffer_size failed"; - // NOTE: Immediate not needed on TX - if (pcap_activate(p) != 0){ - wifibroadcast::log::get_default()->error("pcap_activate failed: {}", - pcap_geterr(p)); - } - //if (pcap_setnonblock(p, 1, errbuf) != 0) wifibroadcast::log::get_default()->warn(string_format("set_nonblock failed: %s", errbuf)); - return p; -} - -} - -#endif // WIFIBROADCAST_SRC_PCAP_HELPER_H_ diff --git a/wifibroadcast/radiotap/RSSIAccumulator.hpp b/wifibroadcast/radiotap/RSSIAccumulator.hpp deleted file mode 100644 index 1c35d952..00000000 --- a/wifibroadcast/radiotap/RSSIAccumulator.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// -// Created by consti10 on 30.06.23. -// - -#ifndef WIFIBROADCAST_RSSIACCUMULATOR_HPP -#define WIFIBROADCAST_RSSIACCUMULATOR_HPP - -#include - -#include "../wifibroadcast_spdlog.h" -#include "TimeHelper.hpp" -#include "spdlog/spdlog.h" - -/** - * UINT16SeqNrHelper to accumulate RSSI values - */ -class RSSIAccumulator{ - public: - void add_rssi(int8_t rssi){ - if(rssi<=INT8_MIN || rssi>=0){ - // RSSI should always be negative and in range [-127,-1] - // It seems to be quite common for drivers to report invalid rssi values from time to time - in this case, - // just ignore the value - if(m_debug_invalid_rssi){ - wifibroadcast::log::get_default()->debug("Invalid rssi on id {}, {}",m_rssi_identifier,rssi); - } - return ; - } - if(rssi>m_rssi_max){ - m_rssi_max=rssi; - } - if(rssi(rssi); - m_rssi_count++; - } - int8_t get_avg()const{ - const auto count=m_rssi_count; - if(count<=0)return INT8_MIN; - const auto avg=m_rssi_sum/m_rssi_count; - return static_cast(avg); - } - int8_t get_min()const{ - if(m_rssi_count<=0)return INT8_MIN; - return m_rssi_min; - } - int8_t get_max()const{ - if(m_rssi_count<=0)return INT8_MIN; - return m_rssi_max; - } - MinMaxAvg get_min_max_avg(){ - MinMaxAvg tmp{get_min(),get_max(),get_avg()}; - return tmp; - } - static std::string min_max_avg_to_string(const MinMaxAvg& data,bool avg_only= false){ - // Need to convert to int such that it is shown correctly - MinMaxAvg tmp{data.min,data.max,data.avg}; - return min_max_avg_as_string(tmp, avg_only); - } - int get_n_samples(){ - return m_rssi_count; - } - std::optional> add_and_recalculate_if_needed(int8_t rssi){ - add_rssi(rssi); - // Calculate every 20 packets or 500ms and at least one packet, whatever is reached first - const auto elapsed=std::chrono::steady_clock::now()-m_last_recalculation; - if(get_n_samples()>=20 || (get_n_samples()>=1 && elapsed>=std::chrono::milliseconds(500))){ - auto tmp=get_min_max_avg(); - reset(); - m_last_recalculation=std::chrono::steady_clock::now(); - return tmp; - } - return std::nullopt; - } - void reset(){ - m_rssi_sum=0; - m_rssi_count=0; - m_rssi_min=INT8_MAX; - m_rssi_max=INT8_MIN; - } - void set_debug_invalid_rssi(bool enable,int rssi_identifier){ - m_debug_invalid_rssi=enable; - m_rssi_identifier=rssi_identifier; - } - private: - int m_rssi_sum=0; - int m_rssi_count=0; - int8_t m_rssi_min=INT8_MAX; - int8_t m_rssi_max=INT8_MIN; - std::chrono::steady_clock::time_point m_last_recalculation=std::chrono::steady_clock::now(); - bool m_debug_invalid_rssi= false; - int m_rssi_identifier=0; -}; - -#endif // WIFIBROADCAST_RSSIACCUMULATOR_HPP diff --git a/wifibroadcast/radiotap/RadiotapHeaderRx.hpp b/wifibroadcast/radiotap/RadiotapHeaderRx.hpp deleted file mode 100644 index ac935133..00000000 --- a/wifibroadcast/radiotap/RadiotapHeaderRx.hpp +++ /dev/null @@ -1,206 +0,0 @@ -// -// Created by consti10 on 05.10.23. -// - -#ifndef WIFIBROADCAST_RADIOTAPHEADERPARSER_H -#define WIFIBROADCAST_RADIOTAPHEADERPARSER_H - -#include - -#include "../Ieee80211Header.hpp" -#include "RadiotapHeaderTx.hpp" - -// Code for parsing the radiotap header coming from rtl8812au / rtl8812bu / other monitor mode wifi drivers - -namespace radiotap::rx{ - -static constexpr int8_t DBM_INVALID=-128; // INT8_MIN -static constexpr uint16_t QUALITY_INVALID=65535; //UINT16_MAX; - -// NOTE: We use std::nullopt to indicate that the card doesn't report this value -struct KeyRfIndicators { - // https://www.radiotap.org/fields/Antenna%20signal.html - // IEEE80211_RADIOTAP_DBM_ANTSIGNAL - std::optional radiotap_dbm_antsignal=std::nullopt; - // IEEE80211_RADIOTAP_DBM_ANTNOISE - std::optional radiotap_dbm_antnoise=std::nullopt; - // IEEE80211_RADIOTAP_LOCK_QUALITY - std::optional radiotap_lock_quality=std::nullopt; -}; - -struct ParsedRxRadiotapPacket { - const Ieee80211HeaderRaw *ieee80211Header; - const uint8_t *payload; - const std::size_t payloadSize; - // --- Values generic (not per rf-path) --- - bool radiotap_f_bad_fcs= false; - KeyRfIndicators rf_adapter; - // --- Values per rf-path ----- - // first one: antenna 1 (if reported by card), second one: antenna 2 (if reported by card) ... - std::vector rf_paths; -}; - -// Returns std::nullopt if radiotap was unable to parse the header -// else return the *parsed information* -// This method is intentionally simple in that it only looks for data relevant to us (right now) inside the radiotap header. -static std::optional process_received_radiotap_packet(const uint8_t *pkt,const int pkt_len) { - //int pktlen = hdr.caplen; - int pktlen=pkt_len; - // - // Copy the value of this flag once present and process it after the loop is done - uint8_t tmp_copy_IEEE80211_RADIOTAP_FLAGS = 0; - struct ieee80211_radiotap_iterator iterator{}; - // With AR9271 I get 39 as length of the radio-tap header - // With my internal laptop wifi chip I get 36 as length of the radio-tap header. - int ret = ieee80211_radiotap_iterator_init(&iterator, (ieee80211_radiotap_header *) pkt, pktlen, nullptr); - if(ret){ - printf("malformed radiotap header (init returns %d)\n", ret); - return std::nullopt; - } - - // This is the best way I came up with of how to seperate the per-adaper and per-rf-path rf metrics from one of another - // It assumes the driver reports them in order - per-adapter first, then per-rf-paths (for as many rf paths there are) - // AND the driver reports signal (noise,lock) if given for the adapter and rf paths(s) alltogether - // Seems to be the case for all drivers, definitely for the openhd supported ones. - std::vector radiotap_dbm_antsignal; - radiotap_dbm_antsignal.reserve(5); - std::vector radiotap_dbm_antnoise; - radiotap_dbm_antnoise.reserve(5); - std::vector radiotap_lock_quality; - radiotap_lock_quality.reserve(5); - - int8_t n_antennas=0; - while (ret == 0) { - ret = ieee80211_radiotap_iterator_next(&iterator); - if (ret) { - continue; - } - /* see if this argument is something we can use */ - switch (iterator.this_arg_index) { - case IEEE80211_RADIOTAP_ANTENNA:{ - const auto antenna_idx= (int8_t)iterator.this_arg[0]; - const int8_t antenna_nr=antenna_idx+1; - if(antenna_nr>n_antennas)n_antennas=antenna_nr; - }break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:{ - int8_t value; - std::memcpy(&value,iterator.this_arg,1); - radiotap_dbm_antsignal.push_back(value); - } break ; - case IEEE80211_RADIOTAP_DBM_ANTNOISE:{ - int8_t value; - std::memcpy(&value,iterator.this_arg,1); - radiotap_dbm_antnoise.push_back(value); - }break; - case IEEE80211_RADIOTAP_FLAGS: - tmp_copy_IEEE80211_RADIOTAP_FLAGS = *(uint8_t *) (iterator.this_arg); - break; - case IEEE80211_RADIOTAP_LOCK_QUALITY:{ - uint16_t value=0; - // NOTE: Here we only copy 8 bits - the value is reported in radiotap as uint16_t type, - // but only in the 0..100 range, so uint8_t would be sufficient (and works) - std::memcpy(&value,iterator.this_arg,1); - radiotap_lock_quality.push_back(value); - } break ; - default:break; - } - } /* while more rt headers */ - if (ret != -ENOENT) { - printf("Cannot parse radiotap header %d\n", ret); - return std::nullopt; - } - bool has_radiotap_f_bad_fcs= false; - if (tmp_copy_IEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_BADFCS) { - //wifibroadcast::log::get_default()->warn("Got packet with bad fsc\n"; - has_radiotap_f_bad_fcs= true; - } - // the fcs is at the end of the packet - if (tmp_copy_IEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_FCS) { - //<<"Packet has IEEE80211_RADIOTAP_F_FCS"; - pktlen -= 4; - } - //assert(iterator._max_length==hdr.caplen); - /* discard the radiotap header part */ - pkt += iterator._max_length; - pktlen -= iterator._max_length; - KeyRfIndicators adapter; - // First fill in the adapter - if(!radiotap_dbm_antsignal.empty()){ - adapter.radiotap_dbm_antsignal=radiotap_dbm_antsignal[0]; - } - if(!radiotap_dbm_antnoise.empty()){ - adapter.radiotap_dbm_antnoise=radiotap_dbm_antnoise[0]; - } - if(!radiotap_lock_quality.empty()){ - adapter.radiotap_lock_quality=radiotap_lock_quality[0]; - } - // Then per rf-path - std::vector rf_paths; - rf_paths.resize(n_antennas); - for(int i=0;iidx){ - rf_paths[i].radiotap_dbm_antsignal=radiotap_dbm_antsignal[idx]; - } - if(radiotap_dbm_antnoise.size()>idx){ - rf_paths[i].radiotap_dbm_antnoise=radiotap_dbm_antnoise[idx]; - } - if(radiotap_lock_quality.size()>idx){ - rf_paths[i].radiotap_lock_quality=radiotap_lock_quality[idx]; - } - } - const Ieee80211HeaderRaw *ieee80211Header = (Ieee80211HeaderRaw *) pkt; - const uint8_t *payload = pkt + Ieee80211HeaderRaw::SIZE_BYTES; - const std::size_t payloadSize = (std::size_t) pktlen - Ieee80211HeaderRaw::SIZE_BYTES; - return ParsedRxRadiotapPacket{ieee80211Header, payload, payloadSize, has_radiotap_f_bad_fcs,adapter,rf_paths}; -} - - -static std::string key_rf_indicators_to_string(const KeyRfIndicators& indicators){ - std::stringstream ss; - ss<<"{"; - if(indicators.radiotap_dbm_antsignal.has_value()){ - ss<<(int)indicators.radiotap_dbm_antsignal.value()<<" dBm : "; - }else{ - ss<<"N/A dBm : "; - } - if(indicators.radiotap_dbm_antnoise.has_value()){ - ss<<(int)indicators.radiotap_dbm_antnoise.value()<<" dBm : "; - }else{ - ss<<"N/A dBm : "; - } - if(indicators.radiotap_lock_quality.has_value()){ - ss<<(int)indicators.radiotap_lock_quality.value()<<" %"; - }else{ - ss<<"N/A %"; - } - ss<<"}"; - return ss.str(); -} - -static std::string all_rf_path_to_string(const std::vector& all_rf_path){ - std::stringstream ss; - ss<<"RF Paths:"; - if(all_rf_path.empty()){ - ss<<"[Empty]"; - return ss.str(); - } - int idx=0; - for(const auto& rf_path:all_rf_path){ - ss<avg; - } - } - if(indicators.radiotap_dbm_antnoise.has_value()){ - const auto radiotap_dbm_antnoise=indicators.radiotap_dbm_antnoise.value(); - auto opt_minmaxavg= agg.noise_dbm.add_and_recalculate_if_needed(radiotap_dbm_antnoise); - if(opt_minmaxavg.has_value()){ - curr.noise_dbm=opt_minmaxavg->avg; - } - } - if(indicators.radiotap_lock_quality.has_value()){ - const auto radiotap_lock_quality=indicators.radiotap_lock_quality.value(); - agg.signal_quality.add_signal_quality(radiotap_lock_quality); - curr.card_signal_quality_perc=agg.signal_quality.get_current_signal_quality(); - } -} - -void RadiotapRxRfAggregator::on_valid_openhd_packet( - const radiotap::rx::ParsedRxRadiotapPacket& packet) { - add_if_valid(packet.rf_adapter,m_agg_adapter,m_current_rx_stats.adapter); - for(int i=0;i=std::chrono::seconds(1)){ - auto current=get_current(); - wifibroadcast::log::get_default()->debug("{}",RadiotapRxRfAggregator::card_key_rf_indicators_to_string(current)); - m_last_debug_log=now; - } -} - -void RadiotapRxRfAggregator::KeyRfAggregators::reset() { - rssi_dbm.reset(); - noise_dbm.reset(); - signal_quality.reset(); -} diff --git a/wifibroadcast/radiotap/SignalQualityAccumulator.hpp b/wifibroadcast/radiotap/SignalQualityAccumulator.hpp deleted file mode 100644 index 4a153900..00000000 --- a/wifibroadcast/radiotap/SignalQualityAccumulator.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// -// Created by consti10 on 09.08.23. -// - -#ifndef WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP -#define WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP - -#include - -#include "../wifibroadcast_spdlog.h" -#include "TimeHelper.hpp" - -/** - * Helper to accumulate (rtl8812au) signal quality values - - * aka values that should always be in [0..100] range. - */ -class SignalQualityAccumulator{ - public: - void add_signal_quality(int signal_quality_perc){ - if(signal_quality_perc>100 || signal_quality_perc<0){ - if(m_debug_invalid_signal_quality){ - wifibroadcast::log::get_default()->debug("Invalid signal quality {}",signal_quality_perc); - } - return ; - } - m_acc.add(signal_quality_perc); - if(m_acc.getNSamples()>10 || m_acc.get_delta_since_last_reset()>std::chrono::milliseconds(500)){ - const auto tmp=m_acc.getMinMaxAvg(); - const auto avg=tmp.avg; - if(avg>=0 && avg<=100){ - m_curr_signal_quality=avg; - } - m_acc.reset(); - } - } - void reset(){ - m_acc.reset(); - m_curr_signal_quality=-1; - } - int8_t get_current_signal_quality()const{ - return m_curr_signal_quality; - } - void set_debug_invalid_signal_quality(bool enable){ - m_debug_invalid_signal_quality=enable; - } - private: - BaseAvgCalculator m_acc; - // -1 if invalid, [0,100] otherwise - int8_t m_curr_signal_quality=-1; - bool m_debug_invalid_signal_quality= false; -}; - -#endif // WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP diff --git a/wifibroadcast/FunkyQueue.cpp b/wifibroadcast/src/FunkyQueue.cpp similarity index 100% rename from wifibroadcast/FunkyQueue.cpp rename to wifibroadcast/src/FunkyQueue.cpp diff --git a/wifibroadcast/FunkyQueue.h b/wifibroadcast/src/FunkyQueue.h similarity index 53% rename from wifibroadcast/FunkyQueue.h rename to wifibroadcast/src/FunkyQueue.h index bde932d6..a5f98819 100644 --- a/wifibroadcast/FunkyQueue.h +++ b/wifibroadcast/src/FunkyQueue.h @@ -5,26 +5,26 @@ #ifndef WIFIBROADCAST_FUNKYQUEUE_H #define WIFIBROADCAST_FUNKYQUEUE_H +#include #include #include #include -#include #include -#include +#include /** - * Thread-safe queue for the openhd use case where there is one producer thread (encoder) and one consumer - * thread (link). - * Funky because the operations are a bit funky (but make sense in this use case). + * Thread-safe queue for the openhd use case where there is one producer thread + * (encoder) and one consumer thread (link). Funky because the operations are a + * bit funky (but make sense in this use case). */ -template +template class FunkyQueue { public: - explicit FunkyQueue(int capacity):m_capacity(capacity){}; + explicit FunkyQueue(int capacity) : m_capacity(capacity){}; // Enqueues a new element. Return true on success, false otherwise - bool try_enqueue(T element){ + bool try_enqueue(T element) { std::unique_lock lk(mtx); - if(queue.size()>=m_capacity){ + if (queue.size() >= m_capacity) { return false; } queue.push(element); @@ -32,15 +32,15 @@ class FunkyQueue { cv.notify_one(); return true; } - // If there is enough space on the queue, enqueue the given element and return 0; - // Otherwise, remove all elements currently in the queue, then enqueue the given element, - // and return the n of removed elements - int enqueue_or_clear_enqueue(T element){ + // If there is enough space on the queue, enqueue the given element and return + // 0; Otherwise, remove all elements currently in the queue, then enqueue the + // given element, and return the n of removed elements + int enqueue_or_clear_enqueue(T element) { std::unique_lock lk(mtx); - if(queue.size()>=m_capacity){ + if (queue.size() >= m_capacity) { // Not enough space - const int count_removed=queue.size(); - while (!queue.empty())queue.pop(); + const int count_removed = queue.size(); + while (!queue.empty()) queue.pop(); queue.push(element); lk.unlock(); cv.notify_one(); @@ -53,35 +53,36 @@ class FunkyQueue { return 0; } // Wait up to timeout until element is available. - template - std::optional wait_dequeue_timed(std::chrono::duration const& timeout){ + template + std::optional wait_dequeue_timed( + std::chrono::duration const& timeout) { std::unique_lock ul(mtx); - if(!queue.empty()){ - auto tmp=queue.front(); + if (!queue.empty()) { + auto tmp = queue.front(); queue.pop(); return tmp; } - const auto res=cv.wait_for(ul,timeout,[this](){ - return !queue.empty(); - }); - if(!res){ + const auto res = + cv.wait_for(ul, timeout, [this]() { return !queue.empty(); }); + if (!res) { // Timeout return std::nullopt; } assert(!queue.empty()); - auto tmp=queue.front(); + auto tmp = queue.front(); queue.pop(); return tmp; } - int get_current_size(){ + int get_current_size() { std::unique_lock ul(mtx); return queue.size(); } -private: - const int m_capacity; - std::queue queue; - std::mutex mtx; - std::condition_variable cv; + + private: + const int m_capacity; + std::queue queue; + std::mutex mtx; + std::condition_variable cv; }; #endif // WIFIBROADCAST_FUNKYQUEUE_H diff --git a/wifibroadcast/HelperSources/Benchmark.hpp b/wifibroadcast/src/HelperSources/Benchmark.hpp similarity index 59% rename from wifibroadcast/HelperSources/Benchmark.hpp rename to wifibroadcast/src/HelperSources/Benchmark.hpp index f1309870..6de70807 100644 --- a/wifibroadcast/HelperSources/Benchmark.hpp +++ b/wifibroadcast/src/HelperSources/Benchmark.hpp @@ -26,9 +26,11 @@ class PacketizedBenchmark { public: /** * @param name1 what we are benchmarking here (for example FEC, encryption) - * @param factor1 use a factor other than 1.0 if the packet size changes during the benchmarked step + * @param factor1 use a factor other than 1.0 if the packet size changes + * during the benchmarked step */ - explicit PacketizedBenchmark(std::string name1, double factor1 = 1.0f) : name(std::move(name1)), factor(factor1) {}; + explicit PacketizedBenchmark(std::string name1, double factor1 = 1.0f) + : name(std::move(name1)), factor(factor1){}; void begin() { testBegin = std::chrono::steady_clock::now(); logTs = std::chrono::steady_clock::now(); @@ -46,10 +48,11 @@ class PacketizedBenchmark { if (delta > std::chrono::seconds(1)) { const float currPacketsPerSecond = packetsDelta; const float currBitRate_MBits = bytesDelta * 8.0 / 1024.0 / 1024.0; - std::cout << "curr. Packets per second:" << currPacketsPerSecond << " before " << name << ": " - << currBitRate_MBits << "Mbit/s"; + std::cout << "curr. Packets per second:" << currPacketsPerSecond + << " before " << name << ": " << currBitRate_MBits << "Mbit/s"; if (factor != 1.0f) { - std::cout << " after " << name << ": " << currBitRate_MBits * factor << "MBit/s"; + std::cout << " after " << name << ": " << currBitRate_MBits * factor + << "MBit/s"; } std::cout << "\n"; logTs = std::chrono::steady_clock::now(); @@ -59,23 +62,34 @@ class PacketizedBenchmark { } void end() { const auto testDuration = std::chrono::steady_clock::now() - testBegin; - const float - testDurationSeconds = std::chrono::duration_cast(testDuration).count() / 1000.0f; - //std::cout<<"Wanted duration:"<(testDuration) + .count() / + 1000.0f; + // std::cout<<"Wanted duration:"<(delta).count(); - //std::cout<<"Encoding a block of size:"<(delta).count(); + // std::cout<<"Encoding a block of + // size:"< +#include + +namespace blocksize { + +// From +// https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c +static int div_ceil(int numerator, int denominator) { + std::div_t res = std::div(numerator, denominator); + return res.rem ? (res.quot + 1) : res.quot; +} + +// If a frame has more fragments than the max block size on this platform +// (Which usually depends on the compute power of the platform we are running +// on, since FEC blocks become exponentially increasing expensive the bigger +// they are) We need to split the frame into more than one block. Usually, this +// needs to be done only for key frames (which are much bigger than other +// frame(s) ), but depends on the platform compute and encoder bitrate, fps +static int calc_min_n_of_blocks(int fragments_in_this_frame, + int max_block_size) { + return std::ceil(static_cast(fragments_in_this_frame) / + static_cast(max_block_size)); +} + +// Algorithm: +// Given some amount of balls, fill the minimum amount of buckets as equally +// distributed as possible with balls such that each bucket has not more than +// max_block_size balls +static std::vector fill_buckets_evenly(int count, int max_size_of_bucket) { + if (count <= max_size_of_bucket) { + return {count}; + } + int consumed = 0; + std::vector ret; + while (consumed < count) { + int remaining = count - consumed; + const int fill = div_ceil(remaining, max_size_of_bucket); + ret.push_back(fill); + consumed += fill; + } + return ret; +} + +static std::vector calculate_best_fit_block_sizes( + int fragments_in_this_frame, int max_block_size) { + if (fragments_in_this_frame <= max_block_size) { + // We can do this whole frame in one FEC block + return {static_cast(fragments_in_this_frame)}; + } + // Algorithm: + // Given some amount of balls, fill the minimum amount of buckets as equally + // distributed as possible with balls such that each bucket has not more than + // max_block_size balls We need at least this many buckets (blocks) + const int min_n_of_blocks = + calc_min_n_of_blocks(fragments_in_this_frame, max_block_size); + std::vector ret; + ret.resize(min_n_of_blocks); + // Fill the buckets (blocks) with fragments, one after another, until we run + // out of balls (fragments) + int remaining = fragments_in_this_frame; + int index = 0; + while (remaining > 0) { + ret[index]++; + remaining--; + index++; + index = index % min_n_of_blocks; + } + return ret; +} + +static std::vector>>> +split_frame_if_needed( + const std::vector>>& frame_fragments, + int max_block_size) { + auto split = + calculate_best_fit_block_sizes(frame_fragments.size(), max_block_size); + if (split.size() == 1) { + return {frame_fragments}; + } + std::vector>>> ret; + ret.resize(split.size()); + int n_used_fragments = 0; + for (int i = 0; i < split.size(); i++) { + for (int j = 0; j < split[i]; j++) { + ret[i].push_back(frame_fragments[n_used_fragments]); + n_used_fragments++; + } + } + return ret; +} + +// Given the size of a frame and the max n of primary fragments we can do on the +// given platform calculate how we should distribute the data (into one or more +// fec blocks, and how many fragments each fec block shall have + +static int min_num_sub_blocks(int frame_size, int max_block_size, int MTU) { + const int max_data_size = max_block_size * MTU; + return div_ceil(frame_size, max_data_size); +} + +} // namespace blocksize +#endif // WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ diff --git a/wifibroadcast/src/HelperSources/DummyStreamGenerator.hpp b/wifibroadcast/src/HelperSources/DummyStreamGenerator.hpp new file mode 100644 index 00000000..d41638e9 --- /dev/null +++ b/wifibroadcast/src/HelperSources/DummyStreamGenerator.hpp @@ -0,0 +1,86 @@ +// +// Created by consti10 on 25.07.23. +// + +#ifndef WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP +#define WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP + +#include +#include +#include +#include + +#include "RandomBufferPot.hpp" +#include "SchedulingHelper.hpp" + +/** + * Generates as close as possible a stream of data packets with a target packets + * per second packet rate. + */ +class DummyStreamGenerator { + public: + typedef std::function + OUTPUT_DATA_CALLBACK; + + DummyStreamGenerator(OUTPUT_DATA_CALLBACK cb, int packet_size) + : m_cb(std::move(cb)), m_packet_size(packet_size) { + m_random_buffer_pot = + std::make_unique(1000, m_packet_size); + }; + ~DummyStreamGenerator() { stop(); } + + void set_target_pps(int pps) { m_target_pps = pps; } + + void start() { + m_terminate = false; + m_producer_thread = + std::make_unique([this]() { loop_generate_data(); }); + } + void stop() { + m_terminate = true; + if (m_producer_thread) { + m_producer_thread->join(); + m_producer_thread = nullptr; + } + } + void loop_generate_data() { + SchedulingHelper::set_thread_params_max_realtime("DummyStreamGenerator"); + std::chrono::steady_clock::time_point last_packet = + std::chrono::steady_clock::now(); + const uint64_t delay_between_packets_ns = 1000 * 1000 * 1000 / m_target_pps; + const auto delay_between_packets = + std::chrono::nanoseconds(delay_between_packets_ns); + wifibroadcast::log::get_default()->debug( + "Target pps:{} delta between packets:{}", m_target_pps, + MyTimeHelper::R(delay_between_packets)); + while (!m_terminate) { + last_packet = std::chrono::steady_clock::now(); + // wifibroadcast::log::get_default()->debug("Delay between packets: + // {}",std::chrono::duration_cast(delay_between_packets).count()); + auto buff = m_random_buffer_pot->get_next_buffer(); + m_cb(buff->data(), buff->size()); + const auto next_packet_tp = + last_packet + delay_between_packets - + std::chrono::nanoseconds(200); // minus Xns to better hit the target + if (std::chrono::steady_clock::now() >= next_packet_tp) { + // wifibroadcast::log::get_default()->warn("Cannot keep up with the + // wanted tx pps"); + n_times_cannot_keep_up_wanted_pps++; + } + while (std::chrono::steady_clock::now() < next_packet_tp) { + // busy wait + } + } + } + int n_times_cannot_keep_up_wanted_pps = 0; + + private: + const int m_packet_size = 1400; + const OUTPUT_DATA_CALLBACK m_cb; + int m_target_pps = 100; + std::unique_ptr m_producer_thread; + std::unique_ptr m_random_buffer_pot; + bool m_terminate = false; +}; + +#endif // WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP diff --git a/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp new file mode 100644 index 00000000..1d3d7a42 --- /dev/null +++ b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp @@ -0,0 +1,56 @@ +#ifndef EMULATEDPACKETDROP_H +#define EMULATEDPACKETDROP_H + +#include +#include +#include +#include + +// emulating packet drop "as" when using wifibroadcast (no matter weather it is +// with or without FEC) is not that easy. + +// Drops a specific percentage of packets, this doesn't eumlate the "big gaps" +// behaviour +class PacketDropEmulator { + public: + PacketDropEmulator(int percentage_dropped_packets) + : m_percentage_dropped_packets(percentage_dropped_packets) {} + // Returns true if you should drop this packet, false otherwise + bool drop_packet() { + std::lock_guard lock(m_mutex); + const auto number = next_random_number_0_100(); + // qDebug()<<"Number is:"< number) { + // drop packet + n_dropped_packets++; + log(); + return true; + } + n_forwarded_packets++; + log(); + return false; + } + void log() { + const double perc_dropped = + (double)n_dropped_packets / (n_totoal_packets)*100.0; + // std::cout<<"N + // dropped:"< lock(m_mutex); + m_percentage_dropped_packets = new_perc; + } + + private: + std::mutex m_mutex; + int m_percentage_dropped_packets; + int n_dropped_packets = 0; + int n_forwarded_packets = 0; + int n_totoal_packets = 0; + int next_random_number_0_100() { return m_dist100(m_mt); } + std::mt19937 m_mt; + std::uniform_int_distribution<> m_dist100{0, 100}; +}; + +#endif // EMULATEDPACKETDROP_H diff --git a/wifibroadcast/HelperSources/Helper.hpp b/wifibroadcast/src/HelperSources/Helper.hpp similarity index 52% rename from wifibroadcast/HelperSources/Helper.hpp rename to wifibroadcast/src/HelperSources/Helper.hpp index 4671fcef..6d8af177 100644 --- a/wifibroadcast/HelperSources/Helper.hpp +++ b/wifibroadcast/src/HelperSources/Helper.hpp @@ -5,15 +5,16 @@ #ifndef WIFIBROADCAST_HELPER_H #define WIFIBROADCAST_HELPER_H -#include -#include +#include #include +#include #include -#include +#include #include "StringHelper.hpp" -// Generic "UINT16SeqNrHelper" code that does not depend on anything else other than the std libraries +// Generic "UINT16SeqNrHelper" code that does not depend on anything else other +// than the std libraries namespace GenericHelper { // fill buffer with random bytes @@ -23,7 +24,7 @@ static void fillBufferWithRandomData(std::vector &data) { data[i] = rand() % 255; } } -template +template static void fillBufferWithRandomData(std::array &data) { for (std::size_t i = 0; i < size; i++) { data[i] = rand() % 255; @@ -36,13 +37,16 @@ static std::vector createRandomDataBuffer(const ssize_t sizeBytes) { return buf; } // same as above but return shared ptr -static std::shared_ptr> createRandomDataBuffer2(const ssize_t sizeBytes) { - return std::make_shared>(createRandomDataBuffer(sizeBytes)); +static std::shared_ptr> createRandomDataBuffer2( + const ssize_t sizeBytes) { + return std::make_shared>( + createRandomDataBuffer(sizeBytes)); } // Create a random number in range [min,...,max] // https://stackoverflow.com/questions/12657962/how-do-i-generate-a-random-number-between-two-variables-that-i-have-stored -static ssize_t create_random_number_between(const ssize_t minSizeB, const ssize_t maxSizeB){ +static ssize_t create_random_number_between(const ssize_t minSizeB, + const ssize_t maxSizeB) { const auto sizeBytes = rand() % (maxSizeB - minSizeB + 1) + minSizeB; assert(sizeBytes <= maxSizeB); assert(sizeBytes >= minSizeB); @@ -52,97 +56,115 @@ static ssize_t create_random_number_between(const ssize_t minSizeB, const ssize_ return sizeBytes; } -static std::vector>> convert_vec_of_vec_to_shared(std::vector> in){ +static std::vector>> +convert_vec_of_vec_to_shared(std::vector> in) { std::vector>> ret; - for(auto data:in){ - std::shared_ptr> shared=std::make_shared>(data.begin(),data.end()); + for (auto data : in) { + std::shared_ptr> shared = + std::make_shared>(data.begin(), data.end()); ret.push_back(shared); } return ret; } -// Create a buffer filled with random data where size is chosen Randomly between [minSizeB,...,maxSizeB] -static std::vector createRandomDataBuffer(const ssize_t minSizeB, const ssize_t maxSizeB) { - return createRandomDataBuffer(create_random_number_between(minSizeB,maxSizeB)); +// Create a buffer filled with random data where size is chosen Randomly between +// [minSizeB,...,maxSizeB] +static std::vector createRandomDataBuffer(const ssize_t minSizeB, + const ssize_t maxSizeB) { + return createRandomDataBuffer( + create_random_number_between(minSizeB, maxSizeB)); } // create n random data buffers with size [minSizeB,...,maxSizeB] -static std::vector> createRandomDataBuffers(const std::size_t nBuffers, - const std::size_t minSizeB, - const std::size_t maxSizeB) { +static std::vector> createRandomDataBuffers( + const std::size_t nBuffers, const std::size_t minSizeB, + const std::size_t maxSizeB) { assert(minSizeB >= 0); std::vector> buffers; for (std::size_t i = 0; i < nBuffers; i++) { - buffers.push_back(GenericHelper::createRandomDataBuffer(minSizeB, maxSizeB)); + buffers.push_back( + GenericHelper::createRandomDataBuffer(minSizeB, maxSizeB)); } return buffers; } -static std::vector>> createRandomDataBuffers_shared(const std::size_t nBuffers, - const std::size_t minSizeB, - const std::size_t maxSizeB) { +static std::vector>> +createRandomDataBuffers_shared(const std::size_t nBuffers, + const std::size_t minSizeB, + const std::size_t maxSizeB) { assert(minSizeB >= 0); std::vector>> buffers; for (std::size_t i = 0; i < nBuffers; i++) { - auto buf=GenericHelper::createRandomDataBuffer(minSizeB,maxSizeB); - auto buf_shared=std::make_shared>(buf.begin(),buf.end()); + auto buf = GenericHelper::createRandomDataBuffer(minSizeB, maxSizeB); + auto buf_shared = + std::make_shared>(buf.begin(), buf.end()); buffers.push_back(buf_shared); } return buffers; } -template -static std::vector> createRandomDataBuffers(const std::size_t nBuffers) { +template +static std::vector> createRandomDataBuffers( + const std::size_t nBuffers) { std::vector> ret(nBuffers); - for (auto &buff: ret) { + for (auto &buff : ret) { GenericHelper::fillBufferWithRandomData(buff); } return ret; } -static bool compareVectors(const std::vector &sb, const std::vector &rb) { +static bool compareVectors(const std::vector &sb, + const std::vector &rb) { if (sb.size() != rb.size()) { return false; } const int result = memcmp(sb.data(), rb.data(), sb.size()); return result == 0; } -static void assertVectorsEqual(const std::vector &sb, const std::vector &rb) { +static void assertVectorsEqual(const std::vector &sb, + const std::vector &rb) { assert(sb.size() == rb.size()); const int result = memcmp(sb.data(), rb.data(), sb.size()); assert(result == 0); } -static void assertVectorsOfVectorsEqual(const std::vector> &sbl, const std::vector> &rbl){ - for(int i=0;i> &sbl, + const std::vector> &rbl) { + for (int i = 0; i < sbl.size(); i++) { + const auto &sb = sbl[i]; + const auto &rb = rbl[i]; + assertVectorsEqual(sb, rb); } } -static std::vector> shared_to(const std::vector>>& in){ +static std::vector> shared_to( + const std::vector>> &in) { std::vector> ret; - for(auto& element:in){ - ret.emplace_back(element->begin(),element->end()); + for (auto &element : in) { + ret.emplace_back(element->begin(), element->end()); } return ret; } -static void assertVectorsOfVectorsEqual(const std::vector>>& sbl, const std::vector> &rbl){ - for(int i=0;i>> &sbl, + const std::vector> &rbl) { + for (int i = 0; i < sbl.size(); i++) { + const auto &sb = sbl[i]; + const auto &rb = rbl[i]; + assertVectorsEqual(*sb, rb); } } -template -static void assertArraysEqual(const std::array &sb, const std::array &rb) { +template +static void assertArraysEqual(const std::array &sb, + const std::array &rb) { const int result = memcmp(sb.data(), rb.data(), sb.size()); if (result != 0) { - //std::cout<<"Data1:"< takeNRandomElements(std::vector values, const std::size_t nElements) { +static std::vector takeNRandomElements( + std::vector values, const std::size_t nElements) { assert(nElements <= values.size()); std::vector ret; for (std::size_t i = 0; i < nElements; i++) { @@ -161,44 +183,47 @@ static std::vector createIndices(const std::size_t nIndices) { } return ret; } -static bool vec_contains(std::vector indices,int index){ - for(const auto i:indices){ - if(i==index)return true; +static bool vec_contains(std::vector indices, int index) { + for (const auto i : indices) { + if (i == index) return true; } return false; } -template -static std::vector convertToP(std::vector> &buff, - std::size_t offset = 0, - std::size_t n = -1) { - if (n == -1)n = buff.size(); +template +static std::vector convertToP( + std::vector> &buff, std::size_t offset = 0, + std::size_t n = -1) { + if (n == -1) n = buff.size(); std::vector ret(n); for (int i = 0; i < ret.size(); i++) { ret[i] = buff[offset + i].data(); } return ret; } -template -static std::vector convertToP_const(std::vector> &buff, - std::size_t offset = 0, - std::size_t n = -1) { - if (n == -1)n = buff.size(); +template +static std::vector convertToP_const( + std::vector> &buff, std::size_t offset = 0, + std::size_t n = -1) { + if (n == -1) n = buff.size(); std::vector ret(n); for (int i = 0; i < ret.size(); i++) { ret[i] = buff[offset + i].data(); } return ret; } -// given an array of available indices, for each index int the rane [0...range[, check if this index is contained in the input array. -// if not, the index is "missing" and added to the return array -static std::vector findMissingIndices(const std::vector &indicesAvailable, - const std::size_t range) { +// given an array of available indices, for each index int the rane [0...range[, +// check if this index is contained in the input array. if not, the index is +// "missing" and added to the return array +static std::vector findMissingIndices( + const std::vector &indicesAvailable, + const std::size_t range) { std::vector indicesMissing; for (unsigned int i = 0; i < range; i++) { - auto found = indicesAvailable.end() != std::find(indicesAvailable.begin(), indicesAvailable.end(), i); + auto found = indicesAvailable.end() != + std::find(indicesAvailable.begin(), indicesAvailable.end(), i); if (!found) { // if not found, add to missing - //std::cout<<"Not found:"<(dur); return timeval{secs.count(), (long int)us.count()}; } -} - - +} // namespace GenericHelper -#endif //WIFIBROADCAST_HELPER_H +#endif // WIFIBROADCAST_HELPER_H diff --git a/wifibroadcast/HelperSources/README.md b/wifibroadcast/src/HelperSources/README.md similarity index 100% rename from wifibroadcast/HelperSources/README.md rename to wifibroadcast/src/HelperSources/README.md diff --git a/wifibroadcast/HelperSources/RandomBufferPot.hpp b/wifibroadcast/src/HelperSources/RandomBufferPot.hpp similarity index 66% rename from wifibroadcast/HelperSources/RandomBufferPot.hpp rename to wifibroadcast/src/HelperSources/RandomBufferPot.hpp index 2c0de6f2..a6e2bd8d 100644 --- a/wifibroadcast/HelperSources/RandomBufferPot.hpp +++ b/wifibroadcast/src/HelperSources/RandomBufferPot.hpp @@ -5,20 +5,23 @@ #ifndef WIFIBROADCAST_RANDOMBUFFERPOT_H #define WIFIBROADCAST_RANDOMBUFFERPOT_H -#include +#include #include #include +#include #include -#include + #include "Helper.hpp" namespace SemiRandomBuffers { /** - * Create @param nBuffers buffers of size @param bufferSize filled with semi-random data. - * Each of the generated buffers contains different data than the previous generated one (by a almost 100% chance) + * Create @param nBuffers buffers of size @param bufferSize filled with + * semi-random data. Each of the generated buffers contains different data than + * the previous generated one (by a almost 100% chance) */ -static std::vector>> createSemiRandomBuffers(const std::size_t nBuffers, - const std::size_t bufferSize) { +static std::vector>> +createSemiRandomBuffers(const std::size_t nBuffers, + const std::size_t bufferSize) { std::vector>> ret(nBuffers); for (int i = 0; i < ret.size(); i++) { ret[i] = std::make_shared>(bufferSize); @@ -28,30 +31,32 @@ static std::vector>> createSemiRandomBuffer std::mt19937 random_engine(0); std::uniform_int_distribution<> distrib(0, 256); - for (auto &buffer: ret) { + for (auto &buffer : ret) { assert(buffer->size() == bufferSize); - // NOTE: I think this one doesn't work since the mt19937 is copied with each invocation ?! + // NOTE: I think this one doesn't work since the mt19937 is copied with each + // invocation ?! // std::generate(buffer->data(),buffer->data()+buffer->size(),random_engine); - for (auto &value: *buffer.get()) { + for (auto &value : *buffer.get()) { value = distrib(random_engine); } } return ret; } // same as above, but different return type -template -static std::vector> createSemiRandomBuffers2(const std::size_t nBuffers) { +template +static std::vector> createSemiRandomBuffers2( + const std::size_t nBuffers) { std::vector> ret(nBuffers); std::mt19937 random_engine(0); std::uniform_int_distribution<> distrib(0, 256); - for (auto &buffer: ret) { - for (auto &value: buffer) { + for (auto &buffer : ret) { + for (auto &value : buffer) { value = distrib(random_engine); } } return ret; } -} +} // namespace SemiRandomBuffers // holds x buffers with (semi-random) data. class RandomBufferPot { @@ -60,10 +65,13 @@ class RandomBufferPot { * Holds @param nBuffers random data buffers of size @param bufferSize */ RandomBufferPot(const std::size_t nBuffers, const std::size_t bufferSize) { - m_buffers = SemiRandomBuffers::createSemiRandomBuffers(nBuffers, bufferSize); + m_buffers = + SemiRandomBuffers::createSemiRandomBuffers(nBuffers, bufferSize); } - // get a semi-random data buffer for this sequence number. If the sequence number is higher than the n of allocated buffers, - // it loops around. As long as this pot is big enough, it should be sufficient to emulate a random data stream + // get a semi-random data buffer for this sequence number. If the sequence + // number is higher than the n of allocated buffers, it loops around. As long + // as this pot is big enough, it should be sufficient to emulate a random data + // stream std::shared_ptr> getBuffer(uint64_t sequenceNumber) { auto index = sequenceNumber % m_buffers.size(); return m_buffers.at(index); @@ -71,10 +79,11 @@ class RandomBufferPot { std::shared_ptr> get_next_buffer() { return getBuffer(++m_seq_nr); } + private: std::vector>> m_buffers; - //static constexpr const uint32_t SEED=12345; - int m_seq_nr=0; + // static constexpr const uint32_t SEED=12345; + int m_seq_nr = 0; }; -#endif //WIFIBROADCAST_RANDOMBUFFERPOT_H +#endif // WIFIBROADCAST_RANDOMBUFFERPOT_H diff --git a/wifibroadcast/src/HelperSources/Rates.hpp b/wifibroadcast/src/HelperSources/Rates.hpp new file mode 100644 index 00000000..09c5a116 --- /dev/null +++ b/wifibroadcast/src/HelperSources/Rates.hpp @@ -0,0 +1,75 @@ +// +// Created by consti10 on 25.07.23. +// + +#ifndef WIFIBROADCAST_RATES_H +#define WIFIBROADCAST_RATES_H + +#include + +namespace wifibroadcast { + +// Theoretical rate(s) +struct Rate { + int rate_20mhz_kbits; + int rate_40mhz_kbits; +}; + +// From https://mcsindex.com/ +static std::vector theoretical_rates_5G() { + return { + Rate{6500, 13500}, // mcs0 (VHT0) + Rate{13000, 27000}, // mcs1 (VHT1) + Rate{19500, 40500}, // mcs2 + Rate{26000, 54000}, // mcs3 + Rate{39000, 81000}, // mcs4 + Rate{52000, 108000}, // mcs5 + Rate{58500, 121500}, // mcs6 + Rate{65000, 135000}, // mcs7 + Rate{13000, 27000}, // mcs8 (VHT0 + SS2) + Rate{26000, 54000}, // mcs9 (VHT1 + SS2) + Rate{39000, 81000}, // mcs10 (VHT2 + SS2) + Rate{52000, 108000}, // mcs11 (VHT3 + SS2) + }; +} +static Rate get_theoretical_rate_5G(int mcs) { + const auto rates = theoretical_rates_5G(); + if (mcs < 0) return {-1, 1}; + if (mcs < rates.size()) { + return rates[mcs]; + } + return rates[rates.size() - 1]; +} + +// Those values come from running the "increase bitrate until tx errors" test 3 +// times and taking the lowest (sensible) value of those runs STBC on, LDPC on, +// GUARD LONG ASUS STICK & x86 i7 laptop +static std::vector openhd_rtl8812au_5G_practical_rates() { + return { + Rate{6100, 11700}, // MCS 0 + Rate{11000, 20600}, // MCS 1 + Rate{16000, 28400}, // MCS 2 + Rate{20000, 33400}, // MCS 3 + Rate{26000, 36900}, // MCS 4 + Rate{28000, 43500}, // MCS 5 + Rate{33000, 48000}, // MCS 6 + Rate{38000, 53000}, // MCS 7 + Rate{11700, 16700}, // MCS 8 (VHT0 + SS2) + Rate{20000, 30000}, // MCS 9 (VHT1 + SS2) + Rate{25000, 37000}, // MCS 10 (VHT2 + SS2) + Rate{30000, 51000}, // MCS 11 (VHT3 + SS2) + }; +} + +static Rate get_practical_rate_5G(int mcs) { + const auto rates = openhd_rtl8812au_5G_practical_rates(); + if (mcs < 0) return {-1, 1}; + if (mcs < rates.size()) { + return rates[mcs]; + } + return rates[rates.size() - 1]; +} + +} // namespace wifibroadcast + +#endif // WIFIBROADCAST_RATES_H diff --git a/wifibroadcast/HelperSources/SchedulingHelper.hpp b/wifibroadcast/src/HelperSources/SchedulingHelper.hpp similarity index 55% rename from wifibroadcast/HelperSources/SchedulingHelper.hpp rename to wifibroadcast/src/HelperSources/SchedulingHelper.hpp index 328a6b27..9e7600bd 100644 --- a/wifibroadcast/HelperSources/SchedulingHelper.hpp +++ b/wifibroadcast/src/HelperSources/SchedulingHelper.hpp @@ -10,34 +10,35 @@ #include #include -#include #include +#include namespace SchedulingHelper { // this thread should run as close to realtime as possible // https://youtu.be/NrjXEaTSyrw?t=647 -// COMMENT: Please don't ever use 99 for your application, there are some kernel threads that run at 99 that are really important -// So ... lets use 90 for now -static void set_thread_params_max_realtime(const std::string& tag,const int priority=90) { - pthread_t target=pthread_self(); +// COMMENT: Please don't ever use 99 for your application, there are some kernel +// threads that run at 99 that are really important So ... lets use 90 for now +static void set_thread_params_max_realtime(const std::string& tag, + const int priority = 90) { + pthread_t target = pthread_self(); int policy = SCHED_FIFO; sched_param param{}; - //param.sched_priority = sched_get_priority_max(policy); - param.sched_priority=priority; + // param.sched_priority = sched_get_priority_max(policy); + param.sched_priority = priority; auto result = pthread_setschedparam(target, policy, ¶m); if (result != 0) { std::stringstream ss; - ss<<"Cannot setThreadParamsMaxRealtime "< + #include #include #include #include #include "../wifibroadcast_spdlog.h" -#include #include "StringHelper.hpp" /** - * Debug the n lost packets and the n of packet gaps by for a continuous stream of packets with increasing sequence number. + * Debug the n lost packets and the n of packet gaps by for a continuous stream + * of packets with increasing sequence number. */ class SequenceNumberDebugger { public: - SequenceNumberDebugger() { - gapsBetweenLostPackets.reserve(1000); - } + SequenceNumberDebugger() { gapsBetweenLostPackets.reserve(1000); } /** * Call when a new squence number is received * @param seqNr the received sequence number. @@ -30,14 +30,16 @@ class SequenceNumberDebugger { nReceivedPackets++; auto delta = seqNr - lastReceivedSequenceNr; if (delta <= 0) { - wifibroadcast::log::get_default()->debug("got packet nr: {} after packet nr: {}",seqNr,lastReceivedSequenceNr); + wifibroadcast::log::get_default()->debug( + "got packet nr: {} after packet nr: {}", seqNr, + lastReceivedSequenceNr); return; } if (delta > 1) { nLostPackets += delta - 1; gapsBetweenLostPackets.push_back(delta); } - if(gapsBetweenLostPackets.size()>=1000){ + if (gapsBetweenLostPackets.size() >= 1000) { gapsBetweenLostPackets.resize(0); } lastReceivedSequenceNr = seqNr; @@ -48,27 +50,31 @@ class SequenceNumberDebugger { */ void debug(bool clear) { std::stringstream ss; - ss<< "N packets received:" << nReceivedPackets << "\tlost:" << nLostPackets << "\n"; - ss<< "Packet gaps:" << StringHelper::vectorAsString(gapsBetweenLostPackets); + ss << "N packets received:" << nReceivedPackets << "\tlost:" << nLostPackets + << "\n"; + ss << "Packet gaps:" + << StringHelper::vectorAsString(gapsBetweenLostPackets); wifibroadcast::log::get_default()->debug(ss.str()); if (clear) { gapsBetweenLostPackets.resize(0); } } - void debug_in_intervals(){ - const auto elapsed=std::chrono::steady_clock::now()-m_last_log; - if(elapsed gapsBetweenLostPackets; - std::chrono::steady_clock::time_point m_last_log=std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point m_last_log = + std::chrono::steady_clock::now(); }; -#endif //WIFIBROADCAST_SRC_HELPERSOURCES_SEQUENCENUMBERDEBUGGER_H_ +#endif // WIFIBROADCAST_SRC_HELPERSOURCES_SEQUENCENUMBERDEBUGGER_H_ diff --git a/wifibroadcast/src/HelperSources/SocketHelper.hpp b/wifibroadcast/src/HelperSources/SocketHelper.hpp new file mode 100644 index 00000000..c6ff5edc --- /dev/null +++ b/wifibroadcast/src/HelperSources/SocketHelper.hpp @@ -0,0 +1,402 @@ +// +// Created by consti10 on 21.04.22. +// + +#ifndef WIFIBROADCAST_SOCKETHELPER_HPP +#define WIFIBROADCAST_SOCKETHELPER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "StringHelper.hpp" +#include "TimeHelper.hpp" +#ifdef DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES +#include "openhd_spdlog.h" +#else +#include "../wifibroadcast_spdlog.h" +#endif // DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES + +namespace SocketHelper { + +static std::shared_ptr get_console() { +#ifdef DIRTY_CONSOLE_FROM_OPENHD_SUBMODULES + return openhd::log::get_default(); +#else + return wifibroadcast::log::get_default(); +#endif +} + +struct UDPConfig { + const std::string ip; + const int port; +}; + +static std::string ip_port_as_string(const std::string ip, int port) { + return ip + ":" + std::to_string(port); +} + +static const std::string ADDRESS_LOCALHOST = "127.0.0.1"; +static const std::string ADDRESS_ANY = "0.0.0.0"; + +// returns the current socket receive timeout +static std::chrono::nanoseconds get_current_socket_send_rcv_timeout( + int socketFd, bool send = false) { + timeval tv{}; + socklen_t len = sizeof(tv); + auto res = getsockopt(socketFd, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, + &tv, &len); + assert(res == 0); + assert(len == sizeof(tv)); + return MyTimeHelper::timevalToDuration(tv); +} +// Returns size in bytes +static int get_socket_send_rcv_buff_size(int socketFd, bool send = false) { + int recvBufferSize = 0; + socklen_t len = sizeof(recvBufferSize); + getsockopt(socketFd, SOL_SOCKET, send ? SO_SNDBUF : SO_RCVBUF, + &recvBufferSize, &len); + return recvBufferSize; +} +static void set_socket_send_rcv_buffsize(int socketFd, + int buffsize_wanted_bytes, + bool send = false) { + const auto buffsize_before_bytes = + get_socket_send_rcv_buff_size(socketFd, send); + const auto res = + setsockopt(socketFd, SOL_SOCKET, send ? SO_SNDBUF : SO_RCVBUF, + &buffsize_wanted_bytes, sizeof(buffsize_wanted_bytes)); + if (res < 0) { + get_console()->warn( + "Cannot set socket {} buffsize from {} to {}", send ? "send" : "recv", + StringHelper::memorySizeReadable(buffsize_before_bytes), + StringHelper::memorySizeReadable(buffsize_wanted_bytes)); + } +} +// set the receive timeout on the socket +// throws runtime exception if this step fails (should never fail on linux) +static void set_socket_send_rcv_timeout(int socketFd, + const std::chrono::nanoseconds timeout, + bool send = false) { + const auto currentTimeout = + get_current_socket_send_rcv_timeout(socketFd, send); + if (currentTimeout != timeout) { + auto tv = MyTimeHelper::durationToTimeval(timeout); + if (setsockopt(socketFd, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, &tv, + sizeof(tv)) < 0) { + get_console()->warn( + "Cannot set socket {} timeout from {} to {}", send ? "send" : "recv", + MyTimeHelper::R(currentTimeout), MyTimeHelper::R(timeout)); + } + } +} +static void debug_send_rcv_timeout(int sockfd, + std::shared_ptr &console) { + const auto send_timeout = get_current_socket_send_rcv_timeout(sockfd, true); + const auto recv_timeout = get_current_socket_send_rcv_timeout(sockfd, false); + console->debug("Socket rcv timeout:{} send timeout:{}", + MyTimeHelper::R(recv_timeout), MyTimeHelper::R(send_timeout)); +} +static void debug_send_rcv_buffsize(int sockfd, + std::shared_ptr &console) { + const auto send_buffsize = get_socket_send_rcv_buff_size(sockfd, true); + const auto recv_buffsize = get_socket_send_rcv_buff_size(sockfd, false); + console->debug("Socket buff size rcv:{} send:{}", + StringHelper::memorySizeReadable(recv_buffsize), + StringHelper::memorySizeReadable(send_buffsize)); +} + +// Set the reuse flag on the socket, so it doesn't care if there is a broken +// down process still on the socket or not. +static void setSocketReuse(int sockfd) { + int enable = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + get_console()->warn("Cannot set socket reuse"); + } +} +// increase the UDP receive buffer size, needed for high bandwidth (at +// ~>20MBit/s the "default" udp receive buffer size is often not enough and the +// OS might (silently) drop packets on localhost) +static void increase_socket_recv_buff_size( + int sockfd, const int wanted_rcvbuff_size_bytes) { + int recvBufferSize = 0; + socklen_t len = sizeof(recvBufferSize); + getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &len); + { + std::stringstream ss; + ss << "Default UDP socket recv buffer size:" + << StringHelper::memorySizeReadable(recvBufferSize) << " wanted:" + << StringHelper::memorySizeReadable(wanted_rcvbuff_size_bytes) << "\n"; + get_console()->warn(ss.str()); + } + // We never decrease the socket receive buffer size, only increase it when + // neccessary + if (wanted_rcvbuff_size_bytes > (size_t)recvBufferSize) { + int wanted_size = wanted_rcvbuff_size_bytes; + recvBufferSize = wanted_rcvbuff_size_bytes; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &wanted_size, len)) { + get_console()->warn( + "Cannot increase UDP buffer size to {}", + StringHelper::memorySizeReadable(wanted_rcvbuff_size_bytes)); + } + // Fetch it again to double check + recvBufferSize = -1; + getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &len); + get_console()->warn( + "UDP Wanted {} Set {}", + StringHelper::memorySizeReadable(wanted_rcvbuff_size_bytes), + StringHelper::memorySizeReadable(recvBufferSize)); + } +} +// Open the specified port for udp receiving +// sets SO_REUSEADDR to true if possible +// throws a runtime exception if opening the socket fails +static int openUdpSocketForReceiving(const std::string &address, + const int port) { + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + std::stringstream ss; + ss << "Error opening socket " << port << " " << strerror(errno) << "\n"; + get_console()->warn(ss.str()); + return -1; + } + setSocketReuse(fd); + struct sockaddr_in saddr {}; + bzero((char *)&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + // saddr.sin_addr.s_addr = htonl(INADDR_ANY); + inet_aton(address.c_str(), (in_addr *)&saddr.sin_addr.s_addr); + saddr.sin_port = htons((unsigned short)port); + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + std::stringstream ss; + ss << "Bind error on socket " << address.c_str() << ":" << port << " " + << strerror(errno) << "\n"; + get_console()->warn(ss.str()); + return -1; + } + return fd; +} +// Wrapper around an UDP port you can send data to +// opens port on construction, closes port on destruction +class UDPForwarder { + public: + explicit UDPForwarder(std::string client_addr1, int client_udp_port1) + : client_addr(std::move(client_addr1)), + client_udp_port(client_udp_port1) { + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + std::stringstream message; + message << "Error opening socket:" << strerror(errno) << "\n"; + get_console()->warn(message.str()); + } + // set up the destination + bzero((char *)&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + // saddr.sin_addr.s_addr = inet_addr(client_addr.c_str()); + inet_aton(client_addr.c_str(), (in_addr *)&saddr.sin_addr.s_addr); + saddr.sin_port = htons((uint16_t)client_udp_port); + get_console()->info("UDPForwarder::configured for {} {}", client_addr, + client_udp_port); + } + UDPForwarder(const UDPForwarder &) = delete; + UDPForwarder &operator=(const UDPForwarder &) = delete; + ~UDPForwarder() { close(sockfd); } + void forwardPacketViaUDP(const uint8_t *packet, + const std::size_t packetSize) const { + // send(sockfd,packet,packetSize, MSG_DONTWAIT); + const auto ret = sendto(sockfd, packet, packetSize, 0, + (const struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0 || ret != packetSize) { + get_console()->warn("Error sending packet of size:{} to {} code:{} {}", + packetSize, + ip_port_as_string(client_addr, client_udp_port), ret, + strerror(errno)); + } + } + + private: + struct sockaddr_in saddr {}; + int sockfd; + + public: + const std::string client_addr; + const int client_udp_port; +}; + +/** + * Similar to UDP forwarder, but allows forwarding the same data to 0 or more + * IP::Port tuples + */ +class UDPMultiForwarder { + public: + /** + * Start forwarding data to another IP::Port tuple + */ + void addForwarder(const std::string &client_addr, int client_udp_port) { + std::lock_guard guard(udpForwardersLock); + // check if we already forward data to this IP::Port tuple + for (const auto &udpForwarder : udpForwarders) { + if (udpForwarder->client_addr == client_addr && + udpForwarder->client_udp_port == client_udp_port) { + get_console()->info("UDPMultiForwarder: already forwarding to: {}:{}", + client_addr, client_udp_port); + return; + } + } + get_console()->info("UDPMultiForwarder: add forwarding to: {}:{}", + client_addr, client_udp_port); + udpForwarders.emplace_back(std::make_unique( + client_addr, client_udp_port)); + } + /** + * Remove an already existing udp forwarding instance. + * Do nothing if such an instance is not found. + */ + void removeForwarder(const std::string &client_addr, int client_udp_port) { + std::lock_guard guard(udpForwardersLock); + udpForwarders.erase(std::find_if( + udpForwarders.begin(), udpForwarders.end(), + [&client_addr, &client_udp_port](const auto &udpForwarder) { + return udpForwarder->client_addr == client_addr && + udpForwarder->client_udp_port == client_udp_port; + })); + } + /** + * Forward data to all added IP::Port tuples via UDP + */ + void forwardPacketViaUDP(const uint8_t *packet, + const std::size_t packetSize) { + std::lock_guard guard(udpForwardersLock); + for (const auto &udpForwarder : udpForwarders) { + udpForwarder->forwardPacketViaUDP(packet, packetSize); + } + } + [[nodiscard]] const std::list> + &getForwarders() const { + return udpForwarders; + } + + private: + // list of host::port tuples where we send the data to. + std::list> udpForwarders; + // modifying the list of forwarders must be thread-safe + std::mutex udpForwardersLock; +}; + +class UDPReceiver { + public: + typedef std::function + OUTPUT_DATA_CALLBACK; + static constexpr const size_t UDP_PACKET_MAX_SIZE = 65507; + /** + * Receive data from socket and forward it via callback until stopLooping() is + * called + */ + explicit UDPReceiver(std::string client_addr, int client_udp_port, + OUTPUT_DATA_CALLBACK cb, + std::optional wanted_recv_buff_size = std::nullopt) + : mCb(std::move(cb)), m_wanted_recv_buff_size(wanted_recv_buff_size) { + mSocket = + SocketHelper::openUdpSocketForReceiving(client_addr, client_udp_port); + if (m_wanted_recv_buff_size != std::nullopt) { + increase_socket_recv_buff_size(mSocket, m_wanted_recv_buff_size.value()); + } + get_console()->info("UDPReceiver created with {}:{}", client_addr, + client_udp_port); + } + ~UDPReceiver() { stopBackground(); } + void loopUntilError() { + const auto buff = + std::make_unique>(); + // sockaddr_in source; + // socklen_t sourceLen= sizeof(sockaddr_in); + while (receiving) { + // const ssize_t message_length = + // recvfrom(mSocket,buff->data(),UDP_PACKET_MAX_SIZE, + // MSG_WAITALL,(sockaddr*)&source,&sourceLen); + const ssize_t message_length = + recv(mSocket, buff->data(), buff->size(), MSG_WAITALL); + if (message_length > 0) { + mCb(buff->data(), (size_t)message_length); + } else { + // this can also come from the shutdown, in which case it is not an + // error. But this way we break out of the loop. + if (receiving) { + get_console()->warn("Got message length of: {}", message_length); + } + } + } + get_console()->debug("UDP end"); + } + // Now this one is kinda special - for mavsdk we need to send messages from + // the port we are listening on to a specific IP::PORT tuple (such that the + // source address of the then received packet matches the address we are + // listening on). + void forwardPacketViaUDP(const std::string &destIp, const int destPort, + const uint8_t *packet, + const std::size_t packetSize) const { + // set up the destination + struct sockaddr_in saddr {}; + bzero((char *)&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + // saddr.sin_addr.s_addr = inet_addr(client_addr.c_str()); + inet_aton(destIp.c_str(), (in_addr *)&saddr.sin_addr.s_addr); + saddr.sin_port = htons((uint16_t)destPort); + // send from the currently bound UDP port to the destination address + const auto ret = sendto(mSocket, packet, packetSize, 0, + (const struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0 || ret != packetSize) { + get_console()->warn("Error sending packet of size:{} to {} code:{} {}", + packetSize, ip_port_as_string(destIp, destPort), ret, + strerror(errno)); + } + } + void stopLooping() { + receiving = false; + // from + // https://github.com/mavlink/MAVSDK/blob/main/src/mavsdk/core/udp_connection.cpp#L102 + shutdown(mSocket, SHUT_RDWR); + close(mSocket); + } + void runInBackground() { + if (receiverThread) { + get_console()->warn( + "Receiver thread is already running or has not been properly " + "stopped"); + return; + } + receiving = true; + receiverThread = + std::make_unique(&UDPReceiver::loopUntilError, this); + } + void stopBackground() { + stopLooping(); + if (receiverThread && receiverThread->joinable()) { + receiverThread->join(); + } + receiverThread = nullptr; + } + + private: + const OUTPUT_DATA_CALLBACK mCb; + bool receiving = true; + int mSocket; + std::unique_ptr receiverThread = nullptr; + const std::optional m_wanted_recv_buff_size; +}; +} // namespace SocketHelper + +#endif // WIFIBROADCAST_SOCKETHELPER_HPP diff --git a/wifibroadcast/src/HelperSources/StringHelper.hpp b/wifibroadcast/src/HelperSources/StringHelper.hpp new file mode 100644 index 00000000..056b12d4 --- /dev/null +++ b/wifibroadcast/src/HelperSources/StringHelper.hpp @@ -0,0 +1,127 @@ +// +// Created by Constantin on 09.10.2017. +// + +#ifndef OSDTESTER_STRINGHELPER_H +#define OSDTESTER_STRINGHELPER_H + +#include +#include +#include +#include + +class StringHelper { + public: + template + static std::string vectorAsString(const std::vector& v) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < v.size(); i++) { + ss << std::to_string(v[i]); + if (i != v.size() - 1) { + ss << ","; + } + } + ss << "]"; + return ss.str(); + } + + static std::string string_vec_as_string(const std::vector& v) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < v.size(); i++) { + ss << v[i]; + if (i != v.size() - 1) { + ss << ","; + } + } + ss << "]"; + return ss.str(); + } + static std::string bytes_as_string_decimal(const uint8_t* data, + int data_len) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < data_len; i++) { + ss << (int)data[i]; + if (i != data_len - 1) { + ss << ","; + } + } + ss << "]"; + return ss.str(); + } + static std::string byte_as_hex(uint8_t byte) { + char str[100] = {}; + sprintf(str, "%x", byte); + return "0x" + std::string(str); + } + static std::string bytes_as_string_hex(const uint8_t* data, int data_len) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < data_len; i++) { + ss << byte_as_hex(data[i]); + if (i != data_len - 1) { + ss << ","; + } + } + ss << "]"; + return ss.str(); + } + + template + static std::string arrayAsString(const std::array& a) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < a.size(); i++) { + ss << std::to_string(a[i]); + if (i != a.size() - 1) { + ss << ","; + } + } + ss << "]"; + return ss.str(); + } + + static std::string memorySizeReadable(const size_t sizeBytes) { + // more than one MB + if (sizeBytes > 1024 * 1024) { + float sizeMB = (float)sizeBytes / 1024.0 / 1024.0; + return std::to_string(sizeMB) + "mB"; + } + // more than one KB + if (sizeBytes > 1024) { + float sizeKB = (float)sizeBytes / 1024.0; + return std::to_string(sizeKB) + "kB"; + } + return std::to_string(sizeBytes) + "B"; + } + + static std::string float_to_string_with_precision(float value, + int precision = -1) { + if (precision == -1) { + return std::to_string(value); + } + std::stringstream ss; + ss.precision(precision); + ss << std::fixed << value; + return ss.str(); + } + + static std::string bitrate_readable(int64_t bits_per_second) { + if (bits_per_second <= 0) { + return std::to_string(bits_per_second) + " Bit/s"; + } + if (bits_per_second > 1024 * 1024) { + float mBitsPerSecond = (float)bits_per_second / 1000.0 / 1000.0; + return float_to_string_with_precision(mBitsPerSecond, 2) + "mBit/s"; + } + if (bits_per_second > 1024) { + float kBitsPerSecond = (float)bits_per_second / 1000.0; + return float_to_string_with_precision(kBitsPerSecond, 2) + "kBit/s"; + } + return float_to_string_with_precision(bits_per_second, 2) + "Bit/s"; + } +}; + +#endif // OSDTESTER_STRINGHELPER_H diff --git a/wifibroadcast/HelperSources/TimeHelper.hpp b/wifibroadcast/src/HelperSources/TimeHelper.hpp similarity index 55% rename from wifibroadcast/HelperSources/TimeHelper.hpp rename to wifibroadcast/src/HelperSources/TimeHelper.hpp index 848eee6b..1c290bba 100644 --- a/wifibroadcast/HelperSources/TimeHelper.hpp +++ b/wifibroadcast/src/HelperSources/TimeHelper.hpp @@ -5,47 +5,54 @@ #ifndef LIVEVIDEO10MS_TIMEHELPER_HPP #define LIVEVIDEO10MS_TIMEHELPER_HPP +#include #include #include -#include -#include #include +#include + #include "StringHelper.hpp" -// This file holds various classes/namespaces usefully for measuring and comparing -// latency samples +// This file holds various classes/namespaces usefully for measuring and +// comparing latency samples namespace MyTimeHelper { // R stands for readable. Convert a std::chrono::duration into a readable format -// Readable format is somewhat arbitrary, in this case readable means that for example -// 1second has 'ms' resolution since for values that big ns resolution probably isn't needed +// Readable format is somewhat arbitrary, in this case readable means that for +// example 1second has 'ms' resolution since for values that big ns resolution +// probably isn't needed static std::string R(const std::chrono::steady_clock::duration &dur) { const auto durAbsolute = std::chrono::abs(dur); if (durAbsolute >= std::chrono::seconds(1)) { // More than one second, print as decimal with ms resolution. - const auto ms = std::chrono::duration_cast(dur).count(); + const auto ms = + std::chrono::duration_cast(dur).count(); return std::to_string(static_cast(ms) / 1000.0f) + "s"; } if (durAbsolute >= std::chrono::milliseconds(1)) { // More than one millisecond, print as decimal with us resolution - const auto us = std::chrono::duration_cast(dur).count(); + const auto us = + std::chrono::duration_cast(dur).count(); return std::to_string(static_cast(us) / 1000.0f) + "ms"; } if (durAbsolute >= std::chrono::microseconds(1)) { // More than one microsecond, print as decimal with ns resolution - const auto ns = std::chrono::duration_cast(dur).count(); + const auto ns = + std::chrono::duration_cast(dur).count(); return std::to_string(static_cast(ns) / 1000.0f) + "us"; } - const auto ns = std::chrono::duration_cast(dur).count(); + const auto ns = + std::chrono::duration_cast(dur).count(); return std::to_string(ns) + "ns"; } static std::string ReadableNS(uint64_t nanoseconds) { return R(std::chrono::nanoseconds(nanoseconds)); } -static std::string timeSamplesAsString(const std::vector &samples) { +static std::string timeSamplesAsString( + const std::vector &samples) { std::stringstream ss; int counter = 0; - for (const auto &sample: samples) { + for (const auto &sample : samples) { ss << "," << MyTimeHelper::R(sample); counter++; if (counter % 10 == 0 && counter != samples.size()) { @@ -54,19 +61,21 @@ static std::string timeSamplesAsString(const std::vector(now.time_since_epoch()).count(); +static int get_curr_time_ms() { + auto now = std::chrono::steady_clock::now(); + return (int)std::chrono::duration_cast( + now.time_since_epoch()) + .count(); } using namespace std::chrono; static constexpr nanoseconds timevalToDuration(timeval tv) { - auto duration = seconds{tv.tv_sec} - + microseconds{tv.tv_usec}; + auto duration = seconds{tv.tv_sec} + microseconds{tv.tv_usec}; return duration_cast(duration); } static constexpr time_point @@ -85,43 +94,55 @@ static constexpr timeval durationToTimeval(nanoseconds dur) { const auto us = duration_cast(dur); return timeval{secs.count(), us.count()}; } -}; -template -struct MinMaxAvg{ +}; // namespace MyTimeHelper +template +struct MinMaxAvg { T min; T max; T avg; }; -template -static std::string min_max_avg_as_string(const MinMaxAvg& minMaxAvg,bool average_only){ +template +static std::string min_max_avg_as_string(const MinMaxAvg &minMaxAvg, + bool average_only) { std::stringstream ss; - if(average_only){ + if (average_only) { if constexpr (std::is_same_v) { - ss<<"avg="<){ - min=std::chrono::nanoseconds::max(); - }else{ - min=std::numeric_limits::max(); + // Workaround for std::numeric_limits returning 0 for + // std::chrono::nanoseconds + if constexpr (std::is_same_v) { + min = std::chrono::nanoseconds::max(); + } else { + min = std::numeric_limits::max(); } max = {}; - m_last_reset=std::chrono::steady_clock::now(); + m_last_reset = std::chrono::steady_clock::now(); } // Merges two AvgCalculator(s) that hold the same types of samples together BaseAvgCalculator operator+(const BaseAvgCalculator &other) { @@ -192,21 +210,22 @@ class BaseAvgCalculator { std::chrono::nanoseconds getMaxDifferenceMinMaxAvg() const { const auto deltaMin = std::chrono::abs(getAvg() - getMin()); const auto deltaMax = std::chrono::abs(getAvg() - getMax()); - if (deltaMin > deltaMax)return deltaMin; + if (deltaMin > deltaMax) return deltaMin; return deltaMax; } - MinMaxAvg getMinMaxAvg()const{ - return {getMin(),getMax(),getAvg()}; - } + MinMaxAvg getMinMaxAvg() const { return {getMin(), getMax(), getAvg()}; } std::string getAvgReadable(const bool averageOnly = false) const { - const auto min_max_avg=getMinMaxAvg(); - return min_max_avg_as_string(min_max_avg,averageOnly); + const auto min_max_avg = getMinMaxAvg(); + return min_max_avg_as_string(min_max_avg, averageOnly); } float getAvg_ms() { - return (float) (std::chrono::duration_cast(getAvg()).count()) / 1000.0f; + return (float)(std::chrono::duration_cast( + getAvg()) + .count()) / + 1000.0f; } - auto get_delta_since_last_reset(){ - return std::chrono::steady_clock::now()-m_last_reset; + auto get_delta_since_last_reset() { + return std::chrono::steady_clock::now() - m_last_reset; } }; // Default is using timestamps @@ -214,16 +233,18 @@ using AvgCalculator = BaseAvgCalculator; using AvgCalculatorSize = BaseAvgCalculator; // Instead of storing only the min, max and average this stores -// The last n samples in a queue. However, this makes calculating the min/max/avg values much more expensive -// And therefore should only be used with a small sample size. +// The last n samples in a queue. However, this makes calculating the +// min/max/avg values much more expensive And therefore should only be used with +// a small sample size. class AvgCalculator2 { private: const size_t sampleSize; std::deque samples; + public: // Use zero for infinite n of recorded samples // Be carefully with memory in this case - explicit AvgCalculator2(size_t sampleSize = 60) : sampleSize(sampleSize) {}; + explicit AvgCalculator2(size_t sampleSize = 60) : sampleSize(sampleSize){}; // void add(const std::chrono::nanoseconds &value) { if (value < std::chrono::nanoseconds(0)) { @@ -241,7 +262,7 @@ class AvgCalculator2 { return std::chrono::nanoseconds(0); } std::chrono::nanoseconds sum{0}; - for (const auto sample: samples) { + for (const auto sample : samples) { sum += sample; } return sum / samples.size(); @@ -252,32 +273,31 @@ class AvgCalculator2 { std::chrono::nanoseconds getMax() const { return *std::max_element(samples.begin(), samples.end()); } - void reset() { - samples.resize(0); - } - size_t getNSamples() const { - return samples.size(); - } + void reset() { samples.resize(0); } + size_t getNSamples() const { return samples.size(); } std::string getAvgReadable(const bool averageOnly = false) const { std::stringstream ss; if (averageOnly) { ss << "avg=" << MyTimeHelper::R(getAvg()); return ss.str(); } - ss << "min=" << MyTimeHelper::R(getMin()) << " max=" << MyTimeHelper::R(getMax()) << " avg=" - << MyTimeHelper::R(getAvg()) << " N samples=" << samples.size(); + ss << "min=" << MyTimeHelper::R(getMin()) + << " max=" << MyTimeHelper::R(getMax()) + << " avg=" << MyTimeHelper::R(getAvg()) + << " N samples=" << samples.size(); return ss.str(); } std::string getAllSamplesAsString() const { std::stringstream ss; - for (const auto &sample: samples) { + for (const auto &sample : samples) { ss << " " << MyTimeHelper::R(sample); } return ss.str(); } // Sort all the samples from low to high std::vector getSamplesSorted() const { - auto ret = std::vector(samples.begin(), samples.end()); + auto ret = + std::vector(samples.begin(), samples.end()); std::sort(ret.begin(), ret.end()); return ret; } @@ -291,9 +311,11 @@ class AvgCalculator2 { if (n > valuesSorted.size() / 2) { n = valuesSorted.size() / 2; } - const auto xPercentLow = std::vector(valuesSorted.begin(), valuesSorted.begin() + n); + const auto xPercentLow = std::vector( + valuesSorted.begin(), valuesSorted.begin() + n); const auto tmpBegin = valuesSorted.begin() + valuesSorted.size() - n; - const auto xPercentHigh = std::vector(tmpBegin, valuesSorted.end()); + const auto xPercentHigh = + std::vector(tmpBegin, valuesSorted.end()); std::stringstream ss; ss << n << " lowest values:\n"; ss << MyTimeHelper::timeSamplesAsString(xPercentLow); @@ -306,10 +328,12 @@ class AvgCalculator2 { std::string getOnePercentLowHigh() const { auto valuesSorted = getSamplesSorted(); const auto sizeOnePercent = valuesSorted.size() / 100; - const auto onePercentLow = - std::vector(valuesSorted.begin(), valuesSorted.begin() + sizeOnePercent); - const auto tmpBegin = valuesSorted.begin() + valuesSorted.size() - sizeOnePercent; - const auto onePercentHigh = std::vector(tmpBegin, valuesSorted.end()); + const auto onePercentLow = std::vector( + valuesSorted.begin(), valuesSorted.begin() + sizeOnePercent); + const auto tmpBegin = + valuesSorted.begin() + valuesSorted.size() - sizeOnePercent; + const auto onePercentHigh = + std::vector(tmpBegin, valuesSorted.end()); std::stringstream ss; ss << "One Percent low:\n"; ss << MyTimeHelper::timeSamplesAsString(onePercentLow); @@ -323,125 +347,143 @@ class AvgCalculator2 { class Chronometer : public AvgCalculator { public: explicit Chronometer(std::string name = "Unknown") : mName(std::move(name)) {} - void start() { - startTS = std::chrono::steady_clock::now(); - } + void start() { startTS = std::chrono::steady_clock::now(); } void stop() { const auto now = std::chrono::steady_clock::now(); const auto delta = (now - startTS); AvgCalculator::add(delta); } - void printInIntervalls(const std::chrono::steady_clock::duration &interval, const bool avgOnly = true) { + void printInIntervalls(const std::chrono::steady_clock::duration &interval, + const bool avgOnly = true) { const auto now = std::chrono::steady_clock::now(); if (now - lastLog > interval) { lastLog = now; - std::cout << (mName) << "Avg: " << AvgCalculator::getAvgReadable(avgOnly) << "\n"; + std::cout << (mName) << "Avg: " << AvgCalculator::getAvgReadable(avgOnly) + << "\n"; reset(); } } + private: const std::string mName; std::chrono::steady_clock::time_point startTS; - std::chrono::steady_clock::time_point lastLog = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point lastLog = + std::chrono::steady_clock::now(); }; class RelativeCalculator { private: long sum = 0; long sumAtLastCall = 0; + public: RelativeCalculator() = default; - void add(unsigned long x) { - sum += x; - } + void add(unsigned long x) { sum += x; } long getDeltaSinceLastCall() { long ret = sum - sumAtLastCall; sumAtLastCall = sum; return ret; } - long getAbsolute() const { - return sum; - } + long getAbsolute() const { return sum; } void reset() { sum = 0; sumAtLastCall = 0; } }; -class BitrateCalculator{ +class BitrateCalculator { private: // return: current bitrate in bits per second. // aka bits received since last call / time delta since last call. - uint64_t recalculateSinceLast(const uint64_t curr_bytes_received){ - const auto now=std::chrono::steady_clock::now(); - const auto deltaTime=now-last_time; - const auto deltaBytes=curr_bytes_received-bytes_last_time; - const auto delta_bits=deltaBytes*8; - last_time=now; - bytes_last_time=curr_bytes_received; - const auto delta_time_us=std::chrono::duration_cast(deltaTime).count(); - if(delta_time_us>0 && delta_bits>0){ - const auto bits_per_second=(delta_bits*1000*1000 / delta_time_us); + uint64_t recalculateSinceLast(const uint64_t curr_bytes_received) { + const auto now = std::chrono::steady_clock::now(); + const auto deltaTime = now - last_time; + const auto deltaBytes = curr_bytes_received - bytes_last_time; + const auto delta_bits = deltaBytes * 8; + last_time = now; + bytes_last_time = curr_bytes_received; + const auto delta_time_us = + std::chrono::duration_cast(deltaTime) + .count(); + if (delta_time_us > 0 && delta_bits > 0) { + const auto bits_per_second = (delta_bits * 1000 * 1000 / delta_time_us); return bits_per_second; - }else{ + } else { return 0; } } + public: // returns bits per second - // calculated in the given interval - uint64_t get_last_or_recalculate(uint64_t curr_bytes_received,const std::chrono::steady_clock::duration& time_between_recalculations=std::chrono::seconds(2)){ - if(std::chrono::steady_clock::now()-last_time>=time_between_recalculations){ - curr_bits_per_second= recalculateSinceLast(curr_bytes_received); + uint64_t get_last_or_recalculate( + uint64_t curr_bytes_received, + const std::chrono::steady_clock::duration &time_between_recalculations = + std::chrono::seconds(2)) { + if (std::chrono::steady_clock::now() - last_time >= + time_between_recalculations) { + curr_bits_per_second = recalculateSinceLast(curr_bytes_received); } return curr_bits_per_second; } - void reset(){ - bytes_last_time=0; - last_time=std::chrono::steady_clock::now(); - curr_bits_per_second=0; + void reset() { + bytes_last_time = 0; + last_time = std::chrono::steady_clock::now(); + curr_bits_per_second = 0; } + private: - uint64_t bytes_last_time=0; - std::chrono::steady_clock::time_point last_time=std::chrono::steady_clock::now(); - uint64_t curr_bits_per_second=0; + uint64_t bytes_last_time = 0; + std::chrono::steady_clock::time_point last_time = + std::chrono::steady_clock::now(); + uint64_t curr_bits_per_second = 0; }; -class PacketsPerSecondCalculator{ +class PacketsPerSecondCalculator { private: // return current packets per second // aka packets since last call / time delta since last call - uint64_t recalculateSinceLast(uint64_t curr_packets){ - const auto now=std::chrono::steady_clock::now(); - const auto deltaTime=now-last_time; - const auto deltaPackets=curr_packets-packets_last_time; - last_time=now; - packets_last_time=curr_packets; - const auto delta_time_us=std::chrono::duration_cast(deltaTime).count(); - if(delta_time_us>0 && deltaPackets>0){ - const auto packets_per_second=(deltaPackets*1000*1000 / delta_time_us); + uint64_t recalculateSinceLast(uint64_t curr_packets) { + const auto now = std::chrono::steady_clock::now(); + const auto deltaTime = now - last_time; + const auto deltaPackets = curr_packets - packets_last_time; + last_time = now; + packets_last_time = curr_packets; + const auto delta_time_us = + std::chrono::duration_cast(deltaTime) + .count(); + if (delta_time_us > 0 && deltaPackets > 0) { + const auto packets_per_second = + (deltaPackets * 1000 * 1000 / delta_time_us); return packets_per_second; - }else{ + } else { return 0; } } + public: - uint64_t get_last_or_recalculate(uint64_t curr_packets,const std::chrono::steady_clock::duration& time_between_recalculations=std::chrono::seconds(2)){ - if(std::chrono::steady_clock::now()-last_time>=time_between_recalculations){ - curr_packets_per_second= recalculateSinceLast(curr_packets); + uint64_t get_last_or_recalculate( + uint64_t curr_packets, + const std::chrono::steady_clock::duration &time_between_recalculations = + std::chrono::seconds(2)) { + if (std::chrono::steady_clock::now() - last_time >= + time_between_recalculations) { + curr_packets_per_second = recalculateSinceLast(curr_packets); } return curr_packets_per_second; } - void reset(){ - packets_last_time=0; - last_time=std::chrono::steady_clock::now(); - curr_packets_per_second=0; + void reset() { + packets_last_time = 0; + last_time = std::chrono::steady_clock::now(); + curr_packets_per_second = 0; } + private: - uint64_t packets_last_time=0; - std::chrono::steady_clock::time_point last_time=std::chrono::steady_clock::now(); + uint64_t packets_last_time = 0; + std::chrono::steady_clock::time_point last_time = + std::chrono::steady_clock::now(); // - uint64_t curr_packets_per_second=0; + uint64_t curr_packets_per_second = 0; }; -#endif //LIVEVIDEO10MS_TIMEHELPER_HPP \ No newline at end of file +#endif // LIVEVIDEO10MS_TIMEHELPER_HPP \ No newline at end of file diff --git a/wifibroadcast/src/HelperSources/UINT16SeqNrHelper.hpp b/wifibroadcast/src/HelperSources/UINT16SeqNrHelper.hpp new file mode 100644 index 00000000..d11a07c1 --- /dev/null +++ b/wifibroadcast/src/HelperSources/UINT16SeqNrHelper.hpp @@ -0,0 +1,146 @@ +// +// Created by consti10 on 21.12.22. +// + +#ifndef WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ +#define WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ + +#include + +#include +#include +#include + +#include "../HelperSources/StringHelper.hpp" +#include "../wifibroadcast_spdlog.h" + +// UINT16SeqNrHelper for calculating statistics for a link with a rolling (wrap +// around) uint16_t sequence number +class UINT16SeqNrHelper { + public: + UINT16SeqNrHelper() { + m_gaps.reserve(MAX_N_STORED_GAPS); + m_curr_packet_loss = -1; + } + void reset() { + m_last_seq_nr = -1; + // m_curr_packet_loss=-1; + // m_curr_gaps_counter=-1; + } + int16_t get_current_loss_percent() { return m_curr_packet_loss; } + int16_t get_current_gaps_counter() { return m_curr_gaps_counter; } + void on_new_sequence_number(uint16_t seq_nr) { + if (m_last_seq_nr == -1) { + // first ever packet + m_last_seq_nr = seq_nr; + return; + } + const auto diff = + diff_between_packets_rolling_uint16_t(m_last_seq_nr, seq_nr); + if (diff > 1) { + const auto gap_size = diff - 1; + // as an example, a diff of 2 means one packet is missing. + m_n_missing_packets += gap_size; + m_n_received_packets++; + // can be usefully for debugging + if (m_store_and_debug_gaps && gap_size > 1) { + store_debug_gap(gap_size); + } + // store_gap(diff-1); + // m_console->debug("Diff:{}",diff); + store_gap2(diff); + } else { + m_n_received_packets++; + } + m_last_seq_nr = seq_nr; + recalculate_loss_if_needed(); + } + void set_store_and_debug_gaps(bool enable) { + m_store_and_debug_gaps = enable; + } + + private: + // recalculate the loss in percentage in fixed intervals + // resets the received and missing packet count + void recalculate_loss_if_needed() { + if (std::chrono::steady_clock::now() - m_last_loss_perc_recalculation > + std::chrono::seconds(2)) { + m_last_loss_perc_recalculation = std::chrono::steady_clock::now(); + const auto n_total_packets = m_n_received_packets + m_n_missing_packets; + // m_console->debug("x_n_missing_packets:{} x_n_received_packets:{} + // n_total_packets:{}",x_n_missing_packets,x_n_received_packets,n_total_packets); + if (n_total_packets >= 1) { + const double loss_perc = static_cast(m_n_missing_packets) / + static_cast(n_total_packets) * 100.0; + // m_curr_packet_loss=static_cast(std::lround(loss_perc)); + // we always round up the packet loss + m_curr_packet_loss = static_cast(std::ceil(loss_perc)); + // wifibroadcast::log::get_default()->debug("Packet loss:{} % {} + // %",m_curr_packet_loss,loss_perc); + } else { + // We did not get any packets in the last x seconds + m_curr_packet_loss = -1; + } + m_n_received_packets = 0; + m_n_missing_packets = 0; + } + } + void store_debug_gap(int gap_size) { + m_gaps.push_back(gap_size); + const auto elasped = std::chrono::steady_clock::now() - m_last_gap_log; + if (elasped > std::chrono::seconds(1) || + m_gaps.size() >= MAX_N_STORED_GAPS) { + wifibroadcast::log::get_default()->debug( + "Gaps: {}", StringHelper::vectorAsString(m_gaps)); + m_gaps.resize(0); + m_last_gap_log = std::chrono::steady_clock::now(); + } + } + void store_gap2(int gap_size) { + if (gap_size >= GAP_SIZE_COUNTS_AS_BIG_GAP) { + m_n_big_gaps++; + } + const auto elapsed = std::chrono::steady_clock::now() - + m_last_big_gaps_counter_recalculation; + if (elapsed >= std::chrono::seconds(1)) { + m_curr_gaps_counter = (int16_t)m_n_big_gaps; + m_n_big_gaps = 0; + m_last_big_gaps_counter_recalculation = std::chrono::steady_clock::now(); + } + } + static int diff_between_packets_rolling_uint16_t(int last_packet, + int curr_packet) { + if (last_packet == curr_packet) { + wifibroadcast::log::get_default()->debug( + "Duplicate in seq nr {}-{}, invalid usage", last_packet, curr_packet); + } + if (curr_packet < last_packet) { + // We probably have overflown the uin16_t range + const auto diff = curr_packet + UINT16_MAX + 1 - last_packet; + return diff; + } else { + return curr_packet - last_packet; + } + } + + private: + int m_last_seq_nr = -1; + static constexpr int MAX_N_STORED_GAPS = 1000; + std::vector m_gaps; + static constexpr int GAP_SIZE_COUNTS_AS_BIG_GAP = 10; + + private: + int m_n_received_packets = 0; + int m_n_missing_packets = 0; + int m_n_big_gaps = 0; + std::chrono::steady_clock::time_point m_last_gap_log; + std::chrono::steady_clock::time_point m_last_loss_perc_recalculation = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point m_last_big_gaps_counter_recalculation = + std::chrono::steady_clock::now(); + std::atomic m_curr_packet_loss{}; + std::atomic m_curr_gaps_counter{}; + bool m_store_and_debug_gaps = false; +}; + +#endif // WIFIBROADCAST_SRC_HELPERSOURCES_SEQNRHELPER_H_ diff --git a/wifibroadcast/src/HelperSources/UINT64SeqNrHelper.hpp b/wifibroadcast/src/HelperSources/UINT64SeqNrHelper.hpp new file mode 100644 index 00000000..3008ed72 --- /dev/null +++ b/wifibroadcast/src/HelperSources/UINT64SeqNrHelper.hpp @@ -0,0 +1,155 @@ +// +// Created by consti10 on 08.08.23. +// + +#ifndef WIFIBROADCAST_UINT64SEQNRHELPER_HPP +#define WIFIBROADCAST_UINT64SEQNRHELPER_HPP + +#include + +#include +#include +#include + +#include "../wifibroadcast_spdlog.h" +#include "StringHelper.hpp" + +// UINT16SeqNrHelper for dealing with sequence number +// (calculate packet loss and more) +// Using a unique uint64_t nonce +class UINT64SeqNrHelper { + public: + int16_t get_current_loss_percent() { return m_curr_loss_perc.load(); } + int16_t get_current_gaps_counter() { return m_curr_gaps_counter; } + // NOTE: Does no packet re-ordering, therefore can only be used per card ! + void on_new_sequence_number(uint64_t seq_nr) { + if (m_last_seq_nr == UINT64_MAX) { + m_last_seq_nr = seq_nr; + return; + } + if (m_last_seq_nr >= seq_nr) { + // Nonce must be strictly increasing, otherwise driver is bugged and + // reorders packets Or - more likely - a tx was restarted - which is so + // rare that it is okay to log a warning here and just accept the new + // value + wifibroadcast::log::get_default()->warn( + "Invalid sequence number last:{} new:{}", m_last_seq_nr, seq_nr); + m_last_seq_nr = seq_nr; + return; + } + const auto diff = seq_nr - m_last_seq_nr; + if (diff > 10000) { + wifibroadcast::log::get_default()->warn( + "Unlikely high gap, diff {} last:{} new:{}", diff, m_last_seq_nr, + seq_nr); + m_last_seq_nr = seq_nr; + return; + } + if (diff > 1) { + // There is a gap of X packets + const auto gap_size = diff - 1; + // as an example, a diff of 2 means one packet is missing. + m_n_missing_packets += gap_size; + m_n_received_packets++; + if (m_store_and_debug_gaps && gap_size > 1) { + store_debug_gap(static_cast(gap_size)); + } + set_and_recalculate_big_gap_counter(static_cast(gap_size)); + } else { + // There is no gap between last and current packet + m_n_received_packets++; + } + recalculate_loss_if_needed(); + m_last_seq_nr = seq_nr; + } + void reset() { + m_last_seq_nr = UINT64_MAX; + m_curr_loss_perc = -1; + m_curr_gaps_counter = -1; + } + void set_store_and_debug_gaps(int card_idx, bool enable) { + m_store_and_debug_gaps = enable; + m_card_index = card_idx; + } + + private: + // recalculate the loss in percentage in fixed intervals + // resets the received and missing packet count + void recalculate_loss_if_needed() { + const auto elapsed = + std::chrono::steady_clock::now() - m_last_loss_perc_recalculation; + // Recalculate once the following limit(s) are reached: + // 1) more than 500 packets (in openhd, air to ground) + // 2) after 2 seconds if at least 10 packets are received (low bandwidth, + // ground to air) 3) after 5 seconds, regardless of n packets + const bool recalculate = + (m_n_received_packets >= 500) || + (elapsed > std::chrono::seconds(2) && m_n_received_packets >= 10) || + (elapsed > std::chrono::seconds(5)); + if (recalculate) { + m_last_loss_perc_recalculation = std::chrono::steady_clock::now(); + const auto n_total_packets = m_n_received_packets + m_n_missing_packets; + // m_console->debug("x_n_missing_packets:{} x_n_received_packets:{} + // n_total_packets:{}",x_n_missing_packets,x_n_received_packets,n_total_packets); + if (n_total_packets >= 1) { + const double loss_perc = static_cast(m_n_missing_packets) / + static_cast(n_total_packets) * 100.0; + // m_curr_packet_loss=static_cast(std::lround(loss_perc)); + // we always round up the packet loss + m_curr_loss_perc = static_cast(std::ceil(loss_perc)); + // wifibroadcast::log::get_default()->debug("Packet loss:{} % {} + // %",m_curr_packet_loss,loss_perc); + } else { + // We did not get any packets in the last x seconds + m_curr_loss_perc = -1; + } + m_n_received_packets = 0; + m_n_missing_packets = 0; + } + } + void store_debug_gap(int gap_size) { + m_gaps.push_back(gap_size); + const auto elasped = std::chrono::steady_clock::now() - m_last_gap_log; + if (elasped > std::chrono::seconds(1) || + m_gaps.size() >= MAX_N_STORED_GAPS) { + wifibroadcast::log::get_default()->debug( + "Card{} Gaps: {}", m_card_index, + StringHelper::vectorAsString(m_gaps)); + m_gaps.resize(0); + m_last_gap_log = std::chrono::steady_clock::now(); + } + } + void set_and_recalculate_big_gap_counter(int gap_size) { + if (gap_size >= GAP_SIZE_COUNTS_AS_BIG_GAP) { + m_n_big_gaps++; + } + const auto elapsed = std::chrono::steady_clock::now() - + m_last_big_gaps_counter_recalculation; + if (elapsed >= std::chrono::seconds(1)) { + m_curr_gaps_counter = (int16_t)m_n_big_gaps; + m_n_big_gaps = 0; + m_last_big_gaps_counter_recalculation = std::chrono::steady_clock::now(); + } + } + + private: + std::chrono::steady_clock::time_point m_last_loss_perc_recalculation = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point m_last_gap_log = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point m_last_big_gaps_counter_recalculation = + std::chrono::steady_clock::now(); + uint64_t m_last_seq_nr = UINT64_MAX; + std::atomic m_curr_loss_perc = -1; + std::atomic m_curr_gaps_counter{}; + int m_n_received_packets = 0; + int m_n_missing_packets = 0; + int m_n_big_gaps = 0; + static constexpr int MAX_N_STORED_GAPS = 1000; + std::vector m_gaps; + static constexpr int GAP_SIZE_COUNTS_AS_BIG_GAP = 10; + bool m_store_and_debug_gaps = false; + int m_card_index = 0; +}; + +#endif // WIFIBROADCAST_UINT64SEQNRHELPER_HPP diff --git a/wifibroadcast/src/Ieee80211Header.hpp b/wifibroadcast/src/Ieee80211Header.hpp new file mode 100644 index 00000000..d19fd501 --- /dev/null +++ b/wifibroadcast/src/Ieee80211Header.hpp @@ -0,0 +1,274 @@ +#ifndef __WIFIBROADCAST_IEEE80211_HEADER_HPP__ +#define __WIFIBROADCAST_IEEE80211_HEADER_HPP__ + +#include +#include + +#include +#include +#include +#include +#include + +#include "StringHelper.hpp" +#include "wifibroadcast_spdlog.h" + +// UINT16SeqNrHelper for dealing with the IEEE80211 header in wifibroadcast / +// openhd Usefully references: +// https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ +// https://en.wikipedia.org/wiki/802.11_Frame_Types +// https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h + +// NOTE: THIS IS THE LAYOUT OF A NORMAL IEEE80211 header +// | 2 bytes | 2 bytes | 6 bytes | 6 bytes | 6 bytes | 2 bytes | | +// control field | duration | MAC of AP | SRC MAC | DST MAC | Sequence control | +static constexpr auto IEEE80211_HEADER_SIZE_BYTES = 24; + +// UINT16SeqNrHelper for control field - we do not touch it +struct ControlField { + uint8_t part1 = 0x08; + uint8_t part2 = 0x01; +} __attribute__((packed)); +static_assert(sizeof(ControlField) == 2); + +// UINT16SeqNrHelper for sequence control field +// https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ +// Sequence Control: Contains a 4-bit fragment number subfield, used for +// fragmentation and reassembly, and a 12-bit sequence number used to number +// frames sent between a given transmitter and receiver. +struct SequenceControl { + uint8_t subfield : 4; + uint16_t sequence_nr : 12; + std::string as_debug_string() const { + std::stringstream ss; + ss << "SequenceControl[" + << "" << (int)subfield << ":" << (int)sequence_nr << "]"; + return ss.str(); + } +} __attribute__((packed)); +static_assert(sizeof(SequenceControl) == 2); + +// We use as many bytes of this header for useful purposes as possible - might +// be a bit hard to understand for beginners why we use stuff this way, but +// optimizing on a byte level is complicated and we have to account for driver +// quirks + +static constexpr auto OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR = 0x01; +static constexpr auto OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND = 0x02; + +struct Ieee80211HeaderOpenHD { + // We do not touch the control field (driver) + // ControlField control_field{}; + uint8_t control_field_part1 = 0x08; + uint8_t control_field_part2 = 0x01; + // We do not touch the duration field (driver) + uint8_t duration1 = 0x00; + uint8_t duration2 = 0x00; + // We do not touch this MAC (driver) - and set it to broadcast such that the + // monitor mode driver accepts it + std::array mac_ap = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + // We can and do use this mac - 1 byte for unique identifier (air/gnd), 4 + // bytes for part 1 of nonce, last byte for radio port + uint8_t mac_src_unique_id_part = OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; + std::array mac_src_nonce_part1 = {}; + uint8_t mac_src_radio_port = 0; + // We can and do use this mac - 1 byte for unique identifier (air/gnd), 4 + // bytes for part 2 of nonce, last byte for radio port + uint8_t mac_dst_unique_id_part = OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; + std::array mac_dst_nonce_part2 = {}; + uint8_t mac_dst_radio_port = 0; + // iee80211 sequence control ( 2 bytes ) - might be overridden by the driver, + // and/or even repurposed + uint16_t sequence_control = 0; + // ----------------------------------- DATA LAYOUT END + // ----------------------------------- + /** + * We use some of the available bytes for a 8 bytes "nonce" + */ + void write_nonce(const uint64_t nonce) { + memcpy((uint8_t*)&mac_src_nonce_part1, (uint8_t*)&nonce, 4); + memcpy((uint8_t*)&mac_dst_nonce_part2, ((uint8_t*)&nonce) + 4, 4); + // From + // https://stackoverflow.com/questions/2810280/how-to-store-a-64-bit-integer-in-two-32-bit-integers-and-convert-back-again + // mac_src_nonce_part1 = static_cast(nonce >> 32); + // mac_dst_nonce_part2 = static_cast(nonce); + } + uint64_t get_nonce() const { + uint64_t nonce = 0; + memcpy(((uint8_t*)&nonce), (uint8_t*)&mac_src_nonce_part1, 4); + memcpy(((uint8_t*)&nonce) + 4, (uint8_t*)&mac_dst_nonce_part2, 4); + return nonce; + } + /** + * NOTE: We write the radio port 2 times - this way we have a pretty reliable + * way to check if this is an openhd packet or packet from someone else + */ + void write_radio_port_src_dst(uint8_t radio_port) { + mac_src_radio_port = radio_port; + mac_dst_radio_port = radio_port; + } + /* + * We also write the unique id 2 times - same reason like with radio port + */ + void write_unique_id_src_dst(uint8_t id) { + mac_src_unique_id_part = id; + mac_dst_unique_id_part = id; + } + // Check - first byte of scr and dst mac needs to mach (unique air / gnd id) + bool has_valid_air_gnd_id() const { + return mac_src_unique_id_part == mac_dst_unique_id_part; + } + // Check - last byte of wifibroadcast and dst mac needs to match (radio port) + bool has_valid_radio_port() const { + return mac_src_radio_port == mac_dst_radio_port; + } + // validate before use (matching) + uint8_t get_valid_air_gnd_id() const { return mac_src_unique_id_part; } + // validate before use (matching) + uint8_t get_valid_radio_port() const { return mac_src_radio_port; } + bool is_data_frame() const { + return control_field_part1 == 0x08 && control_field_part2 == 0x01; + } + std::string debug_radio_ports() const { + return fmt::format("{}:{}", (int)mac_src_radio_port, + (int)mac_dst_radio_port); + } + std::string debug_unique_ids() const { + return fmt::format("{}:{}", (int)mac_src_unique_id_part, + (int)mac_dst_unique_id_part); + } + std::string debug_control_field() const { + return fmt::format("{}:{}", StringHelper::byte_as_hex(control_field_part1), + StringHelper::byte_as_hex(control_field_part2)); + } + // Only for testing ! + void dirty_write_dummy_fixed_src_dest_mac() { + uint8_t* src_mac = &mac_src_unique_id_part; + uint8_t* dst_mac = &mac_dst_unique_id_part; + static constexpr std::array dummy_mac1 = {0x00, 0x00, 0x01, + 0x01, 0x02, 0x02}; + static constexpr std::array dummy_mac2 = {0x02, 0x02, 0x01, + 0x01, 0x00, 0x00}; + memcpy(src_mac, dummy_mac1.data(), 6); + memcpy(dst_mac, dummy_mac2.data(), 6); + } + // Dirty + void write_ieee80211_seq_nr(const uint16_t seq_nr) { + uint8_t seq_nr_buf[2]; + seq_nr_buf[0] = seq_nr & 0xff; + seq_nr_buf[1] = (seq_nr >> 8) & 0xff; + memcpy((uint8_t*)&sequence_control, seq_nr_buf, 2); + } +} __attribute__((packed)); +static_assert(sizeof(Ieee80211HeaderOpenHD) == IEEE80211_HEADER_SIZE_BYTES); + +// Wrapper around the Ieee80211 header (declared as raw array initially) +// info https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ +// In the way this is declared it is an IEE80211 data frame +// https://en.wikipedia.org/wiki/802.11_Frame_Types +// TODO maybe use +// https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h +class Ieee80211HeaderRaw { + public: + static constexpr auto SIZE_BYTES = 24; + // the last byte of the mac address is recycled as a port number + static constexpr const auto SRC_MAC_LASTBYTE = 15; + static constexpr const auto DST_MAC_LASTBYTE = 21; + static constexpr const auto FRAME_SEQ_LB = 22; + static constexpr const auto FRAME_SEQ_HB = 23; + // raw data buffer + std::array data = { + 0x08, 0x01, // first 2 bytes control fiels + 0x00, 0x00, // 2 bytes duration (has this even an effect ?!) + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, // something MAC ( 6 bytes), I think Receiver address (MAC of + // AP) + 0x13, 0x22, 0x33, 0x44, + 0x55, 0x66, // something MAC ( 6 bytes), I think SRC MAC (mac of source + // STA)- last byte is used as radio port + 0x13, 0x22, 0x33, 0x44, + 0x55, 0x66, // something MAC ( 6 bytes), I think DEST MAC (mac of dest + // STA) - last byte is als used as radio port + 0x00, 0x00, // iee80211 sequence control ( 2 bytes ) + }; + // default constructor + Ieee80211HeaderRaw() = default; + /*static std::string mac_as_string(const uint8_t* mac_6bytes){ + return StringHelper::bytes_as_string_hex(mac_6bytes,6); + } + std::string header_as_string()const{ + std::stringstream ss; + ss< -#include -#include -#include -#include #include +#include +#include +#include #include -#include -#include #include -#include +#include #include -#include +#include +#include +#include +#include +#include // FEC Disabled is used for telemetry data in OpenHD. -// We have different requirements on packet loss and/or packet reordering for this type of data stream. -// Adds sizeof(FECDisabledHeader) overhead -// received packets are quaranteed to be forwarded with the following properties: -// No doplicates -// packets out of order are possible +// We have different requirements on packet loss and/or packet reordering for +// this type of data stream. Adds sizeof(FECDisabledHeader) overhead received +// packets are quaranteed to be forwarded with the following properties: No +// doplicates packets out of order are possible -struct FECDisabledHeader{ +struct FECDisabledHeader { // rolling sequence number uint64_t sequence_number; -}__attribute__ ((packed)); -static_assert(sizeof(FECDisabledHeader)==8); +} __attribute__((packed)); +static_assert(sizeof(FECDisabledHeader) == 8); // Really simple, adds a sequence number, nothing else class FECDisabledEncoder { public: - typedef std::function + typedef std::function OUTPUT_DATA_CALLBACK; OUTPUT_DATA_CALLBACK outputDataCallback; - std::vector encode_packet_buffer(const uint8_t *buf, const size_t size){ - std::vector tmp(size+sizeof(FECDisabledHeader)); + std::vector encode_packet_buffer(const uint8_t *buf, + const size_t size) { + std::vector tmp(size + sizeof(FECDisabledHeader)); FECDisabledHeader hdr{}; - hdr.sequence_number=currPacketIndex; + hdr.sequence_number = currPacketIndex; // copy the header - memcpy(tmp.data(),(uint8_t*)&hdr,sizeof(FECDisabledHeader)); + memcpy(tmp.data(), (uint8_t *)&hdr, sizeof(FECDisabledHeader)); // copy the payload - memcpy(tmp.data()+sizeof(FECDisabledHeader),buf,size); + memcpy(tmp.data() + sizeof(FECDisabledHeader), buf, size); currPacketIndex++; if (currPacketIndex == std::numeric_limits::max()) { currPacketIndex = 0; @@ -54,53 +55,63 @@ class FECDisabledEncoder { } // encodes a packet and then forwards it via the cb void encode_packet_cb(const uint8_t *buf, const size_t size) { - const auto packet= encode_packet_buffer(buf,size); - outputDataCallback(packet.data(),packet.size()); + const auto packet = encode_packet_buffer(buf, size); + outputDataCallback(packet.data(), packet.size()); } + private: uint64_t currPacketIndex = 0; }; class FECDisabledDecoder { public: - typedef std::function SEND_DECODED_PACKET; + typedef std::function + SEND_DECODED_PACKET; // WARNING: Don't forget to register this callback ! SEND_DECODED_PACKET mSendDecodedPayloadCallback; + private: // Add a limit here to not allocate infinite amounts of memory static constexpr std::size_t FEC_DISABLED_MAX_SIZE_OF_MAP = 100; std::map m_known_sequence_numbers; bool first_ever_packet = true; + public: - void process_packet(const uint8_t* data,int len){ - if(lensequence_number,payload,payload_size); + auto *hdr = (FECDisabledHeader *)data; + const uint8_t *payload = data + sizeof(FECDisabledHeader); + const auto payload_size = len - sizeof(FECDisabledHeader); + process_packet_seq_nr_and_payload(hdr->sequence_number, payload, + payload_size); } - //No duplicates, but packets out of order are possible - //counting lost packets doesn't work in this mode. It should be done by the upper level - //saves the last FEC_DISABLED_MAX_SIZE_OF_MAP sequence numbers. If the sequence number of a new packet is already inside the map, it is discarded (duplicate) - void process_packet_seq_nr_and_payload(uint64_t packetSeq,const uint8_t* payload,std::size_t payload_len){ + // No duplicates, but packets out of order are possible + // counting lost packets doesn't work in this mode. It should be done by the + // upper level saves the last FEC_DISABLED_MAX_SIZE_OF_MAP sequence numbers. + // If the sequence number of a new packet is already inside the map, it is + // discarded (duplicate) + void process_packet_seq_nr_and_payload(uint64_t packetSeq, + const uint8_t *payload, + std::size_t payload_len) { assert(mSendDecodedPayloadCallback); if (first_ever_packet) { // first ever packet. Map should be empty m_known_sequence_numbers.clear(); - mSendDecodedPayloadCallback(payload,payload_len); + mSendDecodedPayloadCallback(payload, payload_len); m_known_sequence_numbers.insert({packetSeq, nullptr}); first_ever_packet = false; } // check if packet is already known (inside the map) const auto search = m_known_sequence_numbers.find(packetSeq); if (search == m_known_sequence_numbers.end()) { - // if packet is not in the map it was not yet received(unless it is older than MAX_SIZE_OF_MAP, but that is basically impossible) - mSendDecodedPayloadCallback(payload,payload_len); + // if packet is not in the map it was not yet received(unless it is older + // than MAX_SIZE_OF_MAP, but that is basically impossible) + mSendDecodedPayloadCallback(payload, payload_len); m_known_sequence_numbers.insert({packetSeq, nullptr}); - }// else this is a duplicate + } // else this is a duplicate // house keeping, do not increase size to infinity if (m_known_sequence_numbers.size() >= FEC_DISABLED_MAX_SIZE_OF_MAP - 1) { // remove oldest element @@ -108,9 +119,7 @@ class FECDisabledDecoder { } } // - void reset_packets_map(){ - m_known_sequence_numbers.clear(); - } + void reset_packets_map() { m_known_sequence_numbers.clear(); } }; #endif // WIFIBROADCAST_SIMPLESTREAM_HPP diff --git a/wifibroadcast/src/WBStreamRx.cpp b/wifibroadcast/src/WBStreamRx.cpp new file mode 100644 index 00000000..a06593e1 --- /dev/null +++ b/wifibroadcast/src/WBStreamRx.cpp @@ -0,0 +1,172 @@ +// +// Created by consti10 on 29.06.23. +// + +#include "WBStreamRx.h" + +#include + +WBStreamRx::WBStreamRx(std::shared_ptr txrx, Options options1) + : m_txrx(txrx), m_options(options1) { + assert(m_txrx); + if (m_options.opt_console) { + m_console = m_options.opt_console; + } else { + m_console = wifibroadcast::log::create_or_get( + "wb_rx" + std::to_string(m_options.radio_port)); + } + if (m_options.enable_fec) { + m_fec_decoder = std::make_unique( + m_options.fec_rx_queue_depth, MAX_TOTAL_FRAGMENTS_PER_BLOCK, + m_options.enable_fec_debug_log, m_options.forward_gapped_fragments); + auto cb = [this](const uint8_t *data, int data_len) { + on_decoded_packet(data, data_len); + }; + m_fec_decoder->mSendDecodedPayloadCallback = cb; + } else { + m_fec_disabled_decoder = std::make_unique(); + auto cb = [this](const uint8_t *data, int data_len) { + on_decoded_packet(data, data_len); + }; + m_fec_disabled_decoder->mSendDecodedPayloadCallback = cb; + } + auto cb_packet = [this](uint64_t nonce, int wlan_index, const uint8_t *data, + const int data_len) { + this->on_new_packet(nonce, wlan_index, data, data_len); + }; + auto cb_sesssion = [this]() { this->on_new_session(); }; + auto handler = std::make_shared( + m_options.radio_port, cb_packet, cb_sesssion); + m_txrx->rx_register_stream_handler(handler); + if (m_options.enable_threading) { + m_packet_queue = + std::make_unique(m_options.packet_queue_size); + m_process_data_thread_run = true; + m_process_data_thread = + std::make_unique(&WBStreamRx::loop_process_data, this); + } +} + +WBStreamRx::~WBStreamRx() { + m_txrx->rx_unregister_stream_handler(m_options.radio_port); + if (m_options.enable_threading) { + m_process_data_thread_run = false; + if (m_process_data_thread->joinable()) { + m_process_data_thread->join(); + } + } +} + +void WBStreamRx::set_callback( + WBStreamRx::OUTPUT_DATA_CALLBACK output_data_callback) { + m_out_cb = std::move(output_data_callback); +} + +void WBStreamRx::on_new_packet(uint64_t nonce, int wlan_index, + const uint8_t *data, const int data_len) { + m_n_input_packets++; + m_n_input_bytes += data_len; + if (m_options.enable_threading) { + auto item = std::make_shared(); + item->data = std::make_shared>(data, data + data_len); + const bool res = m_packet_queue->try_enqueue(item); + if (!res) { + // would hint at too high cpu usage + m_console->warn("Cannot enqueue packet"); + } + } else { + if (m_options.enable_fec) { + if (!FECDecoder::validate_packet_size(data_len)) { + m_console->debug("invalid fec packet size {}", data_len); + return; + } + m_fec_decoder->process_valid_packet(data, data_len); + } else { + m_fec_disabled_decoder->process_packet(data, data_len); + } + } +} + +void WBStreamRx::on_new_session() { + if (m_fec_decoder) { + m_fec_decoder->reset_rx_queue(); + } + if (m_fec_disabled_decoder) { + m_fec_disabled_decoder->reset_packets_map(); + } + reset_stream_stats(); +} + +void WBStreamRx::loop_process_data() { + std::shared_ptr packet; + static constexpr std::int64_t timeout_usecs = 1000 * 1000; + while (m_process_data_thread_run) { + auto opt_packet = + m_packet_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); + if (opt_packet.has_value()) { + auto packet = opt_packet.value(); + process_queued_packet(*packet); + } + } +} + +void WBStreamRx::process_queued_packet( + const WBStreamRx::EnqueuedPacket &packet) { + assert(m_options.enable_threading == true); + if (m_options.enable_fec) { + if (!FECDecoder::validate_packet_size(packet.data->size())) { + m_console->debug("invalid fec packet size {}", packet.data->size()); + return; + } + m_fec_decoder->process_valid_packet(packet.data->data(), + packet.data->size()); + } else { + m_fec_disabled_decoder->process_packet(packet.data->data(), + packet.data->size()); + } +} + +void WBStreamRx::on_decoded_packet(const uint8_t *data, int data_len) { + m_n_output_bytes += data_len; + if (m_out_cb) { + m_out_cb(data, data_len); + } +} + +WBStreamRx::Statistics WBStreamRx::get_latest_stats() { + WBStreamRx::Statistics ret; + ret.n_input_bytes = m_n_input_bytes; + ret.n_input_packets = m_n_input_packets; + ret.curr_in_packets_per_second = + m_input_packets_per_second_calculator.get_last_or_recalculate( + m_n_input_packets, std::chrono::seconds(2)); + ret.curr_in_bits_per_second = + m_input_bitrate_calculator.get_last_or_recalculate( + m_n_input_bytes, std::chrono::seconds(2)); + ret.curr_out_bits_per_second = + m_received_bitrate_calculator.get_last_or_recalculate( + m_n_output_bytes, std::chrono::seconds(2)); + return ret; +} + +WBStreamRx::FECRxStats2 WBStreamRx::get_latest_fec_stats() { + WBStreamRx::FECRxStats2 ret; + if (m_fec_decoder) { + auto stats = m_fec_decoder->stats; + ret.count_blocks_lost = stats.count_blocks_lost; + ret.count_blocks_recovered = stats.count_blocks_recovered; + ret.count_blocks_total = stats.count_blocks_total; + ret.count_fragments_recovered = stats.count_fragments_recovered; + ret.curr_fec_decode_time = stats.curr_fec_decode_time; + } + return ret; +} + +void WBStreamRx::reset_stream_stats() { + m_n_input_bytes = 0; + m_n_input_packets = 0; +} + +void WBStreamRx::set_on_fec_block_done_cb(WBStreamRx::ON_BLOCK_DONE_CB cb) { + m_fec_decoder->m_block_done_cb = cb; +} diff --git a/wifibroadcast/WBStreamRx.h b/wifibroadcast/src/WBStreamRx.h similarity index 59% rename from wifibroadcast/WBStreamRx.h rename to wifibroadcast/src/WBStreamRx.h index 22e7c659..66e88ad5 100644 --- a/wifibroadcast/WBStreamRx.h +++ b/wifibroadcast/src/WBStreamRx.h @@ -5,16 +5,16 @@ #ifndef WIFIBROADCAST_WBSTREAMRX_H #define WIFIBROADCAST_WBSTREAMRX_H +#include "../fec/FEC.h" +#include "../fec/FECDecoder.h" #include "FunkyQueue.h" -#include "fec/FEC.h" -#include "HelperSources/Helper.hpp" -#include "HelperSources/SequenceNumberDebugger.hpp" -#include "HelperSources/TimeHelper.hpp" -#include "HelperSources/UINT16SeqNrHelper.hpp" +#include "Helper.hpp" +#include "SequenceNumberDebugger.hpp" #include "SimpleStream.hpp" +#include "TimeHelper.hpp" +#include "UINT16SeqNrHelper.hpp" #include "WBTxRx.h" #include "wifibroadcast_spdlog.h" -#include "fec/FECDecoder.h" /** * Receiver for a (multiplexed) wifbroadcast stream @@ -23,69 +23,76 @@ */ class WBStreamRx { public: - typedef std::function OUTPUT_DATA_CALLBACK; + typedef std::function + OUTPUT_DATA_CALLBACK; struct Options { // needs to match the radio port of the corresponding tx uint8_t radio_port = 0; // enable / disable fec - bool enable_fec= true; + bool enable_fec = true; // RX queue depth (max n of blocks that can be buffered in the rx pipeline) - // Use 1 if you have a single RX card, since anything else can result in stuttering (but might/is required for multiple rx card(s)) - unsigned int fec_rx_queue_depth=1; + // Use 1 if you have a single RX card, since anything else can result in + // stuttering (but might/is required for multiple rx card(s)) + unsigned int fec_rx_queue_depth = 1; // overwrite the console used for logging - std::shared_ptr opt_console=nullptr; - // enable / disable multi threading (decouples the processing of data from the thread that provided the data, - // e.g. the thread inside WBTxRx - bool enable_threading= false; + std::shared_ptr opt_console = nullptr; + // enable / disable multi threading (decouples the processing of data from + // the thread that provided the data, e.g. the thread inside WBTxRx + bool enable_threading = false; // only if threading is enabled - int packet_queue_size=20; + int packet_queue_size = 20; // enable fec debug log, obviously only if fec is enbaled - bool enable_fec_debug_log=false; + bool enable_fec_debug_log = false; // dirty - bool forward_gapped_fragments= true; + bool forward_gapped_fragments = true; }; - WBStreamRx(std::shared_ptr txrx,Options options1); + WBStreamRx(std::shared_ptr txrx, Options options1); ~WBStreamRx(); WBStreamRx(const WBStreamRx &) = delete; WBStreamRx &operator=(const WBStreamRx &) = delete; void set_callback(WBStreamRx::OUTPUT_DATA_CALLBACK output_data_callback); - struct Statistics{ - int64_t n_input_packets=0; - int64_t n_input_bytes=0; - int curr_in_packets_per_second=0; - int curr_in_bits_per_second=0; - int curr_out_bits_per_second=0; + struct Statistics { + int64_t n_input_packets = 0; + int64_t n_input_bytes = 0; + int curr_in_packets_per_second = 0; + int curr_in_bits_per_second = 0; + int curr_out_bits_per_second = 0; }; Statistics get_latest_stats(); // matches FECDecoder struct FECRxStats2 { // total block count uint64_t count_blocks_total = 0; - // a block counts as "lost" if it was removed before being fully received or recovered + // a block counts as "lost" if it was removed before being fully received or + // recovered uint64_t count_blocks_lost = 0; // a block counts as "recovered" if it was recovered using FEC packets uint64_t count_blocks_recovered = 0; - // n of primary fragments that were reconstructed during the recovery process of a block + // n of primary fragments that were reconstructed during the recovery + // process of a block uint64_t count_fragments_recovered = 0; // n of forwarded bytes - uint64_t count_bytes_forwarded=0; + uint64_t count_bytes_forwarded = 0; MinMaxAvg curr_fec_decode_time{}; }; FECRxStats2 get_latest_fec_stats(); void reset_stream_stats(); // DIRTY AF ! - typedef std::function + typedef std::function ON_BLOCK_DONE_CB; void set_on_fec_block_done_cb(ON_BLOCK_DONE_CB cb); + private: const Options m_options; std::shared_ptr m_txrx; std::shared_ptr m_console; // Callback that is called with the decoded data - WBStreamRx::OUTPUT_DATA_CALLBACK m_out_cb= nullptr; - int64_t m_n_input_packets=0; - int64_t m_n_input_bytes=0; - std::atomic m_n_output_bytes=0; + WBStreamRx::OUTPUT_DATA_CALLBACK m_out_cb = nullptr; + int64_t m_n_input_packets = 0; + int64_t m_n_input_bytes = 0; + std::atomic m_n_output_bytes = 0; BitrateCalculator m_input_bitrate_calculator{}; PacketsPerSecondCalculator m_input_packets_per_second_calculator{}; // for calculating the current rx bitrate @@ -93,19 +100,20 @@ class WBStreamRx { // On the rx, either one of those two is active at the same time. std::unique_ptr m_fec_decoder = nullptr; std::unique_ptr m_fec_disabled_decoder = nullptr; - void on_new_packet(uint64_t nonce,int wlan_index,const uint8_t *data, const int data_len); + void on_new_packet(uint64_t nonce, int wlan_index, const uint8_t *data, + const int data_len); void on_new_session(); - void on_decoded_packet(const uint8_t* data,int data_len); + void on_decoded_packet(const uint8_t *data, int data_len); // used only if threading is enabled - struct EnqueuedPacket{ + struct EnqueuedPacket { std::shared_ptr> data; }; - using PacketQueueType=FunkyQueue>; + using PacketQueueType = FunkyQueue>; std::unique_ptr m_packet_queue; - bool m_process_data_thread_run=true; + bool m_process_data_thread_run = true; std::unique_ptr m_process_data_thread; void loop_process_data(); - void process_queued_packet(const EnqueuedPacket& packet); + void process_queued_packet(const EnqueuedPacket &packet); }; #endif // WIFIBROADCAST_WBSTREAMRX_H diff --git a/wifibroadcast/src/WBStreamTx.cpp b/wifibroadcast/src/WBStreamTx.cpp new file mode 100644 index 00000000..b70abc72 --- /dev/null +++ b/wifibroadcast/src/WBStreamTx.cpp @@ -0,0 +1,310 @@ +// +// Created by consti10 on 28.06.23. +// + +#include "WBStreamTx.h" + +#include + +#include "BlockSizeHelper.hpp" +#include "SchedulingHelper.hpp" + +WBStreamTx::WBStreamTx( + std::shared_ptr txrx, Options options1, + std::shared_ptr radiotap_header_holder) + : options(options1), + m_txrx(txrx), + m_radiotap_header_holder(std::move(radiotap_header_holder)) { + assert(m_txrx); + if (options.opt_console) { + m_console = options.opt_console; + } else { + m_console = wifibroadcast::log::create_or_get( + "wb_tx" + std::to_string(options.radio_port)); + } + assert(m_console); + m_console->info("WBTransmitter radio_port: {} fec:{}", options.radio_port, + options.enable_fec ? "Y" : "N"); + if (options.enable_fec) { + // m_block_queue=std::make_unique>>(options.block_data_queue_size); + m_block_queue = + std::make_unique(options.block_data_queue_size); + m_fec_encoder = std::make_unique(); + auto cb = [this](const uint8_t* packet, int packet_len) { + send_packet(packet, packet_len); + }; + m_fec_encoder->m_out_cb = cb; + } else { + // m_packet_queue=std::make_unique>>(options.packet_data_queue_size); + m_packet_queue = + std::make_unique(options.packet_data_queue_size); + m_fec_disabled_encoder = std::make_unique(); + auto cb = [this](const uint8_t* packet, int packet_len) { + send_packet(packet, packet_len); + }; + m_fec_disabled_encoder->outputDataCallback = cb; + } + m_process_data_thread_run = true; + m_process_data_thread = + std::make_unique(&WBStreamTx::loop_process_data, this); +} + +WBStreamTx::~WBStreamTx() { + m_process_data_thread_run = false; + if (m_process_data_thread && m_process_data_thread->joinable()) { + m_process_data_thread->join(); + } +} + +bool WBStreamTx::try_enqueue_packet( + std::shared_ptr> packet, int n_injections) { + assert(!options.enable_fec); + m_n_input_packets++; + m_count_bytes_data_provided += packet->size(); + auto item = std::make_shared(); + item->data = std::move(packet); + item->n_injections = n_injections; + const bool res = m_packet_queue->try_enqueue(item); + if (!res) { + m_n_dropped_packets++; + } + return res; +} + +int WBStreamTx::enqueue_packet_dropping( + std::shared_ptr> packet, int n_injections) { + assert(!options.enable_fec); + m_n_input_packets++; + m_count_bytes_data_provided += packet->size(); + auto item = std::make_shared(); + item->data = std::move(packet); + item->n_injections = n_injections; + const int n_dropped = m_packet_queue->enqueue_or_clear_enqueue(item); + if (n_dropped > 0) { + m_n_dropped_packets += n_dropped; + } + return n_dropped; +} + +bool WBStreamTx::try_enqueue_block( + std::vector>> fragments, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time) { + assert(options.enable_fec); + m_n_input_packets += fragments.size(); + for (const auto& fragment : fragments) { + if (fragment->empty() || fragment->size() > FEC_PACKET_MAX_PAYLOAD_SIZE) { + m_console->warn("Fed fragment with incompatible size:{}", + fragment->size()); + return false; + } + m_count_bytes_data_provided += fragment->size(); + } + auto item = std::make_shared(); + item->fragments = fragments; + item->max_block_size = max_block_size; + item->fec_overhead_perc = fec_overhead_perc; + item->creation_time = creation_time; + const bool res = m_block_queue->try_enqueue(item); + if (!res) { + m_n_dropped_packets += fragments.size(); + m_n_dropped_frames++; + // m_curr_seq_nr+=fragments.size(); + } + return res; +} + +int WBStreamTx::enqueue_block_dropping( + std::vector>> fragments, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time) { + assert(options.enable_fec); + m_n_input_packets += fragments.size(); + for (const auto& fragment : fragments) { + if (fragment->empty() || fragment->size() > FEC_PACKET_MAX_PAYLOAD_SIZE) { + m_console->warn("Fed fragment with incompatible size:{}", + fragment->size()); + return false; + } + m_count_bytes_data_provided += fragment->size(); + } + auto item = std::make_shared(); + item->fragments = fragments; + item->max_block_size = max_block_size; + item->fec_overhead_perc = fec_overhead_perc; + item->creation_time = creation_time; + const int ret = m_block_queue->enqueue_or_clear_enqueue(item); + if (ret != 0) { + m_n_dropped_packets += fragments.size(); + m_n_dropped_frames += ret; + } + return ret; +} + +bool WBStreamTx::try_enqueue_frame( + std::shared_ptr> frame, int max_block_size, + int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time) { + assert(options.enable_fec); + m_n_input_packets += 1; + m_count_bytes_data_provided += frame->size(); + auto item = std::make_shared(); + item->frame = frame; + item->max_block_size = max_block_size; + item->fec_overhead_perc = fec_overhead_perc; + item->creation_time = creation_time; + const bool res = m_block_queue->try_enqueue(item); + if (!res) { + m_n_dropped_packets += 1; + m_n_dropped_frames++; + // m_curr_seq_nr+=fragments.size(); + } + return res; +} + +WBStreamTx::FECStats WBStreamTx::get_latest_fec_stats() { + WBStreamTx::FECStats ret{}; + if (m_fec_encoder) { + ret.curr_fec_encode_time = m_fec_encoder->m_curr_fec_block_encode_time; + ret.curr_fec_block_length = m_fec_encoder->m_curr_fec_block_sizes; + } + return ret; +} + +WBStreamTx::Statistics WBStreamTx::get_latest_stats() { + WBStreamTx::Statistics ret{}; + ret.n_provided_bytes = m_count_bytes_data_provided; + ret.n_provided_packets = m_n_input_packets; + ret.n_injected_packets = m_n_injected_packets; + ret.n_injected_bytes = static_cast(m_count_bytes_data_injected); + ret.current_injected_bits_per_second = + m_bitrate_calculator_injected_bytes.get_last_or_recalculate( + m_count_bytes_data_injected, std::chrono::seconds(2)); + ret.current_provided_bits_per_second = + m_bitrate_calculator_data_provided.get_last_or_recalculate( + m_count_bytes_data_provided, std::chrono::seconds(2)); + ret.n_dropped_packets = m_n_dropped_packets; + ret.n_dropped_frames = m_n_dropped_frames; + ret.current_injected_packets_per_second = + m_packets_per_second_calculator.get_last_or_recalculate( + m_n_injected_packets, std::chrono::seconds(2)); + ret.curr_block_until_tx_min_us = m_curr_block_until_tx_min_max_avg_us.min; + ret.curr_block_until_tx_max_us = m_curr_block_until_tx_min_max_avg_us.max; + ret.curr_block_until_tx_avg_us = m_curr_block_until_tx_min_max_avg_us.avg; + return ret; +} + +void WBStreamTx::loop_process_data() { + if (options.dequeue_thread_max_realtime) { + SchedulingHelper::set_thread_params_max_realtime( + "WBStreamTx::loop_process_data"); + } + static constexpr std::int64_t timeout_usecs = 100 * 1000; + if (options.enable_fec) { + while (m_process_data_thread_run) { + auto opt_frame = + m_block_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); + if (opt_frame.has_value()) { + auto frame = opt_frame.value(); + // dequeued frame + m_queue_time_calculator.add(std::chrono::steady_clock::now() - + frame->enqueue_time_point); + if (m_queue_time_calculator.get_delta_since_last_reset() > + std::chrono::seconds(1)) { + if (options.log_time_spent_in_atomic_queue) { + m_console->debug("Time in queue {}", + m_queue_time_calculator.getAvgReadable()); + } + m_queue_time_calculator.reset(); + } + process_enqueued_block(*frame); + const auto delta = + std::chrono::steady_clock::now() - frame->creation_time; + m_block_until_tx_time.add(delta); + if (m_block_until_tx_time.get_delta_since_last_reset() > + std::chrono::seconds(2)) { + if (options.log_time_blocks_until_tx) { + m_console->debug("Time until tx {}", + m_block_until_tx_time.getAvgReadable()); + } + m_curr_block_until_tx_min_max_avg_us = + min_max_avg_as_us(m_block_until_tx_time.getMinMaxAvg()); + m_block_until_tx_time.reset(); + } + } + } + } else { + std::shared_ptr packet; + while (m_process_data_thread_run) { + auto opt_packet = + m_packet_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); + if (opt_packet.has_value()) { + auto packet = opt_packet.value(); + m_queue_time_calculator.add(std::chrono::steady_clock::now() - + packet->enqueue_time_point); + if (m_queue_time_calculator.get_delta_since_last_reset() > + std::chrono::seconds(1)) { + if (options.log_time_spent_in_atomic_queue) { + m_console->debug("Time in queue {}", + m_queue_time_calculator.getAvgReadable()); + } + m_queue_time_calculator.reset(); + } + process_enqueued_packet(*packet); + } + } + } +} + +void WBStreamTx::process_enqueued_packet( + const WBStreamTx::EnqueuedPacket& packet) { + auto buff = m_fec_disabled_encoder->encode_packet_buffer(packet.data->data(), + packet.data->size()); + for (int i = 0; i < packet.n_injections; i++) { + send_packet(buff.data(), buff.size()); + } + // m_fec_disabled_encoder->encode_packet_cb(packet.data->data(),packet.data->size()); +} + +void WBStreamTx::process_enqueued_block( + const WBStreamTx::EnqueuedBlock& block) { + if (block.frame != nullptr) { + dirty_process_enqueued_frame(block); + return; + } + auto blocks = + blocksize::split_frame_if_needed(block.fragments, block.max_block_size); + for (auto& x_block : blocks) { + const auto n_secondary_f = calculate_n_secondary_fragments( + x_block.size(), block.fec_overhead_perc); + m_fec_encoder->encode_block(x_block, n_secondary_f); + } +} + +void WBStreamTx::dirty_process_enqueued_frame( + const WBStreamTx::EnqueuedBlock& block) { + // TODO: Figure out the ideal fragment size for this frame + const int MTU = 1440; + const int n_primary_fragments = blocksize::div_ceil(block.frame->size(), MTU); + const int n_secondary_fragments = calculate_n_secondary_fragments( + n_primary_fragments, block.fec_overhead_perc); + m_fec_encoder->fragment_and_encode(block.frame->data(), block.frame->size(), + n_primary_fragments, + n_secondary_fragments); +} + +void WBStreamTx::send_packet(const uint8_t* packet, int packet_len) { + const auto radiotap_header = m_radiotap_header_holder->thread_safe_get(); + const bool encrypt = m_enable_encryption.load(); + m_txrx->tx_inject_packet(options.radio_port, packet, packet_len, + radiotap_header, encrypt); + m_n_injected_packets++; + m_count_bytes_data_injected += packet_len; +} + +int WBStreamTx::get_tx_queue_available_size_approximate() { + // const auto ret=options.enable_fec ? m_block_queue->size_approx() : + // m_packet_queue->size_approx(); return (int)ret; + if (options.enable_fec) return m_block_queue->get_current_size(); + return m_packet_queue->get_current_size(); +} diff --git a/wifibroadcast/WBStreamTx.h b/wifibroadcast/src/WBStreamTx.h similarity index 53% rename from wifibroadcast/WBStreamTx.h rename to wifibroadcast/src/WBStreamTx.h index f5fc2e15..22b0d625 100644 --- a/wifibroadcast/WBStreamTx.h +++ b/wifibroadcast/src/WBStreamTx.h @@ -9,12 +9,12 @@ #include #include +#include "../fec/FEC.h" +#include "../fec/FECEncoder.h" #include "FunkyQueue.h" -#include "fec/FEC.h" #include "SimpleStream.hpp" -#include "HelperSources/TimeHelper.hpp" +#include "TimeHelper.hpp" #include "WBTxRx.h" -#include "fec/FECEncoder.h" /** * Transmitter for a (multiplexed) wifbroadcast stream @@ -27,75 +27,97 @@ class WBStreamTx { // needs to match the radio port of the corresponding rx uint8_t radio_port = 0; // size of packet data queue - int packet_data_queue_size=64; + int packet_data_queue_size = 64; // size of block / frame data queue - int block_data_queue_size=2; - // Even though setting the fec_k parameter / n of primary fragments creates similar characteristics as a link - // without fec, we have a special impl. when fec is disabled, since there we allow packets out of order and with fec_k == 1 you'd have - // packet re-ordering / packets out of order are not possible. - bool enable_fec= true; - // for development, log time items spend in the data queue (it should be close to 0) - bool log_time_spent_in_atomic_queue=false; - // for development, log time blocks (frames) spend in the data queue AND until all fragments of this element are injected - // (Basically, the time from when a frame was given to WBStreamTx and when all packets for this frame have been given - // to the linux kernel / wifi card) NOTE: this measures the time until the last FEC packet, aka it can be slightly higher than actual - // latency to the rx - bool log_time_blocks_until_tx= false; + int block_data_queue_size = 2; + // Even though setting the fec_k parameter / n of primary fragments creates + // similar characteristics as a link without fec, we have a special impl. + // when fec is disabled, since there we allow packets out of order and with + // fec_k == 1 you'd have packet re-ordering / packets out of order are not + // possible. + bool enable_fec = true; + // for development, log time items spend in the data queue (it should be + // close to 0) + bool log_time_spent_in_atomic_queue = false; + // for development, log time blocks (frames) spend in the data queue AND + // until all fragments of this element are injected (Basically, the time + // from when a frame was given to WBStreamTx and when all packets for this + // frame have been given to the linux kernel / wifi card) NOTE: this + // measures the time until the last FEC packet, aka it can be slightly + // higher than actual latency to the rx + bool log_time_blocks_until_tx = false; // overwrite the console used for logging - std::shared_ptr opt_console=nullptr; - // set sched_param = max realtime on the thread that dequeues and injects the packets - bool dequeue_thread_max_realtime= true; + std::shared_ptr opt_console = nullptr; + // set sched_param = max realtime on the thread that dequeues and injects + // the packets + bool dequeue_thread_max_realtime = true; }; - WBStreamTx(std::shared_ptr txrx,Options options,std::shared_ptr radiotap_header_holder); + WBStreamTx(std::shared_ptr txrx, Options options, + std::shared_ptr radiotap_header_holder); WBStreamTx(const WBStreamTx&) = delete; - WBStreamTx&operator=(const WBStreamTx&) = delete; + WBStreamTx& operator=(const WBStreamTx&) = delete; ~WBStreamTx(); /** * Enqueue a packet to be processed. FEC needs to be disabled in this mode. * Guaranteed to return immediately. * This method is not thread-safe. * @param packet the packet (data) to enqueue - * @param n_injections: This is especially for openhd telemetry - we have the issue that the telemetry uplink is incredibly lossy - * due to the (video) tx talking over the ground telemetry tx. However, FEC is not really suited for telemetry - - * therefore, we have a simple duplicate (aka inject the same packet more than once) feature. - * Since the FECDisabled impl. handles packet duplicates, duplicates only increase the likeliness of a specific packet being received, and - * are not forwarded multiple times. By default, don't do any packet duplication (1) + * @param n_injections: This is especially for openhd telemetry - we have the + * issue that the telemetry uplink is incredibly lossy due to the (video) tx + * talking over the ground telemetry tx. However, FEC is not really suited for + * telemetry - therefore, we have a simple duplicate (aka inject the same + * packet more than once) feature. Since the FECDisabled impl. handles packet + * duplicates, duplicates only increase the likeliness of a specific packet + * being received, and are not forwarded multiple times. By default, don't do + * any packet duplication (1) * @return true on success (space in the packet queue), false otherwise */ - bool try_enqueue_packet(std::shared_ptr> packet,int n_injections=1); - // OpenHD - if the telemetry queue runs full, instead of dropping the most recent packet, - // we clear all previous packets, then enqueue the new one. - int enqueue_packet_dropping(std::shared_ptr> packet,int n_injections=1); + bool try_enqueue_packet(std::shared_ptr> packet, + int n_injections = 1); + // OpenHD - if the telemetry queue runs full, instead of dropping the most + // recent packet, we clear all previous packets, then enqueue the new one. + int enqueue_packet_dropping(std::shared_ptr> packet, + int n_injections = 1); /** - * Enqueue a block (most likely a frame) to be processed, FEC needs to be enabled in this mode. - * Guaranteed to return immediately. - * This method is not thread-safe. - * If the n of fragments exceeds @param max_block_size, the block is split into one or more sub-blocks. + * Enqueue a block (most likely a frame) to be processed, FEC needs to be + * enabled in this mode. Guaranteed to return immediately. This method is not + * thread-safe. If the n of fragments exceeds @param max_block_size, the block + * is split into one or more sub-blocks. * @return true on success (space in the block queue), false otherwise */ - bool try_enqueue_block(std::vector>> fragments,int max_block_size,int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now()); + bool try_enqueue_block( + std::vector>> fragments, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now()); // experimental ;) - bool try_enqueue_frame(std::shared_ptr> frame,int max_block_size,int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now()); + bool try_enqueue_frame(std::shared_ptr> frame, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now()); // Temporary - for IDR frame(s) // Returns the n of dropped elements, or 0 if no elements were dropped - int enqueue_block_dropping(std::vector>> fragments,int max_block_size,int fec_overhead_perc, - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now()); + int enqueue_block_dropping( + std::vector>> fragments, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now()); // statistics - struct Statistics{ + struct Statistics { int64_t n_provided_packets; int64_t n_provided_bytes; int64_t n_injected_packets; int64_t n_injected_bytes; uint64_t current_provided_bits_per_second; uint64_t current_injected_bits_per_second; - // Other than bits per second, packets per second is also an important metric - - // Sending a lot of small packets for example should be avoided + // Other than bits per second, packets per second is also an important + // metric - Sending a lot of small packets for example should be avoided uint64_t current_injected_packets_per_second; - // N of dropped packets, increases when both the internal driver queue and the extra 124 packets queue of the tx fill up - // In FEC mode (video), every time a frame is dropped this is increased by the n of fragments in this frame + // N of dropped packets, increases when both the internal driver queue and + // the extra 124 packets queue of the tx fill up In FEC mode (video), every + // time a frame is dropped this is increased by the n of fragments in this + // frame uint64_t n_dropped_packets; int32_t n_dropped_frames; // only for frame (FEC) mode @@ -105,7 +127,7 @@ class WBStreamTx { }; Statistics get_latest_stats(); // only valid when actually doing FEC - struct FECStats{ + struct FECStats { MinMaxAvg curr_fec_encode_time{}; MinMaxAvg curr_fec_block_length{}; }; @@ -114,13 +136,12 @@ class WBStreamTx { * Enables / disables encryption for this stream * (pass encrypt=true on the inject cb) */ - void set_encryption(bool encrypt){ - m_enable_encryption=encrypt; - } + void set_encryption(bool encrypt) { m_enable_encryption = encrypt; } /** * Approximation of the remaining items in the tx block / packets queue */ int get_tx_queue_available_size_approximate(); + private: const Options options; std::shared_ptr m_txrx; @@ -129,52 +150,59 @@ class WBStreamTx { // On the tx, either one of those two is active at the same time std::unique_ptr m_fec_encoder = nullptr; std::unique_ptr m_fec_disabled_encoder = nullptr; - // We have two data queues with a slightly different layout (depending on the selected operating mode) + // We have two data queues with a slightly different layout (depending on the + // selected operating mode) struct EnqueuedPacket { - std::chrono::steady_clock::time_point enqueue_time_point=std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point enqueue_time_point = + std::chrono::steady_clock::now(); std::shared_ptr> data; int n_injections; }; struct EnqueuedBlock { - std::chrono::steady_clock::time_point enqueue_time_point=std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point creation_time=std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point enqueue_time_point = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now(); int max_block_size; int fec_overhead_perc; std::vector>> fragments; - std::shared_ptr> frame= nullptr; // replaces fragments + std::shared_ptr> frame = + nullptr; // replaces fragments }; // Used if fec is disabled, for telemetry data - using PacketQueueType=FunkyQueue>; + using PacketQueueType = FunkyQueue>; std::unique_ptr m_packet_queue; // Used if fec is enabled, for video data - using BlockQueueType=FunkyQueue>; + using BlockQueueType = FunkyQueue>; std::unique_ptr m_block_queue; - // The thread that consumes the provided packets or blocks, set to sched param realtime + // The thread that consumes the provided packets or blocks, set to sched param + // realtime std::unique_ptr m_process_data_thread; - bool m_process_data_thread_run=true; - uint64_t m_n_dropped_packets=0; - int32_t m_n_dropped_frames=0; + bool m_process_data_thread_run = true; + uint64_t m_n_dropped_packets = 0; + int32_t m_n_dropped_frames = 0; // Time fragments / blocks spend in the non-blocking atomic queue. AvgCalculator m_queue_time_calculator; AvgCalculator m_block_until_tx_time; - MinMaxAvg m_curr_block_until_tx_min_max_avg_us{0,0,0}; + MinMaxAvg m_curr_block_until_tx_min_max_avg_us{0, 0, 0}; // n of packets fed to the instance int64_t m_n_input_packets = 0; - // count of bytes we got passed (aka for example, what the video encoder produced - does not include FEC) - uint64_t m_count_bytes_data_provided =0; + // count of bytes we got passed (aka for example, what the video encoder + // produced - does not include FEC) + uint64_t m_count_bytes_data_provided = 0; // n of actually injected packets int64_t m_n_injected_packets = 0; BitrateCalculator m_bitrate_calculator_data_provided{}; // count of bytes we injected into the wifi card - uint64_t m_count_bytes_data_injected =0; + uint64_t m_count_bytes_data_injected = 0; BitrateCalculator m_bitrate_calculator_injected_bytes{}; PacketsPerSecondCalculator m_packets_per_second_calculator{}; void loop_process_data(); void process_enqueued_packet(const EnqueuedPacket& packet); void process_enqueued_block(const EnqueuedBlock& block); void dirty_process_enqueued_frame(const EnqueuedBlock& block); - void send_packet(const uint8_t* packet,int packet_len); - std::atomic m_enable_encryption=true; + void send_packet(const uint8_t* packet, int packet_len); + std::atomic m_enable_encryption = true; }; #endif // WIFIBROADCAST_WBSTREAMTX_H diff --git a/wifibroadcast/src/WBTxRx.cpp b/wifibroadcast/src/WBTxRx.cpp new file mode 100644 index 00000000..2c435dae --- /dev/null +++ b/wifibroadcast/src/WBTxRx.cpp @@ -0,0 +1,971 @@ +// +// Created by consti10 on 27.06.23. +// + +#include "WBTxRx.h" + +#include + +#include "../radiotap/RadiotapHeaderRx.hpp" +#include "../radiotap/radiotap_util.hpp" +#include "SchedulingHelper.hpp" +#include "pcap_helper.hpp" +#include "raw_socket_helper.hpp" + +WBTxRx::WBTxRx( + std::vector wifi_cards1, Options options1, + std::shared_ptr session_key_radiotap_header) + : m_options(options1), + m_wifi_cards(std::move(wifi_cards1)), + m_session_key_radiotap_header(std::move(session_key_radiotap_header)) { + assert(!m_wifi_cards.empty()); + m_console = wifibroadcast::log::create_or_get("WBTxRx"); + m_console->debug( + "[{}]", options_to_string( + wifibroadcast::get_wifi_card_names(m_wifi_cards), m_options)); + // Common error - not run as root + if (!SchedulingHelper::check_root()) { + std::cerr << "wifibroadcast needs root" << std::endl; + m_console->warn("wifibroadcast needs root"); + assert(false); + } + m_receive_pollfds.resize(m_wifi_cards.size()); + m_active_tx_card_data.resize(m_wifi_cards.size()); + for (int i = 0; i < m_wifi_cards.size(); i++) { + RxStatsPerCard tmp{}; + tmp.card_index = i; + m_rx_stats_per_card.push_back(tmp); + } + m_card_is_disconnected.resize(m_wifi_cards.size()); + for (int i = 0; i < m_wifi_cards.size(); i++) { + auto tmp = std::make_shared(); + tmp->seq_nr.set_store_and_debug_gaps(i, m_options.debug_packet_gaps); + tmp->rf_aggregator.set_debug_invalid_values(m_options.debug_rssi >= 1); + m_per_card_calc.push_back(tmp); + m_card_is_disconnected[i] = false; + } + for (int i = 0; i < m_wifi_cards.size(); i++) { + auto wifi_card = m_wifi_cards[i]; + if (wifi_card.type == wifibroadcast::WIFI_CARD_TYPE_EMULATE_GND) { + m_optional_dummy_link = std::make_unique(false); + } else if (wifi_card.type == wifibroadcast::WIFI_CARD_TYPE_EMULATE_AIR) { + m_optional_dummy_link = std::make_unique(true); + } else { + PcapTxRx pcapTxRx{}; + // RX part - using pcap + pcapTxRx.rx = wifibroadcast::pcap_helper::open_pcap_rx(wifi_card.name); + if (m_options.pcap_rx_set_direction) { + const auto ret = pcap_setdirection(pcapTxRx.rx, PCAP_D_IN); + if (ret != 0) { + m_console->debug("pcap_setdirection() returned {}", ret); + } + } + auto rx_pollfd = pcap_get_selectable_fd(pcapTxRx.rx); + m_receive_pollfds[i].fd = rx_pollfd; + m_receive_pollfds[i].events = POLLIN; + // TX part - using raw socket or pcap + if (m_options.tx_without_pcap) { + pcapTxRx.tx_sockfd = open_wifi_interface_as_raw_socket(wifi_card.name); + } else { + pcapTxRx.tx = wifibroadcast::pcap_helper::open_pcap_tx(wifi_card.name); + } + m_pcap_handles.push_back(pcapTxRx); + } + } + wb::KeyPairTxRx keypair{}; + if (m_options.secure_keypair.has_value()) { + keypair = m_options.secure_keypair.value(); + } else { + keypair = wb::generate_keypair_from_bind_phrase(); + } + m_encryptor = std::make_unique( + keypair.get_tx_key(!m_options.use_gnd_identifier)); + m_decryptor = std::make_unique( + keypair.get_rx_key(!m_options.use_gnd_identifier)); + m_encryptor->makeNewSessionKey(m_tx_sess_key_packet.sessionKeyNonce, + m_tx_sess_key_packet.sessionKeyData); + // next session key in delta ms if packets are being fed + m_session_key_next_announce_ts = std::chrono::steady_clock::now(); + // Per libsodium documentation, the first nonce should be chosen randomly + // This selects a random nonce in 32-bit range - we therefore have still + // 32-bit increasing indexes left, which means tx can run indefinitely + m_nonce = randombytes_random(); +} + +WBTxRx::~WBTxRx() { + stop_receiving(); + for (auto& fd : m_receive_pollfds) { + close(fd.fd); + } + for (auto& pcapTxRx : m_pcap_handles) { + if (pcapTxRx.rx == pcapTxRx.tx) { + pcap_close(pcapTxRx.rx); + pcapTxRx.rx = nullptr; + pcapTxRx.tx = nullptr; + } else { + if (pcapTxRx.rx != nullptr) { + pcap_close(pcapTxRx.rx); + } + if (pcapTxRx.tx != nullptr) { + pcap_close(pcapTxRx.tx); + } + if (pcapTxRx.tx_sockfd != -1) { + close(pcapTxRx.tx_sockfd); + } + } + // pcap_close(pcapTxRx.rx); + // pcap_close(pcapTxRx.tx); + } +} + +void WBTxRx::tx_inject_packet(const uint8_t stream_index, const uint8_t* data, + int data_len, + const RadiotapHeaderTx& tx_radiotap_header, + bool encrypt) { + assert(data_len <= MAX_PACKET_PAYLOAD_SIZE); + assert(stream_index >= STREAM_INDEX_MIN && stream_index <= STREAM_INDEX_MAX); + std::lock_guard guard(m_tx_mutex); + // for openhd ground station functionality + if (m_disable_all_transmissions) { + return; + } + announce_session_key_if_needed(); + // new wifi packet + const auto packet_size = + // Radiotap header comes first + RadiotapHeaderTx::SIZE_BYTES + + // Then the Ieee80211 header + Ieee80211HeaderRaw::SIZE_BYTES + + // actual data + data_len + + // encryption suffix + crypto_aead_chacha20poly1305_ABYTES; + uint8_t* packet_buff = m_tx_packet_buff.data(); + // radiotap header comes first + memcpy(packet_buff, tx_radiotap_header.getData(), + RadiotapHeaderTx::SIZE_BYTES); + // Iee80211 header comes next + // Will most likely be overridden by the driver + const auto this_packet_ieee80211_seq = m_ieee80211_seq++; + m_tx_ieee80211_hdr_openhd.write_ieee80211_seq_nr(this_packet_ieee80211_seq); + // create a new nonce for this packet + const uint64_t this_packet_nonce = m_nonce++; + RadioPort this_packet_radio_port{encrypt, stream_index}; + m_tx_ieee80211_hdr_openhd.write_radio_port_src_dst( + radio_port_to_uint8_t(this_packet_radio_port)); + const auto unique_tx_id = m_options.use_gnd_identifier + ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND + : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; + m_tx_ieee80211_hdr_openhd.write_unique_id_src_dst(unique_tx_id); + m_tx_ieee80211_hdr_openhd.write_nonce(this_packet_nonce); + if (m_options.enable_non_openhd_mode) { + // dirty, just overwrite the mac and inject + m_tx_ieee80211_hdr_openhd.dirty_write_dummy_fixed_src_dest_mac(); + } + // m_console->debug("Test Nonce:{}/{} {} {} + // {}",this_packet_nonce,m_tx_ieee80211_hdr_openhd.get_nonce(),m_tx_ieee80211_hdr_openhd.has_valid_air_gnd_id(),m_tx_ieee80211_hdr_openhd.has_valid_radio_port(), + // m_tx_ieee80211_hdr_openhd.is_data_frame()); + memcpy(packet_buff + RadiotapHeaderTx::SIZE_BYTES, + (uint8_t*)&m_tx_ieee80211_hdr_openhd, Ieee80211HeaderRaw::SIZE_BYTES); + // Then the encrypted / validated data (including encryption / validation + // suffix) + uint8_t* encrypted_data_p = packet_buff + RadiotapHeaderTx::SIZE_BYTES + + Ieee80211HeaderRaw::SIZE_BYTES; + m_encryptor->set_encryption_enabled(encrypt); + const auto before_encrypt = std::chrono::steady_clock::now(); + const auto ciphertext_len = m_encryptor->authenticate_and_encrypt( + this_packet_nonce, data, data_len, encrypted_data_p); + if (m_options.debug_encrypt_time) { + m_packet_encrypt_time.add(std::chrono::steady_clock::now() - + before_encrypt); + if (m_packet_encrypt_time.get_delta_since_last_reset() > + std::chrono::seconds(2)) { + m_console->debug("Encrypt/validate: {}", + m_packet_encrypt_time.getAvgReadable()); + m_packet_encrypt_time.reset(); + } + } + // we allocate the right size in the beginning, but check if ciphertext_len is + // actually matching what we calculated (the documentation says 'write up to n + // bytes' but they probably mean (write exactly n bytes unless an error + // occurs) + assert(data_len + crypto_aead_chacha20poly1305_ABYTES == ciphertext_len); + // we inject the packet on whatever card has the highest rx rssi right now + const bool success = + inject_radiotap_packet(m_curr_tx_card.load(), packet_buff, packet_size); + if (success) { + m_tx_stats.n_injected_bytes_excluding_overhead += data_len; + m_tx_stats.n_injected_bytes_including_overhead += packet_size; + m_tx_stats.n_injected_packets++; + } +} + +bool WBTxRx::inject_radiotap_packet(int card_index, const uint8_t* packet_buff, + int packet_size) { + // inject via pcap + int len_injected = 0; + // we inject the packet on whatever card has the highest rx rssi right now + const auto before_inject = std::chrono::steady_clock::now(); + if (m_optional_dummy_link) { + m_optional_dummy_link->tx_radiotap(packet_buff, packet_size); + len_injected = packet_size; + } else if (m_options.tx_without_pcap) { + len_injected = (int)write(m_pcap_handles[card_index].tx_sockfd, packet_buff, + packet_size); + } else { + len_injected = + pcap_inject(m_pcap_handles[card_index].tx, packet_buff, packet_size); + // const auto + // len_injected=write(m_receive_pollfds[card_index].fd,packet_buff,packet_size); + } + const auto delta_inject = std::chrono::steady_clock::now() - before_inject; + if (delta_inject >= m_options.max_sane_injection_time) { + m_tx_stats.count_tx_injections_error_hint++; + } + if (m_options.debug_tx_injection_time) { + m_tx_inject_time.add(delta_inject); + if (m_tx_inject_time.get_delta_since_last_reset() > + std::chrono::seconds(2)) { + m_console->debug("packet injection time: {}", + m_tx_inject_time.getAvgReadable()); + m_tx_inject_time.reset(); + } + if (delta_inject > m_options.max_sane_injection_time) { + m_console->debug("Injected packet ret:{} took:{}", len_injected, + MyTimeHelper::R(delta_inject)); + } + } + if (len_injected != (int)packet_size) { + // This basically should never fail - if the tx queue is full, pcap seems to + // wait ?! + if (m_options.tx_without_pcap) { + m_console->warn( + "raw sock - unable to inject packet size:{} ret:{} err:[{}]", + packet_size, len_injected, strerror(errno)); + } else { + m_console->warn("pcap -unable to inject packet size:{} ret:{} err:[{}]", + packet_size, len_injected, + pcap_geterr(m_pcap_handles[card_index].tx)); + } + m_tx_stats.count_tx_dropped_packets++; + return false; + } + return true; +} + +void WBTxRx::rx_register_callback(WBTxRx::OUTPUT_DATA_CALLBACK cb) { + m_output_cb = std::move(cb); +} + +void WBTxRx::rx_register_stream_handler( + std::shared_ptr handler) { + assert(handler); + m_rx_handlers[handler->radio_port] = handler; +} + +void WBTxRx::rx_unregister_stream_handler(uint8_t radio_port) { + m_rx_handlers.erase(radio_port); +} + +void WBTxRx::loop_receive_packets() { + if (m_options.receive_thread_max_realtime) { + SchedulingHelper::set_thread_params_max_realtime( + "WBTxRx::loop_receive_packets"); + } + std::vector packets_per_card{}; + packets_per_card.resize(m_wifi_cards.size()); + while (keep_receiving) { + if (m_optional_dummy_link) { + auto packet = m_optional_dummy_link->rx_radiotap(); + if (packet) { + on_new_packet(0, packet->data(), packet->size()); + } + continue; + } + const int timeoutMS = + (int)std::chrono::duration_cast( + std::chrono::seconds(1)) + .count(); + int rc = + poll(m_receive_pollfds.data(), m_receive_pollfds.size(), timeoutMS); + + if (rc < 0) { + if (errno == EINTR || errno == EAGAIN) continue; + m_console->warn("Poll error: {}", strerror(errno)); + } + + if (rc == 0) { + // timeout expired + if (m_options.advanced_debugging_rx) { + m_console->debug("Timeout - no packet after 1 second"); + } + recalculate_pollution_perc(); + continue; + } + // TODO Optimization: If rc>1 we have data on more than one wifi card. It + // would be better to alternating process a couple of packets from card 1, + // then card 2 or similar + for (int i = 0; rc > 0 && i < m_receive_pollfds.size(); i++) { + // m_console->debug("Got data on {}",i); + if (m_receive_pollfds[i].revents & (POLLERR | POLLNVAL)) { + if (keep_receiving) { + // we should only get errors here if the card is disconnected + m_n_receiver_errors++; + m_card_is_disconnected[i] = true; + // limit logging here + const auto elapsed = + std::chrono::steady_clock::now() - m_last_receiver_error_log; + if (elapsed > std::chrono::seconds(1)) { + m_console->warn("{} receiver errors on pcap fd {} (wlan {})", + m_n_receiver_errors, i, m_wifi_cards[i].name); + m_last_receiver_error_log = std::chrono::steady_clock::now(); + } + } else { + return; + } + } + if (m_receive_pollfds[i].revents & POLLIN) { + const auto n_packets = loop_iter_pcap(i); + packets_per_card[i] = n_packets; + rc -= 1; + } else { + packets_per_card[i] = 0; + } + } + if (m_options.debug_multi_rx_packets_variance) { + std::stringstream ss; + ss << "Packets"; + for (int i = 0; i < packets_per_card.size(); i++) { + ss << fmt::format(" Card{}:{}", i, packets_per_card[i]); + } + m_console->debug("{}", ss.str()); + } + recalculate_pollution_perc(); + } +} + +int WBTxRx::loop_iter_pcap(const int rx_index) { + pcap_t* ppcap = m_pcap_handles[rx_index].rx; + // loop while incoming queue is not empty + int nPacketsPolledUntilQueueWasEmpty = 0; + for (;;) { + struct pcap_pkthdr hdr {}; + const uint8_t* pkt = pcap_next(ppcap, &hdr); + if (pkt == nullptr) { + if (m_options.advanced_latency_debugging_rx) { + m_n_packets_polled_pcap.add(nPacketsPolledUntilQueueWasEmpty); + if (m_n_packets_polled_pcap.get_delta_since_last_reset() > + std::chrono::seconds(1)) { + m_console->debug("m_n_packets_polled_pcap: {}", + m_n_packets_polled_pcap.getAvgReadable()); + m_n_packets_polled_pcap.reset(); + } + } + break; + } + if (m_options.advanced_latency_debugging_rx) { + const auto delta = std::chrono::system_clock::now() - + MyTimeHelper::to_time_point_system_clock(hdr.ts); + m_packet_host_latency.add(delta); + if (m_packet_host_latency.get_delta_since_last_reset() > + std::chrono::seconds(1)) { + m_console->debug("packet latency {}", + m_packet_host_latency.getAvgReadable()); + m_packet_host_latency.reset(); + } + } + on_new_packet(rx_index, pkt, hdr.len); + nPacketsPolledUntilQueueWasEmpty++; + } + return nPacketsPolledUntilQueueWasEmpty; +} + +int WBTxRx::loop_iter_raw(const int rx_index) { + // loop while incoming queue is not empty + int nPacketsPolledUntilQueueWasEmpty = 0; + for (;;) { + auto buff = std::vector(PCAP_MAX_PACKET_SIZE); + // const int ret= read(0,buff.data(),buff.size()); + const int ret = recv(m_receive_pollfds[rx_index].fd, buff.data(), + buff.size(), MSG_DONTWAIT); + if (ret <= 0) { + if (m_options.advanced_latency_debugging_rx) { + m_n_packets_polled_pcap.add(nPacketsPolledUntilQueueWasEmpty); + if (m_n_packets_polled_pcap.get_delta_since_last_reset() > + std::chrono::seconds(1)) { + m_console->debug("m_n_packets_polled_pcap: {}", + m_n_packets_polled_pcap.getAvgReadable()); + m_n_packets_polled_pcap.reset(); + } + } + break; + } + on_new_packet(rx_index, buff.data(), ret); + nPacketsPolledUntilQueueWasEmpty++; + } + return nPacketsPolledUntilQueueWasEmpty; +} + +void WBTxRx::on_new_packet(const uint8_t wlan_idx, const uint8_t* pkt, + const int pkt_len) { + if (m_options.log_all_received_packets) { + m_console->debug("Got packet {} {}", wlan_idx, pkt_len); + } + const auto parsedPacket = + radiotap::rx::process_received_radiotap_packet(pkt, pkt_len); + if (parsedPacket == std::nullopt) { + // Radiotap header malformed - should never happen + if (m_options.advanced_debugging_rx) { + m_console->warn("Discarding packet due to radiotap parsing error!"); + } + return; + } + if (parsedPacket->radiotap_f_bad_fcs) { + // Bad FCS - treat as not a usable packet + if (m_options.advanced_debugging_rx) { + m_console->debug("Discarding packet due to bad FCS!"); + } + return; + } + // m_console->debug("{}",radiotap::util::radiotap_header_to_string(pkt,pkt_len)); + // m_console->debug("{}",radiotap::rx::parsed_radiotap_to_string(parsedPacket.value())); + // m_per_card_calc[wlan_idx]->rf_aggregator.on_valid_openhd_packet(parsedPacket.value()); + const uint8_t* pkt_payload = parsedPacket->payload; + const size_t pkt_payload_size = parsedPacket->payloadSize; + m_rx_stats.count_p_any++; + m_rx_stats.count_bytes_any += pkt_payload_size; + m_rx_stats_per_card[wlan_idx].count_p_any++; + if (wlan_idx == 0) { + m_pollution_total_rx_packets++; + } + const auto& rx_iee80211_hdr_openhd = + *((Ieee80211HeaderOpenHD*)parsedPacket->ieee80211Header); + // m_console->debug(parsedPacket->ieee80211Header->header_as_string()); + if (!rx_iee80211_hdr_openhd.is_data_frame()) { + if (m_options.advanced_debugging_rx) { + // we only process data frames + m_console->debug("Got packet that is not a data packet {}", + rx_iee80211_hdr_openhd.debug_control_field()); + } + return; + } + // All these edge cases should NEVER happen if using a proper tx/rx setup and + // the wifi driver isn't complete crap + if (parsedPacket->payloadSize <= 0 || + parsedPacket->payloadSize > RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE) { + m_console->warn("Discarding packet due to no actual payload !"); + return; + } + // Generic packet validation end - now to the openhd specific validation(s) + if (parsedPacket->payloadSize > RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE) { + m_console->warn("Discarding packet due to payload exceeding max {}", + (int)parsedPacket->payloadSize); + return; + } + if (!rx_iee80211_hdr_openhd.has_valid_air_gnd_id()) { + if (m_options.advanced_debugging_rx) { + m_console->debug("Got packet that has not a valid unique id {}", + rx_iee80211_hdr_openhd.debug_unique_ids()); + } + return; + } + const auto unique_air_gnd_id = rx_iee80211_hdr_openhd.get_valid_air_gnd_id(); + const auto unique_tx_id = m_options.use_gnd_identifier + ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND + : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; + const auto unique_rx_id = m_options.use_gnd_identifier + ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR + : OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND; + if (unique_air_gnd_id != unique_rx_id) { + // Rare case - when multiple RX-es are used, we might get a packet we sent + // on this air / gnd unit And on AR9271, there is a bug where the card + // itself gives injected packets back to us + if (unique_air_gnd_id == unique_tx_id) { + // Packet (most likely) originated from this unit + if (m_options.advanced_debugging_rx) { + m_console->debug( + "Got packet back on rx {} that was injected (bug or multi rx) {}", + wlan_idx, rx_iee80211_hdr_openhd.debug_unique_ids()); + } + if (wlan_idx == 0) { + m_pollution_total_rx_packets--; + } + } else { + if (m_options.advanced_debugging_rx) { + m_console->debug("Got packet with invalid unique air gnd id {}", + rx_iee80211_hdr_openhd.debug_unique_ids()); + } + } + return; + } + if (!rx_iee80211_hdr_openhd.has_valid_radio_port()) { + if (m_options.advanced_debugging_rx) { + m_console->debug("Got packet that has not a valid radio port{}", + rx_iee80211_hdr_openhd.debug_radio_ports()); + } + return; + } + const auto radio_port_raw = rx_iee80211_hdr_openhd.get_valid_radio_port(); + const RadioPort& radio_port = *(RadioPort*)&radio_port_raw; + + // m_console->debug("Packet enc:{} stream_idx:{} + // nonce:{}",radio_port.encrypted,radio_port.multiplex_index,nonce); + // Quite likely an openhd packet (I'd say pretty much 100%) but not validated + // yet + m_rx_stats.curr_n_likely_openhd_packets++; + const auto nonce = rx_iee80211_hdr_openhd.get_nonce(); + if (radio_port.multiplex_index == STREAM_INDEX_SESSION_KEY_PACKETS) { + process_session_stream_packet(wlan_idx, radio_port, parsedPacket, + pkt_payload_size, nonce); + } else { + process_common_stream_packet(wlan_idx, radio_port, pkt, pkt_len, + parsedPacket, pkt_payload, pkt_payload_size, + nonce); + } +} + +void WBTxRx::process_session_stream_packet( + const uint8_t wlan_idx, const RadioPort& radio_port, + const std::optional& parsedPacket, + const size_t pkt_payload_size, uint64_t nonce) { + // encryption bit must always be set to off on session key packets, since + // encryption serves no purpose here + if (radio_port.encrypted) { + if (m_options.advanced_debugging_rx) { + m_console->warn( + "Cannot be session key packet - encryption flag set to true"); + } + return; + } + + if (pkt_payload_size != sizeof(SessionKeyPacket)) { + if (m_options.advanced_debugging_rx) { + m_console->warn("Cannot be session key packet - size mismatch {}", + pkt_payload_size); + } + return; + } + const SessionKeyPacket& sessionKeyPacket = + *((SessionKeyPacket*)parsedPacket->payload); + const auto decrypt_res = m_decryptor->onNewPacketSessionKeyData( + sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData); + if (decrypt_res == wb::Decryptor::SESSION_VALID_NEW || + decrypt_res == wb::Decryptor::SESSION_VALID_NOT_NEW) { + if (wlan_idx == 0) { // Pollution is calculated only on card0 + m_pollution_openhd_rx_packets++; + } + m_likely_wrong_encryption_valid_session_keys++; + auto& seq_nr_for_card = m_per_card_calc.at(wlan_idx)->seq_nr; + seq_nr_for_card.on_new_sequence_number(nonce); + m_rx_stats_per_card.at(wlan_idx).curr_packet_loss = + seq_nr_for_card.get_current_loss_percent(); + } else { + m_likely_wrong_encryption_invalid_session_keys++; + } + + // A lot of invalid session keys and no valid session keys hint at a bind + // phrase mismatch + const auto elapsed_likely_wrong_key = + std::chrono::steady_clock::now() - m_likely_wrong_encryption_last_check; + if (elapsed_likely_wrong_key > std::chrono::seconds(5)) { + // No valid session key(s) and at least one invalid session key + if (m_likely_wrong_encryption_valid_session_keys == 0 && + m_likely_wrong_encryption_invalid_session_keys >= 1) { + m_rx_stats.likely_mismatching_encryption_key = true; + } else { + m_rx_stats.likely_mismatching_encryption_key = false; + } + m_likely_wrong_encryption_last_check = std::chrono::steady_clock::now(); + m_likely_wrong_encryption_valid_session_keys = 0; + m_likely_wrong_encryption_invalid_session_keys = 0; + } + + if (decrypt_res == wb::Decryptor::SESSION_VALID_NEW) { + m_console->debug("Initializing new session."); + m_rx_stats.n_received_valid_session_key_packets++; + for (const auto& handler : m_rx_handlers) { + if (auto opt_cb_session = handler.second->cb_session) { + opt_cb_session(); + } + } + } +} + +void WBTxRx::process_common_stream_packet( + const uint8_t wlan_idx, const WBTxRx::RadioPort& radio_port, + const uint8_t* pkt, const int pkt_len, + const std::optional parsedPacket, + const uint8_t* pkt_payload, const size_t pkt_payload_size, + const uint64_t nonce) { + // the payload needs to include at least one byte of actual payload and the + // encryption suffix + static constexpr auto MIN_PACKET_PAYLOAD_SIZE = + 1 + crypto_aead_chacha20poly1305_ABYTES; + if (pkt_payload_size < MIN_PACKET_PAYLOAD_SIZE) { + if (m_options.advanced_debugging_rx) { + m_console->debug("Got packet with payload of {} (min:{})", + pkt_payload_size, MIN_PACKET_PAYLOAD_SIZE); + } + return; + } + + const bool valid = process_received_data_packet( + wlan_idx, radio_port.multiplex_index, radio_port.encrypted, nonce, + pkt_payload, pkt_payload_size); + if (valid) { + if (m_options.rx_radiotap_debug_level == 1 || + m_options.rx_radiotap_debug_level == 4) { + m_console->debug("{}", + radiotap::util::radiotap_header_to_string(pkt, pkt_len)); + } + + if (m_options.rx_radiotap_debug_level == 2 || + m_options.rx_radiotap_debug_level == 4) { + m_console->debug( + "{}", radiotap::rx::parsed_radiotap_to_string(parsedPacket.value())); + } + + m_rx_stats.count_p_valid++; + m_rx_stats.count_bytes_valid += pkt_payload_size; + + // We only use known "good" packets for those stats. + auto& this_wifi_card_stats = m_rx_stats_per_card.at(wlan_idx); + PerCardCalculators& this_wifi_card_calc = *m_per_card_calc.at(wlan_idx); + if (m_options.debug_rssi >= 2) { + m_console->debug( + "{}", radiotap::rx::all_rf_path_to_string(parsedPacket->rf_paths)); + } + + this_wifi_card_calc.rf_aggregator.on_valid_openhd_packet( + parsedPacket.value()); + if (m_options.rx_radiotap_debug_level == 3 || + m_options.rx_radiotap_debug_level == 4) { + this_wifi_card_calc.rf_aggregator.debug_every_one_second(); + } + + this_wifi_card_stats.count_p_valid++; + if (wlan_idx == 0) { + m_pollution_openhd_rx_packets++; + } + { + // Same for iee80211 seq nr + // uint16_t iee_seq_nr=parsedPacket->ieee80211Header->getSequenceNumber(); + // m_seq_nr_helper_iee80211.on_new_sequence_number(iee_seq_nr); + // m_console->debug("IEE SEQ NR PACKET LOSS + // {}",m_seq_nr_helper_iee80211.get_current_loss_percent()); + } + switch_tx_card_if_needed(); + } +} + +void WBTxRx::switch_tx_card_if_needed() { + if (m_wifi_cards.size() > 1 && m_options.enable_auto_switch_tx_card) { + const auto elapsed = + std::chrono::steady_clock::now() - m_last_highest_rssi_adjustment_tp; + if (elapsed >= HIGHEST_RSSI_ADJUSTMENT_INTERVAL) { + m_last_highest_rssi_adjustment_tp = std::chrono::steady_clock::now(); + // NEW: Instead of dealing with RSSI issues, we just take whatever card + // received the most amount of packets + std::vector per_card_packet_delta; + per_card_packet_delta.reserve(m_wifi_cards.size()); + for (int i = 0; i < m_wifi_cards.size(); i++) { + RxStatsPerCard& this_card_stats = m_rx_stats_per_card.at(i); + // Check if this card is behaving "okay", aka receiving packets at the + // time + const auto delta_valid_packets = + this_card_stats.count_p_valid - + m_active_tx_card_data[i].last_received_n_valid_packets; + m_active_tx_card_data[i].last_received_n_valid_packets = + this_card_stats.count_p_valid; + per_card_packet_delta.push_back(delta_valid_packets); + } + int64_t best_packet_delta = per_card_packet_delta[m_curr_tx_card]; + int idx_card_highest_packet_delta = m_curr_tx_card; + for (int i = 0; i < m_wifi_cards.size(); i++) { + // Switch card if there is a difference of more than X packets + if (per_card_packet_delta[i] > best_packet_delta + 50) { + best_packet_delta = per_card_packet_delta[i]; + idx_card_highest_packet_delta = i; + } + } + if (m_curr_tx_card != idx_card_highest_packet_delta) { + m_console->debug("Switching to card {}", idx_card_highest_packet_delta); + m_curr_tx_card = idx_card_highest_packet_delta; + } + } + } +} + +bool WBTxRx::process_received_data_packet(int wlan_idx, uint8_t stream_index, + bool encrypted, const uint64_t nonce, + const uint8_t* payload_and_enc_suffix, + int payload_and_enc_suffix_size) { + std::shared_ptr> decrypted = + std::make_shared>( + payload_and_enc_suffix_size - crypto_aead_chacha20poly1305_ABYTES); + // after that, we have the encrypted data (and the encryption suffix) + const uint8_t* encrypted_data_with_suffix = payload_and_enc_suffix; + const auto encrypted_data_with_suffix_len = payload_and_enc_suffix_size; + + const auto before_decrypt = std::chrono::steady_clock::now(); + bool res; + if (encrypted) { + res = + m_decryptor->decrypt(nonce, encrypted_data_with_suffix, + encrypted_data_with_suffix_len, decrypted->data()); + } else { + res = m_decryptor->authenticate(nonce, encrypted_data_with_suffix, + encrypted_data_with_suffix_len, + decrypted->data()); + } + + if (res) { + if (m_options.log_all_received_validated_packets) { + m_console->debug( + "Got valid packet nonce:{} wlan_idx:{} encrypted:{} stream_index:{} " + "size:{}", + nonce, wlan_idx, encrypted, stream_index, + payload_and_enc_suffix_size); + } + if (m_options.debug_decrypt_time) { + m_packet_decrypt_time.add(std::chrono::steady_clock::now() - + before_decrypt); + if (m_packet_decrypt_time.get_delta_since_last_reset() > + std::chrono::seconds(2)) { + m_console->debug("Decrypt/Validate: {}", + m_packet_decrypt_time.getAvgReadable()); + m_packet_decrypt_time.reset(); + } + } + on_valid_data_packet(nonce, wlan_idx, stream_index, decrypted->data(), + decrypted->size()); + // Calculate sequence number stats per card + auto& seq_nr_for_card = m_per_card_calc.at(wlan_idx)->seq_nr; + seq_nr_for_card.on_new_sequence_number(nonce); + m_rx_stats_per_card.at(wlan_idx).curr_packet_loss = + seq_nr_for_card.get_current_loss_percent(); + // Update the main loss to whichever card reports the lowest loss + int lowest_loss = INT32_MAX; + for (auto& per_card_calc : m_per_card_calc) { + auto& card_loss = per_card_calc->seq_nr; + const auto loss = card_loss.get_current_loss_percent(); + if (loss < 0) { + continue; + } + if (loss < lowest_loss) { + lowest_loss = loss; + } + } + if (lowest_loss == INT32_MAX) { + lowest_loss = -1; + } + m_rx_stats.curr_lowest_packet_loss = lowest_loss; + return true; + } + // m_console->debug("Got non-wb packet {}",radio_port); + return false; +} + +void WBTxRx::on_valid_data_packet(uint64_t nonce, int wlan_index, + const uint8_t stream_index, + const uint8_t* data, const int data_len) { + if (m_output_cb != nullptr) { + m_output_cb(nonce, wlan_index, stream_index, data, data_len); + } + // find a consumer for data of this radio port + auto handler = m_rx_handlers.find(stream_index); + if (handler != m_rx_handlers.end()) { + StreamRxHandler& rxHandler = *handler->second; + rxHandler.cb_packet(nonce, wlan_index, data, data_len); + } +} + +void WBTxRx::start_receiving() { + keep_receiving = true; + m_receive_thread = + std::make_unique(&WBTxRx::loop_receive_packets, this); +} + +void WBTxRx::stop_receiving() { + keep_receiving = false; + if (m_receive_thread != nullptr) { + if (m_receive_thread->joinable()) { + m_receive_thread->join(); + } + m_receive_thread = nullptr; + } +} + +void WBTxRx::announce_session_key_if_needed() { + const auto cur_ts = std::chrono::steady_clock::now(); + if (cur_ts >= m_session_key_next_announce_ts) { + // Announce session key + send_session_key(); + m_session_key_next_announce_ts = + cur_ts + m_options.session_key_packet_interval; + } +} + +void WBTxRx::send_session_key() { + RadiotapHeaderTx tmp_radiotap_header = + m_session_key_radiotap_header->thread_safe_get(); + Ieee80211HeaderOpenHD tmp_tx_hdr{}; + const auto unique_tx_id = m_options.use_gnd_identifier + ? OPENHD_IEEE80211_HEADER_UNIQUE_ID_GND + : OPENHD_IEEE80211_HEADER_UNIQUE_ID_AIR; + tmp_tx_hdr.write_unique_id_src_dst(unique_tx_id); + RadioPort radioPort{false, STREAM_INDEX_SESSION_KEY_PACKETS}; + tmp_tx_hdr.write_radio_port_src_dst(radio_port_to_uint8_t(radioPort)); + tmp_tx_hdr.write_ieee80211_seq_nr(m_ieee80211_seq++); + tmp_tx_hdr.write_nonce(m_nonce++); + auto packet = RadiotapHelper::create_radiotap_wifi_packet( + tmp_radiotap_header, *(Ieee80211HeaderRaw*)&tmp_tx_hdr, + (uint8_t*)&m_tx_sess_key_packet, sizeof(SessionKeyPacket)); + const int packet_size = (int)packet.size(); + // NOTE: Session key is always sent via card 0 since otherwise we might pick + // up the session key intended for the ground unit from the air unit ! + const bool success = inject_radiotap_packet(0, packet.data(), packet_size); + if (success) { + // These bytes only count as "including overhead" + m_tx_stats.n_injected_bytes_including_overhead += packet_size; + m_tx_stats.n_injected_packets++; + } +} + +WBTxRx::TxStats WBTxRx::get_tx_stats() { + m_tx_stats.curr_bits_per_second_excluding_overhead = + m_tx_bitrate_calculator_excluding_overhead.get_last_or_recalculate( + m_tx_stats.n_injected_bytes_excluding_overhead); + m_tx_stats.curr_bits_per_second_including_overhead = + m_tx_bitrate_calculator_including_overhead.get_last_or_recalculate( + m_tx_stats.n_injected_bytes_including_overhead); + m_tx_stats.curr_packets_per_second = + m_tx_packets_per_second_calculator.get_last_or_recalculate( + m_tx_stats.n_injected_packets); + return m_tx_stats; +} + +WBTxRx::RxStats WBTxRx::get_rx_stats() { + WBTxRx::RxStats ret = m_rx_stats; + ret.curr_bits_per_second = + m_rx_bitrate_calculator.get_last_or_recalculate(ret.count_bytes_valid); + ret.curr_packets_per_second = + m_rx_packets_per_second_calculator.get_last_or_recalculate( + ret.count_p_valid); + return ret; +} + +WBTxRx::RxStatsPerCard WBTxRx::get_rx_stats_for_card(int card_index) { + return m_rx_stats_per_card.at(card_index); +} + +void WBTxRx::rx_reset_stats() { + m_rx_stats = RxStats{}; + m_rx_bitrate_calculator.reset(); + m_rx_packets_per_second_calculator.reset(); + for (int i = 0; i < m_wifi_cards.size(); i++) { + RxStatsPerCard card_stats{}; + card_stats.card_index = i; + m_rx_stats_per_card[i] = card_stats; + m_per_card_calc.at(i)->reset_all(); + } +} + +int WBTxRx::get_curr_active_tx_card_idx() { return m_curr_tx_card; } + +void WBTxRx::set_passive_mode(bool passive) { + m_disable_all_transmissions = passive; +} + +bool WBTxRx::get_card_has_disconnected(int card_idx) { + if (card_idx >= m_wifi_cards.size()) { + return true; + } + return m_card_is_disconnected[card_idx]; +} + +void WBTxRx::tx_reset_stats() { + m_tx_stats = TxStats{}; + m_tx_packets_per_second_calculator.reset(); + m_tx_bitrate_calculator_excluding_overhead.reset(); + m_tx_bitrate_calculator_including_overhead.reset(); +} + +void WBTxRx::recalculate_pollution_perc() { + const auto elapsed = + std::chrono::steady_clock::now() - m_last_pollution_calculation; + if (elapsed <= std::chrono::seconds(1)) { + return; + } + m_last_pollution_calculation = std::chrono::steady_clock::now(); + const auto non_openhd_packets = + m_pollution_total_rx_packets - m_pollution_openhd_rx_packets; + if (m_pollution_total_rx_packets > 0) { + double perc_non_openhd_packets = (double)non_openhd_packets / + (double)m_pollution_total_rx_packets * + 100.0; + // m_console->debug("Link pollution: {}% + // [{}:{}]",perc_non_openhd_packets,non_openhd_packets,m_pollution_total_rx_packets); + m_rx_stats.curr_link_pollution_perc = std::ceil(perc_non_openhd_packets); + // curr_link_pollution_perc=std::ceil(); + } else { + m_rx_stats.curr_link_pollution_perc = 0; + } + const int elapsed_ms = static_cast( + std::chrono::duration_cast(elapsed).count()); + m_rx_stats.curr_n_foreign_packets_pps = + (int)non_openhd_packets * 1000 / elapsed_ms; + m_pollution_total_rx_packets = 0; + m_pollution_openhd_rx_packets = 0; +} + +std::string WBTxRx::tx_stats_to_string(const WBTxRx::TxStats& data) { + return fmt::format( + "TxStats[injected packets:{} bytes:{} tx error hint/dropped:{}:{} pps:{} " + "bps:{}:{}]", + data.n_injected_packets, data.n_injected_bytes_including_overhead, + data.count_tx_injections_error_hint, data.count_tx_dropped_packets, + data.curr_packets_per_second, + StringHelper::bitrate_readable( + data.curr_bits_per_second_excluding_overhead), + StringHelper::bitrate_readable( + data.curr_bits_per_second_including_overhead)); +} +std::string WBTxRx::rx_stats_to_string(const WBTxRx::RxStats& data) { + return fmt::format( + "RxStats[packets any:{} session:{} valid:{} Loss:{}% pps:{} bps:{} " + "foreign:{}%/{}pps likely_key_mismatch:{}]", + data.count_p_any, data.n_received_valid_session_key_packets, + data.count_p_valid, data.curr_lowest_packet_loss, + data.curr_packets_per_second, data.curr_bits_per_second, + data.curr_link_pollution_perc, data.curr_n_foreign_packets_pps, + data.likely_mismatching_encryption_key); +} +std::string WBTxRx::rx_stats_per_card_to_string( + const WBTxRx::RxStatsPerCard& data) { + return fmt::format("RxStatsCard{}[packets total:{} valid:{}, loss:{}%]", + data.card_index, data.count_p_any, data.count_p_valid, + data.curr_packet_loss); +} +std::string WBTxRx::options_to_string( + const std::vector& wifi_cards, + const WBTxRx::Options& options) { + return fmt::format( + "Id:{} Cards:{} Key:{} ", options.use_gnd_identifier ? "Ground" : "Air", + StringHelper::string_vec_as_string(wifi_cards), + options.secure_keypair.has_value() ? "Custom" : "Default(openhd)"); +} + +RadiotapRxRfAggregator::CardKeyRfIndicators WBTxRx::get_rx_rf_stats_for_card( + int card_index) { + return m_per_card_calc.at(card_index)->rf_aggregator.get_current(); +} + +std::shared_ptr WBTxRx::get_dummy_link() { + return m_optional_dummy_link; +} + +void WBTxRx::PerCardCalculators::reset_all() { + seq_nr.reset(); + rf_aggregator.reset(); +} diff --git a/wifibroadcast/WBTxRx.h b/wifibroadcast/src/WBTxRx.h similarity index 97% rename from wifibroadcast/WBTxRx.h rename to wifibroadcast/src/WBTxRx.h index e89a9a7d..ed720e3e 100644 --- a/wifibroadcast/WBTxRx.h +++ b/wifibroadcast/src/WBTxRx.h @@ -15,19 +15,19 @@ #include #include -#include "HelperSources/UINT16SeqNrHelper.hpp" -#include "HelperSources/UINT64SeqNrHelper.hpp" +#include "../dummy_link/DummyLink.h" +#include "../encryption/Decryptor.h" +#include "../encryption/Encryption.h" +#include "../encryption/Encryptor.h" +#include "../radiotap/RSSIAccumulator.hpp" +#include "../radiotap/RadiotapHeaderTx.hpp" +#include "../radiotap/RadiotapHeaderTxHolder.hpp" +#include "../radiotap/RadiotapRxRfAggregator.h" +#include "../radiotap/SignalQualityAccumulator.hpp" #include "Ieee80211Header.hpp" +#include "UINT16SeqNrHelper.hpp" +#include "UINT64SeqNrHelper.hpp" #include "WiFiCard.h" -#include "dummy_link/DummyLink.h" -#include "encryption/Decryptor.h" -#include "encryption/Encryption.h" -#include "encryption/Encryptor.h" -#include "radiotap/RSSIAccumulator.hpp" -#include "radiotap/RadiotapHeaderTx.hpp" -#include "radiotap/RadiotapHeaderTxHolder.hpp" -#include "radiotap/RadiotapRxRfAggregator.h" -#include "radiotap/SignalQualityAccumulator.hpp" /** * This class exists to provide a clean, working interface to create a diff --git a/wifibroadcast/src/WBVideoStreamTx.cpp b/wifibroadcast/src/WBVideoStreamTx.cpp new file mode 100644 index 00000000..e44405e6 --- /dev/null +++ b/wifibroadcast/src/WBVideoStreamTx.cpp @@ -0,0 +1,118 @@ +// +// Created by consti10 on 06.01.24. +// + +#include "WBVideoStreamTx.h" + +#include "BlockSizeHelper.hpp" +#include "SchedulingHelper.hpp" + +struct CodecConfigPacket { + uint8_t codec_type; + uint16_t config_data_len; + // config_data_len bytes follow +}; + +WBVideoStreamTx::WBVideoStreamTx( + std::shared_ptr txrx, WBVideoStreamTx::Options options1, + std::shared_ptr radiotap_header_holder) + : options(options1), + m_txrx(txrx), + m_radiotap_header_holder(std::move(radiotap_header_holder)) { + assert(m_txrx); + if (options.opt_console) { + m_console = options.opt_console; + } else { + m_console = wifibroadcast::log::create_or_get( + "wb_tx" + std::to_string(options.radio_port)); + } + assert(m_console); + m_block_queue = std::make_unique(options.frame_queue_size); + m_fec_encoder = std::make_unique(); + auto cb = [this](const uint8_t* packet, int packet_len) { + send_packet(packet, packet_len); + }; + m_fec_encoder->m_out_cb = cb; + m_process_data_thread_run = true; + m_process_data_thread = + std::make_unique(&WBVideoStreamTx::loop_process_data, this); +} + +WBVideoStreamTx::~WBVideoStreamTx() { + m_process_data_thread_run = false; + if (m_process_data_thread && m_process_data_thread->joinable()) { + m_process_data_thread->join(); + } +} + +void WBVideoStreamTx::set_config_data( + uint8_t codec_type, std::shared_ptr> config_buff) { + auto config = std::make_shared(); + config->codec_type = codec_type; + config->config_buff = config_buff; + std::lock_guard guard(m_codec_config_mutex); + m_codec_config = config; +} + +bool WBVideoStreamTx::enqueue_frame( + std::shared_ptr> frame, int max_block_size, + int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time) { + auto item = std::make_shared(); + item->frame = frame; + item->max_block_size = max_block_size; + item->fec_overhead_perc = fec_overhead_perc; + item->creation_time = creation_time; + const bool res = m_block_queue->try_enqueue(item); + return res; +} + +void WBVideoStreamTx::loop_process_data() { + if (options.dequeue_thread_max_realtime) { + SchedulingHelper::set_thread_params_max_realtime("WBVideoStreamTx::loop"); + } + std::chrono::steady_clock::time_point last_config = + std::chrono::steady_clock::now(); + while (m_process_data_thread_run) { + auto opt_frame = + m_block_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); + if (opt_frame.has_value()) { + auto frame = opt_frame.value(); + process_enqueued_frame(*frame); + } + const auto now = std::chrono::steady_clock::now(); + if (now - last_config >= options.codec_config_interval) { + if (send_video_config()) { + last_config = now; + } + } + } +} + +void WBVideoStreamTx::process_enqueued_frame(const EnqueuedFrame& enq_frame) { + // TODO: Figure out the ideal fragment size for this frame + const int n_primary_fragments = + blocksize::div_ceil(enq_frame.frame->size(), FEC_PACKET_MAX_PAYLOAD_SIZE); + const int n_secondary_fragments = calculate_n_secondary_fragments( + n_primary_fragments, enq_frame.fec_overhead_perc); + m_fec_encoder->fragment_and_encode( + enq_frame.frame->data(), enq_frame.frame->size(), n_primary_fragments, + n_secondary_fragments); +} + +void WBVideoStreamTx::send_packet(const uint8_t* packet, int packet_len) { + const auto radiotap_header = m_radiotap_header_holder->thread_safe_get(); + const bool encrypt = m_enable_encryption.load(); + m_txrx->tx_inject_packet(options.radio_port, packet, packet_len, + radiotap_header, encrypt); +} + +bool WBVideoStreamTx::send_video_config() { + std::lock_guard guard(m_codec_config_mutex); + if (m_codec_config == nullptr) return false; + auto& config_buff = *m_codec_config->config_buff; + assert(!config_buff.empty() && config_buff.size() < MAX_PAYLOAD_BEFORE_FEC); + m_fec_encoder->fragment_and_encode(config_buff.data(), config_buff.size(), 1, + 0); + return true; +} diff --git a/wifibroadcast/src/WBVideoStreamTx.h b/wifibroadcast/src/WBVideoStreamTx.h new file mode 100644 index 00000000..2b5c75f5 --- /dev/null +++ b/wifibroadcast/src/WBVideoStreamTx.h @@ -0,0 +1,83 @@ +// +// Created by consti10 on 06.01.24. +// + +#ifndef WIFIBROADCAST_WBVIDEOSTREAMTX_H +#define WIFIBROADCAST_WBVIDEOSTREAMTX_H + +#include +#include +#include + +#include "../fec/FEC.h" +#include "../fec/FECEncoder.h" +#include "FunkyQueue.h" +#include "SimpleStream.hpp" +#include "TimeHelper.hpp" +#include "WBTxRx.h" + +class WBVideoStreamTx { + public: + struct Options { + // needs to match the radio port of the corresponding rx + uint8_t radio_port = 0; + int frame_queue_size = 2; + // overwrite the console used for logging + std::shared_ptr opt_console = nullptr; + // set sched_param = max realtime on the thread that dequeues and injects + // the packets + bool dequeue_thread_max_realtime = true; + std::chrono::milliseconds codec_config_interval = std::chrono::seconds(1); + }; + WBVideoStreamTx( + std::shared_ptr txrx, Options options, + std::shared_ptr radiotap_header_holder); + WBVideoStreamTx(const WBVideoStreamTx&) = delete; + WBVideoStreamTx& operator=(const WBVideoStreamTx&) = delete; + ~WBVideoStreamTx(); + void set_config_data(uint8_t codec_type, + std::shared_ptr> config_buff); + bool enqueue_frame(std::shared_ptr> frame, + int max_block_size, int fec_overhead_perc, + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now()); + std::atomic_int32_t in_fps = 0; + std::atomic_int32_t in_bps = 0; + std::atomic_int32_t out_pps = 0; + + private: + struct EnqueuedFrame { + std::chrono::steady_clock::time_point enqueue_time_point = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point creation_time = + std::chrono::steady_clock::now(); + int max_block_size; + int fec_overhead_perc; + std::shared_ptr> frame = nullptr; + }; + struct CodecConfigData { + uint8_t codec_type; + std::shared_ptr> config_buff = nullptr; + }; + + private: + const Options options; + std::shared_ptr m_txrx; + std::shared_ptr m_radiotap_header_holder; + std::shared_ptr m_console; + // On the tx, either one of those two is active at the same time + std::unique_ptr m_fec_encoder = nullptr; + std::unique_ptr m_process_data_thread; + using FrameQueueType = FunkyQueue>; + std::unique_ptr m_block_queue; + bool m_process_data_thread_run = true; + void loop_process_data(); + void process_enqueued_frame(const EnqueuedFrame& enq_frame); + void send_packet(const uint8_t* packet, int packet_len); + bool send_video_config(); + std::shared_ptr m_codec_config = nullptr; + std::mutex m_codec_config_mutex; + std::atomic m_enable_encryption = true; +}; + +#endif // WIFIBROADCAST_WBVIDEOSTREAMTX_H diff --git a/wifibroadcast/src/WiFiCard.h b/wifibroadcast/src/WiFiCard.h new file mode 100644 index 00000000..47f63fb4 --- /dev/null +++ b/wifibroadcast/src/WiFiCard.h @@ -0,0 +1,41 @@ +// +// Created by consti10 on 13.09.23. +// + +#ifndef WIFIBROADCAST_WIFICARD_H +#define WIFIBROADCAST_WIFICARD_H + +#include +#include +#include + +namespace wifibroadcast { +// In OpenHD, we have a quite extensive WiFiCard abstraction - +// in wifibroadcast, we are a bit simpler +// (But we require info for quirks) +// RTL8812AU driver requires a quirk regarding rssi +static constexpr auto WIFI_CARD_TYPE_UNKNOWN = 0; +static constexpr auto WIFI_CARD_TYPE_RTL8812AU = 1; +static constexpr auto WIFI_CARD_TYPE_EMULATE_AIR = 2; +static constexpr auto WIFI_CARD_TYPE_EMULATE_GND = 3; +struct WifiCard { + std::string name; + int type; +}; +static std::vector get_wifi_card_names( + const std::vector& cards) { + std::vector ret; + for (const auto& card : cards) { + ret.push_back(card.name); + } + return ret; +} + +static WifiCard create_card_emulate(bool is_air_card) { + return WifiCard{ + is_air_card ? "emu_air" : "emu_gnd", + is_air_card ? WIFI_CARD_TYPE_EMULATE_AIR : WIFI_CARD_TYPE_EMULATE_GND}; +} +} // namespace wifibroadcast + +#endif // WIFIBROADCAST_WIFICARD_H diff --git a/wifibroadcast/src/dummy_link/DummyLink.cpp b/wifibroadcast/src/dummy_link/DummyLink.cpp new file mode 100644 index 00000000..b6bff03b --- /dev/null +++ b/wifibroadcast/src/dummy_link/DummyLink.cpp @@ -0,0 +1,158 @@ +// +// Created by consti10 on 07.01.24. +// + +#include "DummyLink.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "SchedulingHelper.hpp" +#include "SocketHelper.hpp" + +// From +// http://www.atakansarioglu.com/linux-ipc-inter-process-messaging-linux-domain-socket-fifo-pipe-shared-memory-shm-example/ + +static sockaddr_un create_adr(const std::string& name) { + // Unix domain socket file address. + struct sockaddr_un address; + address.sun_family = AF_UNIX; + strcpy(address.sun_path, name.c_str()); + return address; +} + +static int create_socket_read(const std::string& name) { + auto address = create_adr(name); + // Delete the old socket file. + unlink(name.c_str()); + // Create a unix domain socket. + int fd; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + std::cout << "Receiver: Cannot create socket" << std::endl; + return -1; + } + + // Bind the socket to the address. + if (bind(fd, (struct sockaddr*)&address, sizeof(sockaddr_un)) != 0) { + std::cout << "Receiver: Cannot bind socket" << std::endl; + return -1; + } + return fd; +} + +static int create_socket_send() { + // Create a unix domain socket. + int fd; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + std::cout << "Sender: Cannot create socket" << std::endl; + return -1; + } + return fd; +} + +static void send_data(int fd, const std::string& name, const uint8_t* data, + int data_len) { + auto address = create_adr(name); + if (sendto(fd, data, data_len, 0, (struct sockaddr*)&address, + sizeof(sockaddr_un)) != data_len) { + // std::cout << "Client: Cannot send" << std::endl; + } + // std::cout<<"Sent:"<(1000); + m_keep_receiving = true; + m_receive_thread = std::make_unique(&DummyLink::loop_rx, this); +} + +DummyLink::~DummyLink() { + m_keep_receiving = false; + shutdown(m_fd_rx, SHUT_RDWR); + close(m_fd_rx); + m_receive_thread->join(); + m_receive_thread = nullptr; + close(m_fd_tx); +} + +void DummyLink::tx_radiotap(const uint8_t* packet_buff, int packet_size) { + const bool drop = should_drop_packet(); + if (!drop) { + send_data(m_fd_tx, m_fn_tx, packet_buff, packet_size); + } +} + +std::shared_ptr> DummyLink::rx_radiotap() { + std::shared_ptr packet = nullptr; + static constexpr std::int64_t timeout_usecs = 100 * 1000; + auto opt_packet = + m_rx_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); + if (opt_packet.has_value()) { + // dequeued frame + return opt_packet.value()->buff; + } + return nullptr; +} + +void DummyLink::loop_rx() { + SchedulingHelper::set_thread_params_max_realtime("DummyLink::loop_rx"); + auto read_buffer = + std::make_shared>(MAX_MTU_INCLUDING_HEADER); + while (m_keep_receiving) { + // auto packet= read_data(m_fd_rx); + // auto size=recvfrom(fd, buff->data(), buff->size(), MSG_DONTWAIT, NULL, + // NULL); + auto size = + recv(m_fd_rx, read_buffer->data(), read_buffer->size(), MSG_WAITALL); + if (size > 0) { + auto packet = std::make_shared>( + read_buffer->data(), read_buffer->data() + size); + // std::cout<<"Got packet"<size()<(); + item->buff = packet; + const auto success = m_rx_queue->try_enqueue(item); + if (!success) { + // Should never happen + } + } + // std::cout<<"ARGH"< +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../FunkyQueue.h" + +// TODO: Write something that emulates a wb link (tx, rx) +// using linux shm or similar +class DummyLink { + public: + explicit DummyLink(bool is_air); + ~DummyLink(); + void tx_radiotap(const uint8_t* packet_buff, int packet_size); + std::shared_ptr> rx_radiotap(); + void set_drop_mode(int drop_mode); + + private: + const bool m_is_air; + int m_fd_tx; + int m_fd_rx; + std::string m_fn_tx; + std::string m_fn_rx; + std::unique_ptr m_receive_thread; + void loop_rx(); + bool m_keep_receiving = true; + // Drop packets with a probability of 5% + bool should_drop_packet(); + int next_random_number_0_100() { return m_dist100(m_mt); } + std::mt19937 m_mt; + std::uniform_int_distribution<> m_dist100{0, 100}; + struct RxPacket { + std::shared_ptr> buff; + }; + using RxPacketQueueType = FunkyQueue>; + std::unique_ptr m_rx_queue; + std::atomic_int m_drop_mode = 0; +}; + +#endif // OPENHD_DUMMYLINK_H diff --git a/wifibroadcast/encryption/Decryptor.cpp b/wifibroadcast/src/encryption/Decryptor.cpp similarity index 80% rename from wifibroadcast/encryption/Decryptor.cpp rename to wifibroadcast/src/encryption/Decryptor.cpp index 08af10e2..a0224fa7 100644 --- a/wifibroadcast/encryption/Decryptor.cpp +++ b/wifibroadcast/src/encryption/Decryptor.cpp @@ -5,8 +5,8 @@ #include #include -#include "Encryption.h" #include "../wifibroadcast_spdlog.h" +#include "Encryption.h" wb::Decryptor::Decryptor(wb::Key key1) : rx_secretkey(key1.secret_key), tx_publickey(key1.public_key) { @@ -35,19 +35,17 @@ int wb::Decryptor::onNewPacketSessionKeyData( return SESSION_VALID_NOT_NEW; } -bool wb::Decryptor::authenticate( - const uint64_t& nonce, - const uint8_t* encrypted, - int encrypted_size, - uint8_t* dest) { +bool wb::Decryptor::authenticate(const uint64_t& nonce, + const uint8_t* encrypted, int encrypted_size, + uint8_t* dest) { const auto payload_size = encrypted_size - crypto_onetimeauth_BYTES; assert(payload_size > 0); const uint8_t* sign = encrypted + payload_size; // const int // res=crypto_auth_hmacsha256_verify(sign,msg,payload_size,session_key.data()); const auto sub_key = wb::create_onetimeauth_subkey(nonce, session_key); - const int res = crypto_onetimeauth_verify(sign, encrypted, payload_size, - sub_key.data()); + const int res = + crypto_onetimeauth_verify(sign, encrypted, payload_size, sub_key.data()); if (res != -1) { memcpy(dest, encrypted, payload_size); return true; @@ -55,21 +53,12 @@ bool wb::Decryptor::authenticate( return false; } -bool wb::Decryptor::decrypt(const uint64_t& nonce, - const uint8_t* encrypted, - int encrypted_size, - uint8_t* dest) { +bool wb::Decryptor::decrypt(const uint64_t& nonce, const uint8_t* encrypted, + int encrypted_size, uint8_t* dest) { unsigned long long mlen; int res = crypto_aead_chacha20poly1305_decrypt( - dest, - &mlen, - nullptr, - encrypted, - encrypted_size, - nullptr, - 0, - (uint8_t*)(&nonce), - session_key.data()); + dest, &mlen, nullptr, encrypted, encrypted_size, nullptr, 0, + (uint8_t*)(&nonce), session_key.data()); return res != -1; } diff --git a/wifibroadcast/encryption/Decryptor.h b/wifibroadcast/src/encryption/Decryptor.h similarity index 94% rename from wifibroadcast/encryption/Decryptor.h rename to wifibroadcast/src/encryption/Decryptor.h index 43abd539..f4baf38e 100644 --- a/wifibroadcast/encryption/Decryptor.h +++ b/wifibroadcast/src/encryption/Decryptor.h @@ -39,7 +39,7 @@ class Decryptor { * @param dest needs to be at least @param encrypted - 16 bytes big. */ bool decrypt(const uint64_t& nonce, const uint8_t* encrypted, - int encrypted_size, uint8_t* dest); + int encrypted_size, uint8_t* dest); /** * Validate only the given message @@ -48,14 +48,16 @@ class Decryptor { * valid message) * @param dest needs to be at least @param encrypted - 16 bytes big. */ - bool authenticate(const uint64_t& nonce, const uint8_t* encrypted, int encrypted_size, uint8_t* dest); + bool authenticate(const uint64_t& nonce, const uint8_t* encrypted, + int encrypted_size, uint8_t* dest); /** * Easier to use, but usage might require memcpy * For test use */ std::shared_ptr> authenticate_and_decrypt_buff( - const uint64_t& nonce, const uint8_t* encrypted, int encrypted_size, bool isEncrypt); + const uint64_t& nonce, const uint8_t* encrypted, int encrypted_size, + bool isEncrypt); // Set to true as soon as a valid session has been detected bool has_valid_session() const { return m_has_valid_session; } diff --git a/wifibroadcast/src/encryption/Encryption.cpp b/wifibroadcast/src/encryption/Encryption.cpp new file mode 100644 index 00000000..fbd4ee88 --- /dev/null +++ b/wifibroadcast/src/encryption/Encryption.cpp @@ -0,0 +1,73 @@ +// +// Created by consti10 on 13.08.23. +// + +#include "Encryption.h" + +#include + +#include +#include +#include + +#include "../wifibroadcast_spdlog.h" + +wb::KeyPairTxRx wb::generate_keypair_random() { + KeyPairTxRx ret{}; + crypto_box_keypair(ret.key_1.public_key.data(), ret.key_1.secret_key.data()); + crypto_box_keypair(ret.key_2.public_key.data(), ret.key_2.secret_key.data()); + return ret; +} + +// Salts generated once using +// https://www.random.org/cgi-bin/randbyte?nbytes=16&format=d We want +// deterministic seed from a pw, and are only interested in making it impossible +// to reverse the process (even though the salt is plain text) +static constexpr std::array OHD_SALT_AIR{ + 192, 189, 216, 102, 56, 153, 154, 92, 228, 26, 49, 209, 157, 7, 128, 207}; +static constexpr std::array OHD_SALT_GND{ + 179, 30, 150, 20, 17, 200, 225, 82, 48, 64, 18, 130, 89, 62, 83, 234}; + +std::array +wb::create_seed_from_password_openhd_salt(const std::string& pw, + bool use_salt_air) { + const auto salt = use_salt_air ? OHD_SALT_AIR : OHD_SALT_GND; + std::array key{}; + if (crypto_pwhash(key.data(), key.size(), pw.c_str(), pw.length(), + salt.data(), crypto_pwhash_OPSLIMIT_INTERACTIVE, + crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + std::cerr << "ERROR: cannot create_seed_from_password_openhd_salt" + << std::endl; + assert(false); + // out of memory + } + return key; +} + +wb::KeyPairTxRx wb::generate_keypair_from_bind_phrase( + const std::string& bind_phrase) { + const auto seed_air = + create_seed_from_password_openhd_salt(bind_phrase, true); + const auto seed_gnd = + create_seed_from_password_openhd_salt(bind_phrase, false); + KeyPairTxRx ret{}; + crypto_box_seed_keypair(ret.key_1.public_key.data(), + ret.key_1.secret_key.data(), seed_air.data()); + crypto_box_seed_keypair(ret.key_2.public_key.data(), + ret.key_2.secret_key.data(), seed_gnd.data()); + return ret; +} + +std::array +wb::create_onetimeauth_subkey(const uint64_t& nonce, + const std::array& session_key) { + // sub-key for this packet + std::array subkey{}; + // We only have an 8 byte nonce, this should be enough entropy + std::array nonce_buf{0}; + memcpy(nonce_buf.data(), (uint8_t*)&nonce, 8); + crypto_core_hchacha20(subkey.data(), nonce_buf.data(), session_key.data(), + nullptr); + return subkey; +} \ No newline at end of file diff --git a/wifibroadcast/encryption/Encryption.h b/wifibroadcast/src/encryption/Encryption.h similarity index 52% rename from wifibroadcast/encryption/Encryption.h rename to wifibroadcast/src/encryption/Encryption.h index 09d93f9b..97be6e8e 100644 --- a/wifibroadcast/encryption/Encryption.h +++ b/wifibroadcast/src/encryption/Encryption.h @@ -1,28 +1,31 @@ #ifndef ENCRYPTION_HPP #define ENCRYPTION_HPP -#include +#include + #include -#include #include - -#include +#include +#include #include "KeyPair.h" // Namespace that can be used to add encryption+packet validation // (Or packet validation only to save CPU resources) // to a lossy unidirectional link -// Packet validation is quite important, to make sure only openhd packets (and not standard wifi packets) are used in OpenHD -// The Encryption / Decryption name(s) are legacy - -// The more difficult part is dealing with the session key stuff, and this class makes it a bit easier to use +// Packet validation is quite important, to make sure only openhd packets (and +// not standard wifi packets) are used in OpenHD The Encryption / Decryption +// name(s) are legacy - The more difficult part is dealing with the session key +// stuff, and this class makes it a bit easier to use // one time authentication and encryption nicely are really similar -static_assert(crypto_onetimeauth_BYTES==crypto_aead_chacha20poly1305_ABYTES); -// Encryption (or packet validation) adds this many bytes to the end of the message -static constexpr auto ENCRYPTION_ADDITIONAL_VALIDATION_DATA=crypto_aead_chacha20poly1305_ABYTES; +static_assert(crypto_onetimeauth_BYTES == crypto_aead_chacha20poly1305_ABYTES); +// Encryption (or packet validation) adds this many bytes to the end of the +// message +static constexpr auto ENCRYPTION_ADDITIONAL_VALIDATION_DATA = + crypto_aead_chacha20poly1305_ABYTES; -namespace wb{ +namespace wb { /** * Generates a new keypair. Non-deterministic, 100% secure. @@ -34,24 +37,29 @@ KeyPairTxRx generate_keypair_random(); * Deterministic seed from password, but hides password itself (non-reversible) * Uses a pre-defined salt to be deterministic */ -std::array -create_seed_from_password_openhd_salt(const std::string& pw,bool use_salt_air); +std::array create_seed_from_password_openhd_salt( + const std::string& pw, bool use_salt_air); // We always use the same bind phrase by default -static constexpr auto DEFAULT_BIND_PHRASE="openhd"; +static constexpr auto DEFAULT_BIND_PHRASE = "openhd"; /** - * Generates 2 new (deterministic) tx rx keys, using the seed created from the pw. + * Generates 2 new (deterministic) tx rx keys, using the seed created from the + * pw. * @param bind_phrase the password / bind phrase */ -KeyPairTxRx generate_keypair_from_bind_phrase(const std::string& bind_phrase=DEFAULT_BIND_PHRASE); +KeyPairTxRx generate_keypair_from_bind_phrase( + const std::string& bind_phrase = DEFAULT_BIND_PHRASE); /** * https://libsodium.gitbook.io/doc/key_derivation - * UINT16SeqNrHelper since we both support encryption and one time validation to save cpu performance + * UINT16SeqNrHelper since we both support encryption and one time validation to + * save cpu performance */ -std::array create_onetimeauth_subkey(const uint64_t& nonce,const std::array& session_key); - -} // namespace wb end +std::array create_onetimeauth_subkey( + const uint64_t& nonce, + const std::array& + session_key); +} // namespace wb -#endif //ENCRYPTION_HPP \ No newline at end of file +#endif // ENCRYPTION_HPP \ No newline at end of file diff --git a/wifibroadcast/encryption/EncryptionFsUtils.cpp b/wifibroadcast/src/encryption/EncryptionFsUtils.cpp similarity index 51% rename from wifibroadcast/encryption/EncryptionFsUtils.cpp rename to wifibroadcast/src/encryption/EncryptionFsUtils.cpp index 466182d7..8135336c 100644 --- a/wifibroadcast/encryption/EncryptionFsUtils.cpp +++ b/wifibroadcast/src/encryption/EncryptionFsUtils.cpp @@ -5,25 +5,25 @@ #include "EncryptionFsUtils.h" #include +#include #include #include #include -#include #include "../wifibroadcast_spdlog.h" bool wb::write_keypair_to_file(const wb::KeyPairTxRx& keypair_txrx, const std::string& filename) { - FILE *fp; + FILE* fp; if ((fp = fopen(filename.c_str(), "w")) == nullptr) { - std::cerr<<"Unable to save "< wb::read_keypair_from_file(const std::string& filename) { +std::optional wb::read_keypair_from_file( + const std::string& filename) { KeyPairTxRx ret{}; - FILE *fp; + FILE* fp; if ((fp = fopen(filename.c_str(), "r")) == nullptr) { - std::cerr< raw{}; - auto res= fread(raw.data(),raw.size(),1,fp); - if(res!=1){ - std::cerr<<"Cannot read keypair file"< raw{}; + auto res = fread(raw.data(), raw.size(), 1, fp); + if (res != 1) { + std::cerr << "Cannot read keypair file" << std::endl; fclose(fp); return std::nullopt; } diff --git a/wifibroadcast/encryption/EncryptionFsUtils.h b/wifibroadcast/src/encryption/EncryptionFsUtils.h similarity index 73% rename from wifibroadcast/encryption/EncryptionFsUtils.h rename to wifibroadcast/src/encryption/EncryptionFsUtils.h index db0c03cd..48408873 100644 --- a/wifibroadcast/encryption/EncryptionFsUtils.h +++ b/wifibroadcast/src/encryption/EncryptionFsUtils.h @@ -11,13 +11,14 @@ namespace wb { /** * Saves the KeyPairTxRx as a raw file */ -bool write_keypair_to_file(const KeyPairTxRx& keypair_txrx,const std::string& filename); +bool write_keypair_to_file(const KeyPairTxRx& keypair_txrx, + const std::string& filename); /** * Reads a raw KeyPairTxRx from a raw file previusly generated. */ std::optional read_keypair_from_file(const std::string& filename); -} // namespace wb end +} // namespace wb #endif // ENCRYPTION_FS_UTILS_HPP \ No newline at end of file diff --git a/wifibroadcast/encryption/Encryptor.cpp b/wifibroadcast/src/encryption/Encryptor.cpp similarity index 100% rename from wifibroadcast/encryption/Encryptor.cpp rename to wifibroadcast/src/encryption/Encryptor.cpp diff --git a/wifibroadcast/encryption/Encryptor.h b/wifibroadcast/src/encryption/Encryptor.h similarity index 100% rename from wifibroadcast/encryption/Encryptor.h rename to wifibroadcast/src/encryption/Encryptor.h diff --git a/wifibroadcast/encryption/KeyPair.cpp b/wifibroadcast/src/encryption/KeyPair.cpp similarity index 77% rename from wifibroadcast/encryption/KeyPair.cpp rename to wifibroadcast/src/encryption/KeyPair.cpp index 0602c99d..396a39f3 100644 --- a/wifibroadcast/encryption/KeyPair.cpp +++ b/wifibroadcast/src/encryption/KeyPair.cpp @@ -2,18 +2,19 @@ // Created by consti10 on 05.01.24. // #include "KeyPair.h" + #include std::array wb::KeyPairTxRx::as_raw( const KeyPairTxRx& keypair) { std::array ret{}; - std::memcpy(ret.data(), &keypair,KEYPAIR_RAW_SIZE); + std::memcpy(ret.data(), &keypair, KEYPAIR_RAW_SIZE); return ret; } wb::KeyPairTxRx wb::KeyPairTxRx::from_raw( const std::array& raw) { wb::KeyPairTxRx ret{}; - std::memcpy(&ret,raw.data(),KEYPAIR_RAW_SIZE); + std::memcpy(&ret, raw.data(), KEYPAIR_RAW_SIZE); return ret; } diff --git a/wifibroadcast/src/encryption/KeyPair.h b/wifibroadcast/src/encryption/KeyPair.h new file mode 100644 index 00000000..77abda7d --- /dev/null +++ b/wifibroadcast/src/encryption/KeyPair.h @@ -0,0 +1,45 @@ +#ifndef KEY_HPP +#define KEY_HPP +#include + +#include +#include + +namespace wb { +// A wb key consists of a public and secret key +struct Key { + std::array public_key; + std::array secret_key; + int operator==(const Key& other) const { + return std::equal(std::begin(public_key), std::end(public_key), + std::begin(other.public_key)) && + std::equal(std::begin(secret_key), std::end(secret_key), + std::begin(other.secret_key)); + } +} __attribute__((packed)); +; +static_assert(sizeof(Key) == + crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES); + +// A wb keypair are 2 keys, one for transmitting, one for receiving +// (Since both ground and air unit talk bidirectional) +// We use a different key for the down-link / uplink, respective +static constexpr const int KEYPAIR_RAW_SIZE = 32 * 4; +struct KeyPairTxRx { + Key key_1; + Key key_2; + Key get_tx_key(bool is_air) { return is_air ? key_1 : key_2; } + Key get_rx_key(bool is_air) { return is_air ? key_2 : key_1; } + int operator==(const KeyPairTxRx& other) const { + return key_1 == other.key_1 && key_2 == other.key_2; + } + static std::array as_raw( + const KeyPairTxRx& keypair); + static KeyPairTxRx from_raw(const std::array& raw); +} __attribute__((packed)); +static_assert(sizeof(KeyPairTxRx) == 2 * sizeof(Key)); +static_assert(sizeof(KeyPairTxRx) == KEYPAIR_RAW_SIZE); + +} // namespace wb + +#endif // KEY_HPP diff --git a/wifibroadcast/fec/FEC.cpp b/wifibroadcast/src/fec/FEC.cpp similarity index 100% rename from wifibroadcast/fec/FEC.cpp rename to wifibroadcast/src/fec/FEC.cpp diff --git a/wifibroadcast/fec/FEC.h b/wifibroadcast/src/fec/FEC.h similarity index 98% rename from wifibroadcast/fec/FEC.h rename to wifibroadcast/src/fec/FEC.h index 91aca136..3c46e9e1 100644 --- a/wifibroadcast/fec/FEC.h +++ b/wifibroadcast/src/fec/FEC.h @@ -9,9 +9,9 @@ #include #include -#include "../HelperSources/Helper.hpp" -#include "../external/fec/fec_base.h" +#include "../../external/fec/fec_base.h" #include "FECConstants.hpp" +#include "Helper.hpp" // c++ wrapper around fec library // NOTE: When working with FEC, people seem to use the terms block, fragments diff --git a/wifibroadcast/fec/FECConstants.hpp b/wifibroadcast/src/fec/FECConstants.hpp similarity index 100% rename from wifibroadcast/fec/FECConstants.hpp rename to wifibroadcast/src/fec/FECConstants.hpp diff --git a/wifibroadcast/fec/FECDecoder.cpp b/wifibroadcast/src/fec/FECDecoder.cpp similarity index 100% rename from wifibroadcast/fec/FECDecoder.cpp rename to wifibroadcast/src/fec/FECDecoder.cpp diff --git a/wifibroadcast/fec/FECDecoder.h b/wifibroadcast/src/fec/FECDecoder.h similarity index 100% rename from wifibroadcast/fec/FECDecoder.h rename to wifibroadcast/src/fec/FECDecoder.h index 59b4f540..b9eb455d 100644 --- a/wifibroadcast/fec/FECDecoder.h +++ b/wifibroadcast/src/fec/FECDecoder.h @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include "FECConstants.hpp" #include "RxBlock.h" diff --git a/wifibroadcast/fec/FECEncoder.cpp b/wifibroadcast/src/fec/FECEncoder.cpp similarity index 99% rename from wifibroadcast/fec/FECEncoder.cpp rename to wifibroadcast/src/fec/FECEncoder.cpp index 44b2d5ac..6529cd34 100644 --- a/wifibroadcast/fec/FECEncoder.cpp +++ b/wifibroadcast/src/fec/FECEncoder.cpp @@ -5,7 +5,7 @@ #include #include -#include "../external/fec/fec_base.h" +#include "../../external/fec/fec_base.h" #include "BlockSizeHelper.hpp" #include "FECConstants.hpp" diff --git a/wifibroadcast/fec/FECEncoder.h b/wifibroadcast/src/fec/FECEncoder.h similarity index 100% rename from wifibroadcast/fec/FECEncoder.h rename to wifibroadcast/src/fec/FECEncoder.h diff --git a/wifibroadcast/fec/FECPayloadHdr.hpp b/wifibroadcast/src/fec/FECPayloadHdr.hpp similarity index 100% rename from wifibroadcast/fec/FECPayloadHdr.hpp rename to wifibroadcast/src/fec/FECPayloadHdr.hpp diff --git a/wifibroadcast/fec/RxBlock.cpp b/wifibroadcast/src/fec/RxBlock.cpp similarity index 100% rename from wifibroadcast/fec/RxBlock.cpp rename to wifibroadcast/src/fec/RxBlock.cpp diff --git a/wifibroadcast/fec/RxBlock.h b/wifibroadcast/src/fec/RxBlock.h similarity index 100% rename from wifibroadcast/fec/RxBlock.h rename to wifibroadcast/src/fec/RxBlock.h diff --git a/wifibroadcast/legacy/README.md b/wifibroadcast/src/legacy/README.md similarity index 100% rename from wifibroadcast/legacy/README.md rename to wifibroadcast/src/legacy/README.md diff --git a/wifibroadcast/legacy/WBStreamRxUDP.h b/wifibroadcast/src/legacy/WBStreamRxUDP.h similarity index 100% rename from wifibroadcast/legacy/WBStreamRxUDP.h rename to wifibroadcast/src/legacy/WBStreamRxUDP.h diff --git a/wifibroadcast/legacy/WBStreamTxUDP.h b/wifibroadcast/src/legacy/WBStreamTxUDP.h similarity index 100% rename from wifibroadcast/legacy/WBStreamTxUDP.h rename to wifibroadcast/src/legacy/WBStreamTxUDP.h diff --git a/wifibroadcast/src/pcap_helper.hpp b/wifibroadcast/src/pcap_helper.hpp new file mode 100644 index 00000000..fc95d052 --- /dev/null +++ b/wifibroadcast/src/pcap_helper.hpp @@ -0,0 +1,118 @@ +// +// Created by consti10 on 17.12.22. +// + +#ifndef WIFIBROADCAST_SRC_PCAP_HELPER_H_ +#define WIFIBROADCAST_SRC_PCAP_HELPER_H_ + +#include + +#include + +namespace wifibroadcast::pcap_helper { + +// debugging +static std::string tstamp_types_to_string(int *ts_types, int n) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < n; i++) { + const char *name = pcap_tstamp_type_val_to_name(ts_types[i]); + const char *description = pcap_tstamp_type_val_to_description(ts_types[i]); + ss << name << "=" << description << ","; + } + ss << "]"; + return ss.str(); +} + +// Set timestamp type to PCAP_TSTAMP_HOST if available +static void iteratePcapTimestamps(pcap_t *ppcap) { + int *availableTimestamps; + const int nTypes = pcap_list_tstamp_types(ppcap, &availableTimestamps); + wifibroadcast::log::get_default()->debug( + "TS types:{}", wifibroadcast::pcap_helper::tstamp_types_to_string( + availableTimestamps, nTypes)); + //"N available timestamp types "<debug("Setting timestamp to host"); + pcap_set_tstamp_type(ppcap, PCAP_TSTAMP_HOST); + } + } + pcap_free_tstamp_types(availableTimestamps); +} + +// creates a pcap handle for the given wlan and sets common params for wb +// returns nullptr on failure, a valid pcap handle otherwise +static pcap_t *open_pcap_rx(const std::string &wlan) { + pcap_t *ppcap = nullptr; + char errbuf[PCAP_ERRBUF_SIZE]; + ppcap = pcap_create(wlan.c_str(), errbuf); + if (ppcap == nullptr) { + wifibroadcast::log::get_default()->error( + "Unable to open interface {} in pcap: {}", wlan.c_str(), errbuf); + return nullptr; + } + iteratePcapTimestamps(ppcap); + if (pcap_set_snaplen(ppcap, 4096) != 0) + wifibroadcast::log::get_default()->error("set_snaplen failed"); + if (pcap_set_promisc(ppcap, 1) != 0) + wifibroadcast::log::get_default()->error("set_promisc failed"); + // if (pcap_set_rfmon(ppcap, 1) !=0) + // wifibroadcast::log::get_default()->error("set_rfmon failed"); + if (pcap_set_timeout(ppcap, -1) != 0) + wifibroadcast::log::get_default()->error("set_timeout failed"); + // if (pcap_set_buffer_size(ppcap, 2048) !=0) + // wifibroadcast::log::get_default()->error("set_buffer_size failed"); + // Important: Without enabling this mode pcap buffers quite a lot of packets + // starting with version 1.5.0 ! + // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html + if (pcap_set_immediate_mode(ppcap, true) != 0) { + wifibroadcast::log::get_default()->warn( + "pcap_set_immediate_mode failed: {}", pcap_geterr(ppcap)); + } + if (pcap_activate(ppcap) != 0) { + wifibroadcast::log::get_default()->error("pcap_activate failed: {}", + pcap_geterr(ppcap)); + } + if (pcap_setnonblock(ppcap, 1, errbuf) != 0) { + wifibroadcast::log::get_default()->error("set_nonblock failed: {}", errbuf); + } + return ppcap; +} + +// copy paste from svpcom +static pcap_t *open_pcap_tx(const std::string &wlan) { + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *p = pcap_create(wlan.c_str(), errbuf); + if (p == nullptr) { + wifibroadcast::log::get_default()->error( + "Unable to open interface {} in pcap: {}", wlan.c_str(), errbuf); + } + if (pcap_set_snaplen(p, 4096) != 0) + wifibroadcast::log::get_default()->warn("set_snaplen failed"); + if (pcap_set_promisc(p, 1) != 0) + wifibroadcast::log::get_default()->warn("set_promisc failed"); + // if (pcap_set_rfmon(p, 1) !=0) + // wifibroadcast::log::get_default()->warn("set_rfmon failed"; + // Used to be -1 at some point, which is undefined behaviour. -1 can cause + // issues on older kernels, according to @Pete + const int timeout_ms = 10; + if (pcap_set_timeout(p, timeout_ms) != 0) + wifibroadcast::log::get_default()->warn("set_timeout {} failed", + timeout_ms); + // if (pcap_set_buffer_size(p, 2048) !=0) + // wifibroadcast::log::get_default()->warn("set_buffer_size failed"; + // NOTE: Immediate not needed on TX + if (pcap_activate(p) != 0) { + wifibroadcast::log::get_default()->error("pcap_activate failed: {}", + pcap_geterr(p)); + } + // if (pcap_setnonblock(p, 1, errbuf) != 0) + // wifibroadcast::log::get_default()->warn(string_format("set_nonblock failed: + // %s", errbuf)); + return p; +} + +} // namespace wifibroadcast::pcap_helper + +#endif // WIFIBROADCAST_SRC_PCAP_HELPER_H_ diff --git a/wifibroadcast/radiotap/README.md b/wifibroadcast/src/radiotap/README.md similarity index 100% rename from wifibroadcast/radiotap/README.md rename to wifibroadcast/src/radiotap/README.md diff --git a/wifibroadcast/src/radiotap/RSSIAccumulator.hpp b/wifibroadcast/src/radiotap/RSSIAccumulator.hpp new file mode 100644 index 00000000..1642af88 --- /dev/null +++ b/wifibroadcast/src/radiotap/RSSIAccumulator.hpp @@ -0,0 +1,101 @@ +// +// Created by consti10 on 30.06.23. +// + +#ifndef WIFIBROADCAST_RSSIACCUMULATOR_HPP +#define WIFIBROADCAST_RSSIACCUMULATOR_HPP + +#include + +#include "../wifibroadcast_spdlog.h" +#include "TimeHelper.hpp" +#include "spdlog/spdlog.h" + +/** + * UINT16SeqNrHelper to accumulate RSSI values + */ +class RSSIAccumulator { + public: + void add_rssi(int8_t rssi) { + if (rssi <= INT8_MIN || rssi >= 0) { + // RSSI should always be negative and in range [-127,-1] + // It seems to be quite common for drivers to report invalid rssi values + // from time to time - in this case, just ignore the value + if (m_debug_invalid_rssi) { + wifibroadcast::log::get_default()->debug("Invalid rssi on id {}, {}", + m_rssi_identifier, rssi); + } + return; + } + if (rssi > m_rssi_max) { + m_rssi_max = rssi; + } + if (rssi < m_rssi_min) { + m_rssi_min = rssi; + } + m_rssi_sum += static_cast(rssi); + m_rssi_count++; + } + int8_t get_avg() const { + const auto count = m_rssi_count; + if (count <= 0) return INT8_MIN; + const auto avg = m_rssi_sum / m_rssi_count; + return static_cast(avg); + } + int8_t get_min() const { + if (m_rssi_count <= 0) return INT8_MIN; + return m_rssi_min; + } + int8_t get_max() const { + if (m_rssi_count <= 0) return INT8_MIN; + return m_rssi_max; + } + MinMaxAvg get_min_max_avg() { + MinMaxAvg tmp{get_min(), get_max(), get_avg()}; + return tmp; + } + static std::string min_max_avg_to_string(const MinMaxAvg& data, + bool avg_only = false) { + // Need to convert to int such that it is shown correctly + MinMaxAvg tmp{data.min, data.max, data.avg}; + return min_max_avg_as_string(tmp, avg_only); + } + int get_n_samples() { return m_rssi_count; } + std::optional> add_and_recalculate_if_needed(int8_t rssi) { + add_rssi(rssi); + // Calculate every 20 packets or 500ms and at least one packet, whatever is + // reached first + const auto elapsed = + std::chrono::steady_clock::now() - m_last_recalculation; + if (get_n_samples() >= 20 || + (get_n_samples() >= 1 && elapsed >= std::chrono::milliseconds(500))) { + auto tmp = get_min_max_avg(); + reset(); + m_last_recalculation = std::chrono::steady_clock::now(); + return tmp; + } + return std::nullopt; + } + void reset() { + m_rssi_sum = 0; + m_rssi_count = 0; + m_rssi_min = INT8_MAX; + m_rssi_max = INT8_MIN; + } + void set_debug_invalid_rssi(bool enable, int rssi_identifier) { + m_debug_invalid_rssi = enable; + m_rssi_identifier = rssi_identifier; + } + + private: + int m_rssi_sum = 0; + int m_rssi_count = 0; + int8_t m_rssi_min = INT8_MAX; + int8_t m_rssi_max = INT8_MIN; + std::chrono::steady_clock::time_point m_last_recalculation = + std::chrono::steady_clock::now(); + bool m_debug_invalid_rssi = false; + int m_rssi_identifier = 0; +}; + +#endif // WIFIBROADCAST_RSSIACCUMULATOR_HPP diff --git a/wifibroadcast/src/radiotap/RadiotapHeaderRx.hpp b/wifibroadcast/src/radiotap/RadiotapHeaderRx.hpp new file mode 100644 index 00000000..f42f85f5 --- /dev/null +++ b/wifibroadcast/src/radiotap/RadiotapHeaderRx.hpp @@ -0,0 +1,221 @@ +// +// Created by consti10 on 05.10.23. +// + +#ifndef WIFIBROADCAST_RADIOTAPHEADERPARSER_H +#define WIFIBROADCAST_RADIOTAPHEADERPARSER_H + +#include + +#include "../Ieee80211Header.hpp" +#include "RadiotapHeaderTx.hpp" + +// Code for parsing the radiotap header coming from rtl8812au / rtl8812bu / +// other monitor mode wifi drivers + +namespace radiotap::rx { + +static constexpr int8_t DBM_INVALID = -128; // INT8_MIN +static constexpr uint16_t QUALITY_INVALID = 65535; // UINT16_MAX; + +// NOTE: We use std::nullopt to indicate that the card doesn't report this value +struct KeyRfIndicators { + // https://www.radiotap.org/fields/Antenna%20signal.html + // IEEE80211_RADIOTAP_DBM_ANTSIGNAL + std::optional radiotap_dbm_antsignal = std::nullopt; + // IEEE80211_RADIOTAP_DBM_ANTNOISE + std::optional radiotap_dbm_antnoise = std::nullopt; + // IEEE80211_RADIOTAP_LOCK_QUALITY + std::optional radiotap_lock_quality = std::nullopt; +}; + +struct ParsedRxRadiotapPacket { + const Ieee80211HeaderRaw *ieee80211Header; + const uint8_t *payload; + const std::size_t payloadSize; + // --- Values generic (not per rf-path) --- + bool radiotap_f_bad_fcs = false; + KeyRfIndicators rf_adapter; + // --- Values per rf-path ----- + // first one: antenna 1 (if reported by card), second one: antenna 2 (if + // reported by card) ... + std::vector rf_paths; +}; + +// Returns std::nullopt if radiotap was unable to parse the header +// else return the *parsed information* +// This method is intentionally simple in that it only looks for data relevant +// to us (right now) inside the radiotap header. +static std::optional process_received_radiotap_packet( + const uint8_t *pkt, const int pkt_len) { + // int pktlen = hdr.caplen; + int pktlen = pkt_len; + // + // Copy the value of this flag once present and process it after the loop is + // done + uint8_t tmp_copy_IEEE80211_RADIOTAP_FLAGS = 0; + struct ieee80211_radiotap_iterator iterator {}; + // With AR9271 I get 39 as length of the radio-tap header + // With my internal laptop wifi chip I get 36 as length of the radio-tap + // header. + int ret = ieee80211_radiotap_iterator_init( + &iterator, (ieee80211_radiotap_header *)pkt, pktlen, nullptr); + if (ret) { + printf("malformed radiotap header (init returns %d)\n", ret); + return std::nullopt; + } + + // This is the best way I came up with of how to seperate the per-adaper and + // per-rf-path rf metrics from one of another It assumes the driver reports + // them in order - per-adapter first, then per-rf-paths (for as many rf paths + // there are) AND the driver reports signal (noise,lock) if given for the + // adapter and rf paths(s) alltogether Seems to be the case for all drivers, + // definitely for the openhd supported ones. + std::vector radiotap_dbm_antsignal; + radiotap_dbm_antsignal.reserve(5); + std::vector radiotap_dbm_antnoise; + radiotap_dbm_antnoise.reserve(5); + std::vector radiotap_lock_quality; + radiotap_lock_quality.reserve(5); + + int8_t n_antennas = 0; + while (ret == 0) { + ret = ieee80211_radiotap_iterator_next(&iterator); + if (ret) { + continue; + } + /* see if this argument is something we can use */ + switch (iterator.this_arg_index) { + case IEEE80211_RADIOTAP_ANTENNA: { + const auto antenna_idx = (int8_t)iterator.this_arg[0]; + const int8_t antenna_nr = antenna_idx + 1; + if (antenna_nr > n_antennas) n_antennas = antenna_nr; + } break; + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: { + int8_t value; + std::memcpy(&value, iterator.this_arg, 1); + radiotap_dbm_antsignal.push_back(value); + } break; + case IEEE80211_RADIOTAP_DBM_ANTNOISE: { + int8_t value; + std::memcpy(&value, iterator.this_arg, 1); + radiotap_dbm_antnoise.push_back(value); + } break; + case IEEE80211_RADIOTAP_FLAGS: + tmp_copy_IEEE80211_RADIOTAP_FLAGS = *(uint8_t *)(iterator.this_arg); + break; + case IEEE80211_RADIOTAP_LOCK_QUALITY: { + uint16_t value = 0; + // NOTE: Here we only copy 8 bits - the value is reported in radiotap as + // uint16_t type, but only in the 0..100 range, so uint8_t would be + // sufficient (and works) + std::memcpy(&value, iterator.this_arg, 1); + radiotap_lock_quality.push_back(value); + } break; + default: + break; + } + } /* while more rt headers */ + if (ret != -ENOENT) { + printf("Cannot parse radiotap header %d\n", ret); + return std::nullopt; + } + bool has_radiotap_f_bad_fcs = false; + if (tmp_copy_IEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_BADFCS) { + // wifibroadcast::log::get_default()->warn("Got packet with bad fsc\n"; + has_radiotap_f_bad_fcs = true; + } + // the fcs is at the end of the packet + if (tmp_copy_IEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_FCS) { + //<<"Packet has IEEE80211_RADIOTAP_F_FCS"; + pktlen -= 4; + } + // assert(iterator._max_length==hdr.caplen); + /* discard the radiotap header part */ + pkt += iterator._max_length; + pktlen -= iterator._max_length; + KeyRfIndicators adapter; + // First fill in the adapter + if (!radiotap_dbm_antsignal.empty()) { + adapter.radiotap_dbm_antsignal = radiotap_dbm_antsignal[0]; + } + if (!radiotap_dbm_antnoise.empty()) { + adapter.radiotap_dbm_antnoise = radiotap_dbm_antnoise[0]; + } + if (!radiotap_lock_quality.empty()) { + adapter.radiotap_lock_quality = radiotap_lock_quality[0]; + } + // Then per rf-path + std::vector rf_paths; + rf_paths.resize(n_antennas); + for (int i = 0; i < n_antennas; i++) { + const int idx = i + 1; + if (radiotap_dbm_antsignal.size() > idx) { + rf_paths[i].radiotap_dbm_antsignal = radiotap_dbm_antsignal[idx]; + } + if (radiotap_dbm_antnoise.size() > idx) { + rf_paths[i].radiotap_dbm_antnoise = radiotap_dbm_antnoise[idx]; + } + if (radiotap_lock_quality.size() > idx) { + rf_paths[i].radiotap_lock_quality = radiotap_lock_quality[idx]; + } + } + const Ieee80211HeaderRaw *ieee80211Header = (Ieee80211HeaderRaw *)pkt; + const uint8_t *payload = pkt + Ieee80211HeaderRaw::SIZE_BYTES; + const std::size_t payloadSize = + (std::size_t)pktlen - Ieee80211HeaderRaw::SIZE_BYTES; + return ParsedRxRadiotapPacket{ieee80211Header, payload, payloadSize, + has_radiotap_f_bad_fcs, adapter, rf_paths}; +} + +static std::string key_rf_indicators_to_string( + const KeyRfIndicators &indicators) { + std::stringstream ss; + ss << "{"; + if (indicators.radiotap_dbm_antsignal.has_value()) { + ss << (int)indicators.radiotap_dbm_antsignal.value() << " dBm : "; + } else { + ss << "N/A dBm : "; + } + if (indicators.radiotap_dbm_antnoise.has_value()) { + ss << (int)indicators.radiotap_dbm_antnoise.value() << " dBm : "; + } else { + ss << "N/A dBm : "; + } + if (indicators.radiotap_lock_quality.has_value()) { + ss << (int)indicators.radiotap_lock_quality.value() << " %"; + } else { + ss << "N/A %"; + } + ss << "}"; + return ss.str(); +} + +static std::string all_rf_path_to_string( + const std::vector &all_rf_path) { + std::stringstream ss; + ss << "RF Paths:"; + if (all_rf_path.empty()) { + ss << "[Empty]"; + return ss.str(); + } + int idx = 0; + for (const auto &rf_path : all_rf_path) { + ss << key_rf_indicators_to_string(rf_path); + idx++; + } + return ss.str(); +} + +static std::string parsed_radiotap_to_string( + const ParsedRxRadiotapPacket &parsed) { + std::stringstream ss; + ss << "{signal:noise:lock}\n"; + ss << "Adapter:" << key_rf_indicators_to_string(parsed.rf_adapter) << "\n"; + ss << all_rf_path_to_string(parsed.rf_paths); + return ss.str(); +} + +} // namespace radiotap::rx + +#endif // WIFIBROADCAST_RADIOTAPHEADERPARSER_H diff --git a/wifibroadcast/radiotap/RadiotapHeaderTx.hpp b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp similarity index 52% rename from wifibroadcast/radiotap/RadiotapHeaderTx.hpp rename to wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp index a9a4644a..8e49bf1f 100644 --- a/wifibroadcast/radiotap/RadiotapHeaderTx.hpp +++ b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp @@ -3,8 +3,8 @@ #include "Helper.hpp" extern "C" { -#include "../external/radiotap/radiotap.h" -#include "../external/radiotap/radiotap_iter.h" +#include "../../external/radiotap/radiotap.h" +#include "../../external/radiotap/radiotap_iter.h" }; #include @@ -20,21 +20,24 @@ extern "C" { #include #include +#include "../Ieee80211Header.hpp" #include "../wifibroadcast_spdlog.h" // everything must be in little endian byte order http://www.radiotap.org/ -static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, "This code is written for little endian only !"); +static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, + "This code is written for little endian only !"); namespace radiotap::tx { -static constexpr auto MCS_MAX=31; -static constexpr auto MCS_MIN=0; +static constexpr auto MCS_MAX = 31; +static constexpr auto MCS_MIN = 0; // https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit // http://www.radiotap.org/ -static uint32_t writePresenceBitfield(const std::vector &valuesToBePresent) { +static uint32_t writePresenceBitfield( + const std::vector &valuesToBePresent) { uint32_t present = 0; - for (const auto &valueToBePresent: valuesToBePresent) { + for (const auto &valueToBePresent : valuesToBePresent) { present |= 1 << valueToBePresent; } return present; @@ -45,8 +48,8 @@ struct MCS { uint8_t known = 0; uint8_t flags = 0; uint8_t modulationIndex = 0; -}__attribute__ ((packed)); -} +} __attribute__((packed)); +} // namespace radiotap::tx // To inject packets we need 2 radiotap fields: "TX flags" and the "MCS field" struct RadiotapHeaderWithTxFlagsAndMCS { @@ -57,22 +60,22 @@ struct RadiotapHeaderWithTxFlagsAndMCS { uint32_t presence = 0; // http://www.radiotap.org/fields/TX%20flags.html uint16_t txFlags = 0; - //http://www.radiotap.org/fields/MCS.html - // mcs is more than just the mcs index. Be carefully ! + // http://www.radiotap.org/fields/MCS.html + // mcs is more than just the mcs index. Be carefully ! radiotap::tx::MCS mcs{}; -}__attribute__ ((packed)); +} __attribute__((packed)); static_assert(sizeof(RadiotapHeaderWithTxFlagsAndMCS) == 13); -// To inject packets we need a proper radiotap header. The fields of importance for use are: -// 1) "TX flags" -// 2) "MCS field" -// This class holds the bytes for a proper radiotap header after constructing it with the user-selectable Params +// To inject packets we need a proper radiotap header. The fields of importance +// for use are: 1) "TX flags" 2) "MCS field" This class holds the bytes for a +// proper radiotap header after constructing it with the user-selectable Params class RadiotapHeaderTx { public: static constexpr auto SIZE_BYTES = 13; // these are the params in use by OpenHD right now struct UserSelectableParams { - // 20 or 40 mhz channel width. I do not recommend using 40mhz channel width even though it might provide higher throughput. + // 20 or 40 mhz channel width. I do not recommend using 40mhz channel width + // even though it might provide higher throughput. int bandwidth = 20; // I do not recommend using a short guard interval bool short_gi = false; @@ -84,46 +87,56 @@ class RadiotapHeaderTx { // https://mcsindex.com/ int mcs_index = 3; // depends on the driver - bool set_flag_tx_no_ack= false; + bool set_flag_tx_no_ack = false; }; // Make sure that this is the only constructor explicit RadiotapHeaderTx(const UserSelectableParams ¶ms) { - if (params.mcs_index < radiotap::tx::MCS_MIN || params.mcs_index > radiotap::tx::MCS_MAX) { - throw std::runtime_error(fmt::format("Unsupported MCS index {}", params.mcs_index)); + if (params.mcs_index < radiotap::tx::MCS_MIN || + params.mcs_index > radiotap::tx::MCS_MAX) { + throw std::runtime_error( + fmt::format("Unsupported MCS index {}", params.mcs_index)); } if (!(params.bandwidth == 20 || params.bandwidth == 40)) { - throw std::runtime_error(fmt::format("Unsupported bandwidth: {}", params.bandwidth)); + throw std::runtime_error( + fmt::format("Unsupported bandwidth: {}", params.bandwidth)); } - if (!(params.stbc == 0 || params.stbc == 1 || params.stbc == 2 || params.stbc == 3)) { - throw std::runtime_error(fmt::format("Unsupported STBC: {}", params.stbc)); + if (!(params.stbc == 0 || params.stbc == 1 || params.stbc == 2 || + params.stbc == 3)) { + throw std::runtime_error( + fmt::format("Unsupported STBC: {}", params.stbc)); } // size is fixed here radiotapHeaderData.length = SIZE_BYTES; // we use 2 radiotap fields, tx flags and mcs field - radiotapHeaderData.presence = - radiotap::tx::writePresenceBitfield({IEEE80211_RADIOTAP_TX_FLAGS, IEEE80211_RADIOTAP_MCS}); + radiotapHeaderData.presence = radiotap::tx::writePresenceBitfield( + {IEEE80211_RADIOTAP_TX_FLAGS, IEEE80211_RADIOTAP_MCS}); - // in wifibroadcast we never want ack from the receiver - well, this is true, - // but rtl8812au driver actually uses this one a bit differently - if(params.set_flag_tx_no_ack){ + // in wifibroadcast we never want ack from the receiver - well, this is + // true, but rtl8812au driver actually uses this one a bit differently + if (params.set_flag_tx_no_ack) { radiotapHeaderData.txFlags = - IEEE80211_RADIOTAP_F_TX_NOACK; //| IEEE80211_RADIOTAP_F_TX_CTS | IEEE80211_RADIOTAP_F_TX_RTS - }else{ + IEEE80211_RADIOTAP_F_TX_NOACK; //| IEEE80211_RADIOTAP_F_TX_CTS | + //IEEE80211_RADIOTAP_F_TX_RTS + } else { radiotapHeaderData.txFlags = 0; } // now onto the "MCS field" radiotapHeaderData.mcs.known = - (IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_BW | IEEE80211_RADIOTAP_MCS_HAVE_GI - | IEEE80211_RADIOTAP_MCS_HAVE_STBC | IEEE80211_RADIOTAP_MCS_HAVE_FEC); + (IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_BW | + IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_STBC | + IEEE80211_RADIOTAP_MCS_HAVE_FEC); // write the mcs index radiotapHeaderData.mcs.modulationIndex = params.mcs_index; switch (params.bandwidth) { - case 20:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_20; + case 20: + radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_20; break; - case 40:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_40; + case 40: + radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_40; break; - default:assert(true); + default: + assert(true); } if (params.short_gi) { @@ -135,39 +148,53 @@ class RadiotapHeaderTx { } switch (params.stbc) { - case 0:break; - case 1:radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_1 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); + case 0: + break; + case 1: + radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_1 + << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); break; - case 2:radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_2 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); + case 2: + radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_2 + << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); break; - case 3:radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_3 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); + case 3: + radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_3 + << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); break; - default:assert(true); + default: + assert(true); } }; const uint8_t *getData() const { - return (const uint8_t *) &radiotapHeaderData; - } - constexpr std::size_t getSize() const { - return SIZE_BYTES; + return (const uint8_t *)&radiotapHeaderData; } - static std::string user_params_to_string(const UserSelectableParams& params){ - return fmt::format("BW:{} MCS:{} SGI:{} STBC:{} LDPC:{} NO_ACK:{}",params.bandwidth,params.mcs_index,params.short_gi,params.stbc,params.ldpc,params.set_flag_tx_no_ack); + constexpr std::size_t getSize() const { return SIZE_BYTES; } + static std::string user_params_to_string(const UserSelectableParams ¶ms) { + return fmt::format("BW:{} MCS:{} SGI:{} STBC:{} LDPC:{} NO_ACK:{}", + params.bandwidth, params.mcs_index, params.short_gi, + params.stbc, params.ldpc, params.set_flag_tx_no_ack); } + private: RadiotapHeaderWithTxFlagsAndMCS radiotapHeaderData; -}__attribute__ ((packed)); -static_assert(sizeof(RadiotapHeaderTx) == RadiotapHeaderTx::SIZE_BYTES, "ALWAYS TRUE"); +} __attribute__((packed)); +static_assert(sizeof(RadiotapHeaderTx) == RadiotapHeaderTx::SIZE_BYTES, + "ALWAYS TRUE"); static_assert(sizeof(RadiotapHeaderWithTxFlagsAndMCS) == - RadiotapHeaderTx::SIZE_BYTES, "ALWAYS TRUE"); + RadiotapHeaderTx::SIZE_BYTES, + "ALWAYS TRUE"); namespace RadiotapHelper { -// [RadiotapHeaderTx | Ieee80211HeaderRaw | customHeader (if not size 0) | payload (if not size 0)] -static std::vector create_radiotap_wifi_packet(const RadiotapHeaderTx & radiotapHeader, - const Ieee80211HeaderRaw &ieee80211Header, - const uint8_t* data,int data_len){ - std::vector packet(radiotapHeader.getSize() + sizeof(ieee80211Header.data) + data_len); +// [RadiotapHeaderTx | Ieee80211HeaderRaw | customHeader (if not size 0) | +// payload (if not size 0)] +static std::vector create_radiotap_wifi_packet( + const RadiotapHeaderTx &radiotapHeader, + const Ieee80211HeaderRaw &ieee80211Header, const uint8_t *data, + int data_len) { + std::vector packet(radiotapHeader.getSize() + + sizeof(ieee80211Header.data) + data_len); uint8_t *p = packet.data(); // radiotap header memcpy(p, radiotapHeader.getData(), radiotapHeader.getSize()); @@ -180,6 +207,6 @@ static std::vector create_radiotap_wifi_packet(const RadiotapHeaderTx & return packet; } -} +} // namespace RadiotapHelper -#endif //__WIFIBROADCAST_RADIOTAP_HEADER_HPP__ \ No newline at end of file +#endif //__WIFIBROADCAST_RADIOTAP_HEADER_HPP__ \ No newline at end of file diff --git a/wifibroadcast/radiotap/RadiotapHeaderTxHolder.hpp b/wifibroadcast/src/radiotap/RadiotapHeaderTxHolder.hpp similarity index 58% rename from wifibroadcast/radiotap/RadiotapHeaderTxHolder.hpp rename to wifibroadcast/src/radiotap/RadiotapHeaderTxHolder.hpp index 8e8b5ffa..63913b05 100644 --- a/wifibroadcast/radiotap/RadiotapHeaderTxHolder.hpp +++ b/wifibroadcast/src/radiotap/RadiotapHeaderTxHolder.hpp @@ -19,56 +19,59 @@ */ class RadiotapHeaderTxHolder { public: - explicit RadiotapHeaderTxHolder(){ - m_console=wifibroadcast::log::get_default(); + explicit RadiotapHeaderTxHolder() { + m_console = wifibroadcast::log::get_default(); } - void thread_safe_set(RadiotapHeaderTx::UserSelectableParams params){ - m_radioTapHeaderParams=params; + void thread_safe_set(RadiotapHeaderTx::UserSelectableParams params) { + m_radioTapHeaderParams = params; update_locked(); } RadiotapHeaderTx thread_safe_get() { std::lock_guard guard(m_radiotap_header_mutex); return m_radiotap_header; } + public: - void update_mcs_index(uint8_t mcs_index){ - m_console->debug("update_mcs_index {}",mcs_index); - m_radioTapHeaderParams.mcs_index=mcs_index; + void update_mcs_index(uint8_t mcs_index) { + m_console->debug("update_mcs_index {}", mcs_index); + m_radioTapHeaderParams.mcs_index = mcs_index; update_locked(); } - void update_channel_width(int width_mhz){ - m_console->debug("update_channel_width {}",width_mhz); - m_radioTapHeaderParams.bandwidth=width_mhz; + void update_channel_width(int width_mhz) { + m_console->debug("update_channel_width {}", width_mhz); + m_radioTapHeaderParams.bandwidth = width_mhz; update_locked(); } - void update_stbc(int stbc){ - m_console->debug("update_stbc {}",stbc); - if(stbc<0 || stbc> 3){ + void update_stbc(int stbc) { + m_console->debug("update_stbc {}", stbc); + if (stbc < 0 || stbc > 3) { m_console->warn("Invalid stbc index"); - return ; + return; } - m_radioTapHeaderParams.stbc=stbc; + m_radioTapHeaderParams.stbc = stbc; update_locked(); } - void update_guard_interval(bool short_gi){ - m_radioTapHeaderParams.short_gi=short_gi; + void update_guard_interval(bool short_gi) { + m_radioTapHeaderParams.short_gi = short_gi; update_locked(); } - void update_ldpc(bool ldpc){ - m_radioTapHeaderParams.ldpc=ldpc; + void update_ldpc(bool ldpc) { + m_radioTapHeaderParams.ldpc = ldpc; update_locked(); } - void update_set_flag_tx_no_ack(bool enable){ - m_radioTapHeaderParams.set_flag_tx_no_ack=enable; + void update_set_flag_tx_no_ack(bool enable) { + m_radioTapHeaderParams.set_flag_tx_no_ack = enable; update_locked(); } + private: - void update_locked(){ - auto header= RadiotapHeaderTx{m_radioTapHeaderParams}; + void update_locked() { + auto header = RadiotapHeaderTx{m_radioTapHeaderParams}; // Swap out the actual header (thread-safe) std::lock_guard guard(m_radiotap_header_mutex); - m_radiotap_header=header; + m_radiotap_header = header; } + private: std::shared_ptr m_console; RadiotapHeaderTx::UserSelectableParams m_radioTapHeaderParams{}; diff --git a/wifibroadcast/src/radiotap/RadiotapRxRfAggregator.cpp b/wifibroadcast/src/radiotap/RadiotapRxRfAggregator.cpp new file mode 100644 index 00000000..f62b79e8 --- /dev/null +++ b/wifibroadcast/src/radiotap/RadiotapRxRfAggregator.cpp @@ -0,0 +1,92 @@ +// +// Created by consti10 on 05.10.23. +// + +#include "RadiotapRxRfAggregator.h" + +void RadiotapRxRfAggregator::add_if_valid( + const radiotap::rx::KeyRfIndicators& indicators, + RadiotapRxRfAggregator::KeyRfAggregators& agg, + RadiotapRxRfAggregator::AggKeyRfIndicators& curr) { + if (indicators.radiotap_dbm_antsignal.has_value()) { + const auto radiotap_dbm_antsignal = + indicators.radiotap_dbm_antsignal.value(); + auto opt_minmaxavg = + agg.rssi_dbm.add_and_recalculate_if_needed(radiotap_dbm_antsignal); + if (opt_minmaxavg.has_value()) { + curr.rssi_dbm = opt_minmaxavg->avg; + } + } + if (indicators.radiotap_dbm_antnoise.has_value()) { + const auto radiotap_dbm_antnoise = indicators.radiotap_dbm_antnoise.value(); + auto opt_minmaxavg = + agg.noise_dbm.add_and_recalculate_if_needed(radiotap_dbm_antnoise); + if (opt_minmaxavg.has_value()) { + curr.noise_dbm = opt_minmaxavg->avg; + } + } + if (indicators.radiotap_lock_quality.has_value()) { + const auto radiotap_lock_quality = indicators.radiotap_lock_quality.value(); + agg.signal_quality.add_signal_quality(radiotap_lock_quality); + curr.card_signal_quality_perc = + agg.signal_quality.get_current_signal_quality(); + } +} + +void RadiotapRxRfAggregator::on_valid_openhd_packet( + const radiotap::rx::ParsedRxRadiotapPacket& packet) { + add_if_valid(packet.rf_adapter, m_agg_adapter, m_current_rx_stats.adapter); + for (int i = 0; i < packet.rf_paths.size() && i < 2; i++) { + const auto& rf_path = packet.rf_paths[i]; + auto& agg = i == 0 ? m_agg_antenna1 : m_agg_antenna2; + auto& curr = + i == 0 ? m_current_rx_stats.antenna1 : m_current_rx_stats.antenna2; + add_if_valid(rf_path, agg, curr); + } + // if(m_wifi_cards[wlan_idx].type==wifibroadcast::WIFI_CARD_TYPE_RTL8812AU){ + // RTL8812AU BUG - general value cannot be used, use max of antennas instead + // this_wifi_card_stats.card_dbm=std::max(this_wifi_card_stats.antenna1_dbm,this_wifi_card_stats.antenna2_dbm); + // }*/ +} + +void RadiotapRxRfAggregator::set_debug_invalid_values(bool enable) { + m_agg_adapter.rssi_dbm.set_debug_invalid_rssi(enable, 0); + m_agg_adapter.noise_dbm.set_debug_invalid_rssi(enable, 0); + m_agg_adapter.signal_quality.set_debug_invalid_signal_quality(enable); +} + +void RadiotapRxRfAggregator::reset() { + m_current_rx_stats = {}; + m_agg_adapter.reset(); + m_agg_antenna1.reset(); + m_agg_antenna2.reset(); +} + +std::string RadiotapRxRfAggregator::card_key_rf_indicators_to_string( + const RadiotapRxRfAggregator::CardKeyRfIndicators& indicators) { + return fmt::format( + "RxRfStats[Adapter: {}:{}:{} | Antenna1: {}:{}:{} | Antenna2: {}:{}:{}]", + indicators.adapter.rssi_dbm, indicators.adapter.noise_dbm, + indicators.adapter.card_signal_quality_perc, indicators.antenna1.rssi_dbm, + indicators.antenna1.noise_dbm, + indicators.antenna1.card_signal_quality_perc, + indicators.antenna2.rssi_dbm, indicators.antenna2.noise_dbm, + indicators.antenna2.card_signal_quality_perc); +} + +void RadiotapRxRfAggregator::debug_every_one_second() { + const auto now = std::chrono::steady_clock::now(); + if (now - m_last_debug_log >= std::chrono::seconds(1)) { + auto current = get_current(); + wifibroadcast::log::get_default()->debug( + "{}", + RadiotapRxRfAggregator::card_key_rf_indicators_to_string(current)); + m_last_debug_log = now; + } +} + +void RadiotapRxRfAggregator::KeyRfAggregators::reset() { + rssi_dbm.reset(); + noise_dbm.reset(); + signal_quality.reset(); +} diff --git a/wifibroadcast/radiotap/RadiotapRxRfAggregator.h b/wifibroadcast/src/radiotap/RadiotapRxRfAggregator.h similarity index 69% rename from wifibroadcast/radiotap/RadiotapRxRfAggregator.h rename to wifibroadcast/src/radiotap/RadiotapRxRfAggregator.h index 51155404..36d0f1fc 100644 --- a/wifibroadcast/radiotap/RadiotapRxRfAggregator.h +++ b/wifibroadcast/src/radiotap/RadiotapRxRfAggregator.h @@ -5,10 +5,11 @@ #ifndef WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H #define WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H -#include "RadiotapHeaderRx.hpp" +#include + #include "RSSIAccumulator.hpp" +#include "RadiotapHeaderRx.hpp" #include "SignalQualityAccumulator.hpp" -#include /** * Aggregates all key rf metrics. @@ -18,10 +19,10 @@ class RadiotapRxRfAggregator { // Aggregated (average) key rf metrics struct AggKeyRfIndicators { // -128 = invalid, [-127..-1] otherwise - int8_t rssi_dbm=-128; - int8_t noise_dbm=-128; + int8_t rssi_dbm = -128; + int8_t noise_dbm = -128; // [0,100] if valid, -1 otherwise - int8_t card_signal_quality_perc=-1; + int8_t card_signal_quality_perc = -1; }; struct CardKeyRfIndicators { // ------------- PER ADAPTER ------------ @@ -31,19 +32,20 @@ class RadiotapRxRfAggregator { AggKeyRfIndicators antenna2; }; // Called every time a valid openhd packet is received - void on_valid_openhd_packet(const radiotap::rx::ParsedRxRadiotapPacket& packet); + void on_valid_openhd_packet( + const radiotap::rx::ParsedRxRadiotapPacket& packet); // debugging of the 'invalid values reported by driver' issue void set_debug_invalid_values(bool enable); // Reset all rf metrics void reset(); // TODO: Thread safety ? - CardKeyRfIndicators get_current(){ - return m_current_rx_stats; - } - static std::string card_key_rf_indicators_to_string(const CardKeyRfIndicators& indicators); + CardKeyRfIndicators get_current() { return m_current_rx_stats; } + static std::string card_key_rf_indicators_to_string( + const CardKeyRfIndicators& indicators); void debug_every_one_second(); + private: - struct KeyRfAggregators{ + struct KeyRfAggregators { RSSIAccumulator rssi_dbm; RSSIAccumulator noise_dbm; SignalQualityAccumulator signal_quality; @@ -56,11 +58,14 @@ class RadiotapRxRfAggregator { KeyRfAggregators m_agg_antenna1; KeyRfAggregators m_agg_antenna2; CardKeyRfIndicators m_current_rx_stats{}; - std::chrono::steady_clock::time_point m_last_debug_log=std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point m_last_debug_log = + std::chrono::steady_clock::now(); }; -static std::ostream& operator<<(std::ostream& strm, const RadiotapRxRfAggregator::CardKeyRfIndicators& data){ - strm< + +#include "../wifibroadcast_spdlog.h" +#include "TimeHelper.hpp" + +/** + * Helper to accumulate (rtl8812au) signal quality values - + * aka values that should always be in [0..100] range. + */ +class SignalQualityAccumulator { + public: + void add_signal_quality(int signal_quality_perc) { + if (signal_quality_perc > 100 || signal_quality_perc < 0) { + if (m_debug_invalid_signal_quality) { + wifibroadcast::log::get_default()->debug("Invalid signal quality {}", + signal_quality_perc); + } + return; + } + m_acc.add(signal_quality_perc); + if (m_acc.getNSamples() > 10 || + m_acc.get_delta_since_last_reset() > std::chrono::milliseconds(500)) { + const auto tmp = m_acc.getMinMaxAvg(); + const auto avg = tmp.avg; + if (avg >= 0 && avg <= 100) { + m_curr_signal_quality = avg; + } + m_acc.reset(); + } + } + void reset() { + m_acc.reset(); + m_curr_signal_quality = -1; + } + int8_t get_current_signal_quality() const { return m_curr_signal_quality; } + void set_debug_invalid_signal_quality(bool enable) { + m_debug_invalid_signal_quality = enable; + } + + private: + BaseAvgCalculator m_acc; + // -1 if invalid, [0,100] otherwise + int8_t m_curr_signal_quality = -1; + bool m_debug_invalid_signal_quality = false; +}; + +#endif // WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP diff --git a/wifibroadcast/radiotap/radiotap_util.hpp b/wifibroadcast/src/radiotap/radiotap_util.hpp similarity index 61% rename from wifibroadcast/radiotap/radiotap_util.hpp rename to wifibroadcast/src/radiotap/radiotap_util.hpp index 925b6555..a684bda3 100644 --- a/wifibroadcast/radiotap/radiotap_util.hpp +++ b/wifibroadcast/src/radiotap/radiotap_util.hpp @@ -8,7 +8,7 @@ #include "../wifibroadcast_spdlog.h" #include "RadiotapHeaderTx.hpp" -namespace radiotap::util{ +namespace radiotap::util { static std::string toStringRadiotapFlags(uint8_t flags) { std::stringstream ss; @@ -41,7 +41,7 @@ static std::string toStringRadiotapFlags(uint8_t flags) { static std::string toStringRadiotapChannel(uint16_t frequency, uint16_t flags) { std::stringstream ss; ss << "All Radiotap channel values: ["; - ss << "Frequency[" << (int) frequency << "],"; + ss << "Frequency[" << (int)frequency << "],"; if (flags & IEEE80211_CHAN_CCK) { ss << "CHAN_CCK,"; } @@ -66,7 +66,7 @@ static std::string toStringRadiotapChannel(uint16_t frequency, uint16_t flags) { ss << "]"; return ss.str(); } -//http://www.radiotap.org/fields/RX%20flags.html +// http://www.radiotap.org/fields/RX%20flags.html static std::string toStringRadiotapRXFlags(uint16_t rxFlags) { std::stringstream ss; ss << "All IEEE80211_RADIOTAP_RX_FLAGS values: ["; @@ -97,27 +97,33 @@ static std::string toStringRadiotapTXFlags(const uint16_t txFlags) { } // http://www.radiotap.org/fields/MCS.html -static std::string toStringRadiotapMCS(uint8_t known, uint8_t flags, uint8_t mcs) { +static std::string toStringRadiotapMCS(uint8_t known, uint8_t flags, + uint8_t mcs) { std::stringstream ss; ss << "MCS Stuff: ["; if (known & IEEE80211_RADIOTAP_MCS_HAVE_BW) { ss << "HAVE_BW["; uint8_t bandwidth = flags & IEEE80211_RADIOTAP_MCS_BW_MASK; switch (bandwidth) { - case IEEE80211_RADIOTAP_MCS_BW_20: ss << "BW_20"; + case IEEE80211_RADIOTAP_MCS_BW_20: + ss << "BW_20"; break; - case IEEE80211_RADIOTAP_MCS_BW_40: ss << "BW_40"; + case IEEE80211_RADIOTAP_MCS_BW_40: + ss << "BW_40"; break; - case IEEE80211_RADIOTAP_MCS_BW_20L: ss << "BW_20L"; + case IEEE80211_RADIOTAP_MCS_BW_20L: + ss << "BW_20L"; break; - case IEEE80211_RADIOTAP_MCS_BW_20U: ss << "BW_20U"; + case IEEE80211_RADIOTAP_MCS_BW_20U: + ss << "BW_20U"; break; - default:ss << "Unknown"; + default: + ss << "Unknown"; } ss << "],"; } if (known & IEEE80211_RADIOTAP_MCS_HAVE_MCS) { - ss << "HAVE_MCS[" << (int) mcs << "],"; + ss << "HAVE_MCS[" << (int)mcs << "],"; } if (known & IEEE80211_RADIOTAP_MCS_HAVE_GI) { uint8_t gi = flags & IEEE80211_RADIOTAP_MCS_SGI; @@ -133,62 +139,65 @@ static std::string toStringRadiotapMCS(uint8_t known, uint8_t flags, uint8_t mcs } if (known & IEEE80211_RADIOTAP_MCS_HAVE_STBC) { uint8_t stbc = flags << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; - ss << "HAVE_STBC[" << (int) stbc << "],"; + ss << "HAVE_STBC[" << (int)stbc << "],"; } ss << "]"; return ss.str(); } static std::string radiotap_header_to_string(const uint8_t *pkt, int pktlen) { - struct ieee80211_radiotap_iterator iterator{}; + struct ieee80211_radiotap_iterator iterator {}; std::stringstream ss; - int ret = ieee80211_radiotap_iterator_init(&iterator, (ieee80211_radiotap_header *) pkt, pktlen, NULL); + int ret = ieee80211_radiotap_iterator_init( + &iterator, (ieee80211_radiotap_header *)pkt, pktlen, NULL); if (ret) { - ss<<"ill-formed ieee80211_radiotap header "< radiotap_rc_ath9k = { - 0, // <-- radiotap version (0x00) - 0, // <-- radiotap version (0x00) + 0, // <-- radiotap version (0x00) + 0, // <-- radiotap version (0x00) - 13, // <- radiotap header length (0x0d) - 0, // <- radiotap header length (0x00) + 13, // <- radiotap header length (0x0d) + 0, // <- radiotap header length (0x00) - 0, // <-- radiotap present flags(0x00) - 128, // <-- RADIOTAP_TX_FLAGS + (0x80) - 8, // <-- RADIOTAP_MCS (0x08) - 0, // (0x00) + 0, // <-- radiotap present flags(0x00) + 128, // <-- RADIOTAP_TX_FLAGS + (0x80) + 8, // <-- RADIOTAP_MCS (0x08) + 0, // (0x00) - 8, // <-- RADIOTAP_F_TX_NOACK (0x08) - 0, // (0x00) - 55, // <-- bitmap (0x37) - 48, // <-- flags (0x30) - 0, // <-- mcs_index (0x00) + 8, // <-- RADIOTAP_F_TX_NOACK (0x08) + 0, // (0x00) + 55, // <-- bitmap (0x37) + 48, // <-- flags (0x30) + 0, // <-- mcs_index (0x00) }; -} +} // namespace OldRadiotapHeaders #endif // WIFIBROADCAST_RADIOTAP_UTIL_HPP diff --git a/wifibroadcast/raw_socket_helper.hpp b/wifibroadcast/src/raw_socket_helper.hpp similarity index 52% rename from wifibroadcast/raw_socket_helper.hpp rename to wifibroadcast/src/raw_socket_helper.hpp index 7ec1de01..e9e67974 100644 --- a/wifibroadcast/raw_socket_helper.hpp +++ b/wifibroadcast/src/raw_socket_helper.hpp @@ -9,20 +9,20 @@ #include #include #include -#include "HelperSources/SocketHelper.hpp" +#include "SocketHelper.hpp" #include "wifibroadcast_spdlog.h" // taken from // https://github.com/OpenHD/Open.HD/blob/2.0/wifibroadcast-base/tx_rawsock.c#L86 // open wifi interface using a socket (somehow this works ?!) static int open_wifi_interface_as_raw_socket(const std::string &wifi) { - auto console=wifibroadcast::log::create_or_get("raw_sock"); - struct sockaddr_ll ll_addr{}; - struct ifreq ifr{}; + auto console = wifibroadcast::log::create_or_get("raw_sock"); + struct sockaddr_ll ll_addr {}; + struct ifreq ifr {}; int sock = socket(AF_PACKET, SOCK_RAW, 0); if (sock == -1) { - console->error("open socket failed {} {}",wifi.c_str(), strerror(errno)); + console->error("open socket failed {} {}", wifi.c_str(), strerror(errno)); } ll_addr.sll_family = AF_PACKET; @@ -43,23 +43,26 @@ static int open_wifi_interface_as_raw_socket(const std::string &wifi) { memcpy(ll_addr.sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - if (bind(sock, (struct sockaddr *) &ll_addr, sizeof(ll_addr)) == -1) { + if (bind(sock, (struct sockaddr *)&ll_addr, sizeof(ll_addr)) == -1) { close(sock); console->error("bind failed"); } - SocketHelper::debug_send_rcv_timeout(sock,console); - //const auto wanted_send_timeout=std::chrono::milliseconds(20); - //console->debug("Setting send timeout to {}",MyTimeHelper::R(wanted_send_timeout)); - //SocketHelper::set_socket_send_rcv_timeout(sock,std::chrono::milliseconds(20), true); - // debug the timeout after setting - //SocketHelper::debug_send_rcv_timeout(sock,console); - // buff size - SocketHelper::set_socket_send_rcv_buffsize(sock,1510*1, true); - SocketHelper::debug_send_rcv_buffsize(sock,console); - //const int wanted_sendbuff_bytes=128*1024*1024; - //SocketHelper::set_socket_send_rcv_buffsize(sock,wanted_sendbuff_bytes, true); - // buff size end - console->debug("{} socket opened",wifi); + SocketHelper::debug_send_rcv_timeout(sock, console); + // const auto wanted_send_timeout=std::chrono::milliseconds(20); + // console->debug("Setting send timeout to + // {}",MyTimeHelper::R(wanted_send_timeout)); + // SocketHelper::set_socket_send_rcv_timeout(sock,std::chrono::milliseconds(20), + // true); + // debug the timeout after setting + // SocketHelper::debug_send_rcv_timeout(sock,console); + // buff size + SocketHelper::set_socket_send_rcv_buffsize(sock, 1510 * 1, true); + SocketHelper::debug_send_rcv_buffsize(sock, console); + // const int wanted_sendbuff_bytes=128*1024*1024; + // SocketHelper::set_socket_send_rcv_buffsize(sock,wanted_sendbuff_bytes, + // true); + // buff size end + console->debug("{} socket opened", wifi); return sock; } diff --git a/wifibroadcast/tmp.txt b/wifibroadcast/src/tmp.txt similarity index 100% rename from wifibroadcast/tmp.txt rename to wifibroadcast/src/tmp.txt diff --git a/wifibroadcast/wifibroadcast_spdlog.cpp b/wifibroadcast/src/wifibroadcast_spdlog.cpp similarity index 100% rename from wifibroadcast/wifibroadcast_spdlog.cpp rename to wifibroadcast/src/wifibroadcast_spdlog.cpp diff --git a/wifibroadcast/wifibroadcast_spdlog.h b/wifibroadcast/src/wifibroadcast_spdlog.h similarity index 86% rename from wifibroadcast/wifibroadcast_spdlog.h rename to wifibroadcast/src/wifibroadcast_spdlog.h index 5d0ab097..e272a973 100644 --- a/wifibroadcast/wifibroadcast_spdlog.h +++ b/wifibroadcast/src/wifibroadcast_spdlog.h @@ -8,13 +8,14 @@ //#include "wifibroadcast-spdlog-fake.h" #include + #include -namespace wifibroadcast::log{ +namespace wifibroadcast::log { std::shared_ptr create_or_get(const std::string& logger_name); std::shared_ptr get_default(); -} +} // namespace wifibroadcast::log #endif // WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ From 63c729ab33293277178788f7ebf0f0fa0b0ca6ad Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:50:23 +0100 Subject: [PATCH 3/7] add clang format CI --- .github/workflows/clang_format_test.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/clang_format_test.yml diff --git a/.github/workflows/clang_format_test.yml b/.github/workflows/clang_format_test.yml new file mode 100644 index 00000000..6ad361f7 --- /dev/null +++ b/.github/workflows/clang_format_test.yml @@ -0,0 +1,23 @@ +# Run clang-tidy +name: Clang-tidy + +on: + push: + +jobs: + + validate_codestyle_clang_format: + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Dependencies + run: | + sudo apt install clang-format + + - name: Validate + run: | + ./run_clang_format.sh From 24117a5f7f50c3b3931066b628b914ccc603b9fb Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:55:52 +0100 Subject: [PATCH 4/7] rename to lib --- wifibroadcast/WBLib.cmake | 6 +++--- wifibroadcast/{external => lib}/Readme.txt | 0 wifibroadcast/{external => lib}/fec/fec_base.cpp | 0 wifibroadcast/{external => lib}/fec/fec_base.h | 0 wifibroadcast/{external => lib}/fec/gf_optimized/Readme.md | 0 .../{external => lib}/fec/gf_optimized/alignment_check.h | 0 .../{external => lib}/fec/gf_optimized/gf256_avx2.h | 0 .../{external => lib}/fec/gf_optimized/gf256_flat_table.h | 0 .../{external => lib}/fec/gf_optimized/gf256_neon.h | 0 .../fec/gf_optimized/gf256_optimized_include.h | 0 .../{external => lib}/fec/gf_optimized/gf256_ssse3.h | 0 .../{external => lib}/fec/gf_optimized/gf256tables285.h | 0 wifibroadcast/{external => lib}/fec/gf_simple/gf_simple.h | 0 .../moodycamel/concurrentqueue/blockingconcurrentqueue.h | 0 .../{ => lib}/moodycamel/concurrentqueue/concurrentqueue.h | 0 .../moodycamel/concurrentqueue/lightweightsemaphore.h | 0 .../{ => lib}/moodycamel/readerwriterqueue/atomicops.h | 0 .../readerwriterqueue/readerwritercircularbuffer.h | 0 wifibroadcast/{external => lib}/radiotap/platform.h | 0 wifibroadcast/{external => lib}/radiotap/radiotap.c | 0 wifibroadcast/{external => lib}/radiotap/radiotap.h | 0 wifibroadcast/{external => lib}/radiotap/radiotap_iter.h | 0 wifibroadcast/src/fec/FEC.h | 2 +- wifibroadcast/src/fec/FECEncoder.cpp | 2 +- wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp | 4 ++-- 25 files changed, 7 insertions(+), 7 deletions(-) rename wifibroadcast/{external => lib}/Readme.txt (100%) rename wifibroadcast/{external => lib}/fec/fec_base.cpp (100%) rename wifibroadcast/{external => lib}/fec/fec_base.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/Readme.md (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/alignment_check.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256_avx2.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256_flat_table.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256_neon.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256_optimized_include.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256_ssse3.h (100%) rename wifibroadcast/{external => lib}/fec/gf_optimized/gf256tables285.h (100%) rename wifibroadcast/{external => lib}/fec/gf_simple/gf_simple.h (100%) rename wifibroadcast/{ => lib}/moodycamel/concurrentqueue/blockingconcurrentqueue.h (100%) rename wifibroadcast/{ => lib}/moodycamel/concurrentqueue/concurrentqueue.h (100%) rename wifibroadcast/{ => lib}/moodycamel/concurrentqueue/lightweightsemaphore.h (100%) rename wifibroadcast/{ => lib}/moodycamel/readerwriterqueue/atomicops.h (100%) rename wifibroadcast/{ => lib}/moodycamel/readerwriterqueue/readerwritercircularbuffer.h (100%) rename wifibroadcast/{external => lib}/radiotap/platform.h (100%) rename wifibroadcast/{external => lib}/radiotap/radiotap.c (100%) rename wifibroadcast/{external => lib}/radiotap/radiotap.h (100%) rename wifibroadcast/{external => lib}/radiotap/radiotap_iter.h (100%) diff --git a/wifibroadcast/WBLib.cmake b/wifibroadcast/WBLib.cmake index b393cdd8..af904dbf 100644 --- a/wifibroadcast/WBLib.cmake +++ b/wifibroadcast/WBLib.cmake @@ -18,11 +18,11 @@ add_library(wifibroadcast STATIC) # initialized below add_library(wifibroadcast::wifibroadcast ALIAS wifibroadcast) #target_compile_options(wifibroadcast INTERFACE -Wno-address-of-packed-member -Wno-cast-align) -# the stuff in external is a bit extra +# the stuff in lib is a bit extra target_sources(wifibroadcast PRIVATE # radiotap and fec - ${CMAKE_CURRENT_LIST_DIR}/external/radiotap/radiotap.c - ${CMAKE_CURRENT_LIST_DIR}/external/fec/fec_base.cpp + ${CMAKE_CURRENT_LIST_DIR}/lib/radiotap/radiotap.c + ${CMAKE_CURRENT_LIST_DIR}/lib/fec/fec_base.cpp ) # Well, let's just build everything together diff --git a/wifibroadcast/external/Readme.txt b/wifibroadcast/lib/Readme.txt similarity index 100% rename from wifibroadcast/external/Readme.txt rename to wifibroadcast/lib/Readme.txt diff --git a/wifibroadcast/external/fec/fec_base.cpp b/wifibroadcast/lib/fec/fec_base.cpp similarity index 100% rename from wifibroadcast/external/fec/fec_base.cpp rename to wifibroadcast/lib/fec/fec_base.cpp diff --git a/wifibroadcast/external/fec/fec_base.h b/wifibroadcast/lib/fec/fec_base.h similarity index 100% rename from wifibroadcast/external/fec/fec_base.h rename to wifibroadcast/lib/fec/fec_base.h diff --git a/wifibroadcast/external/fec/gf_optimized/Readme.md b/wifibroadcast/lib/fec/gf_optimized/Readme.md similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/Readme.md rename to wifibroadcast/lib/fec/gf_optimized/Readme.md diff --git a/wifibroadcast/external/fec/gf_optimized/alignment_check.h b/wifibroadcast/lib/fec/gf_optimized/alignment_check.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/alignment_check.h rename to wifibroadcast/lib/fec/gf_optimized/alignment_check.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256_avx2.h b/wifibroadcast/lib/fec/gf_optimized/gf256_avx2.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256_avx2.h rename to wifibroadcast/lib/fec/gf_optimized/gf256_avx2.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256_flat_table.h b/wifibroadcast/lib/fec/gf_optimized/gf256_flat_table.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256_flat_table.h rename to wifibroadcast/lib/fec/gf_optimized/gf256_flat_table.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256_neon.h b/wifibroadcast/lib/fec/gf_optimized/gf256_neon.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256_neon.h rename to wifibroadcast/lib/fec/gf_optimized/gf256_neon.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256_optimized_include.h b/wifibroadcast/lib/fec/gf_optimized/gf256_optimized_include.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256_optimized_include.h rename to wifibroadcast/lib/fec/gf_optimized/gf256_optimized_include.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256_ssse3.h b/wifibroadcast/lib/fec/gf_optimized/gf256_ssse3.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256_ssse3.h rename to wifibroadcast/lib/fec/gf_optimized/gf256_ssse3.h diff --git a/wifibroadcast/external/fec/gf_optimized/gf256tables285.h b/wifibroadcast/lib/fec/gf_optimized/gf256tables285.h similarity index 100% rename from wifibroadcast/external/fec/gf_optimized/gf256tables285.h rename to wifibroadcast/lib/fec/gf_optimized/gf256tables285.h diff --git a/wifibroadcast/external/fec/gf_simple/gf_simple.h b/wifibroadcast/lib/fec/gf_simple/gf_simple.h similarity index 100% rename from wifibroadcast/external/fec/gf_simple/gf_simple.h rename to wifibroadcast/lib/fec/gf_simple/gf_simple.h diff --git a/wifibroadcast/moodycamel/concurrentqueue/blockingconcurrentqueue.h b/wifibroadcast/lib/moodycamel/concurrentqueue/blockingconcurrentqueue.h similarity index 100% rename from wifibroadcast/moodycamel/concurrentqueue/blockingconcurrentqueue.h rename to wifibroadcast/lib/moodycamel/concurrentqueue/blockingconcurrentqueue.h diff --git a/wifibroadcast/moodycamel/concurrentqueue/concurrentqueue.h b/wifibroadcast/lib/moodycamel/concurrentqueue/concurrentqueue.h similarity index 100% rename from wifibroadcast/moodycamel/concurrentqueue/concurrentqueue.h rename to wifibroadcast/lib/moodycamel/concurrentqueue/concurrentqueue.h diff --git a/wifibroadcast/moodycamel/concurrentqueue/lightweightsemaphore.h b/wifibroadcast/lib/moodycamel/concurrentqueue/lightweightsemaphore.h similarity index 100% rename from wifibroadcast/moodycamel/concurrentqueue/lightweightsemaphore.h rename to wifibroadcast/lib/moodycamel/concurrentqueue/lightweightsemaphore.h diff --git a/wifibroadcast/moodycamel/readerwriterqueue/atomicops.h b/wifibroadcast/lib/moodycamel/readerwriterqueue/atomicops.h similarity index 100% rename from wifibroadcast/moodycamel/readerwriterqueue/atomicops.h rename to wifibroadcast/lib/moodycamel/readerwriterqueue/atomicops.h diff --git a/wifibroadcast/moodycamel/readerwriterqueue/readerwritercircularbuffer.h b/wifibroadcast/lib/moodycamel/readerwriterqueue/readerwritercircularbuffer.h similarity index 100% rename from wifibroadcast/moodycamel/readerwriterqueue/readerwritercircularbuffer.h rename to wifibroadcast/lib/moodycamel/readerwriterqueue/readerwritercircularbuffer.h diff --git a/wifibroadcast/external/radiotap/platform.h b/wifibroadcast/lib/radiotap/platform.h similarity index 100% rename from wifibroadcast/external/radiotap/platform.h rename to wifibroadcast/lib/radiotap/platform.h diff --git a/wifibroadcast/external/radiotap/radiotap.c b/wifibroadcast/lib/radiotap/radiotap.c similarity index 100% rename from wifibroadcast/external/radiotap/radiotap.c rename to wifibroadcast/lib/radiotap/radiotap.c diff --git a/wifibroadcast/external/radiotap/radiotap.h b/wifibroadcast/lib/radiotap/radiotap.h similarity index 100% rename from wifibroadcast/external/radiotap/radiotap.h rename to wifibroadcast/lib/radiotap/radiotap.h diff --git a/wifibroadcast/external/radiotap/radiotap_iter.h b/wifibroadcast/lib/radiotap/radiotap_iter.h similarity index 100% rename from wifibroadcast/external/radiotap/radiotap_iter.h rename to wifibroadcast/lib/radiotap/radiotap_iter.h diff --git a/wifibroadcast/src/fec/FEC.h b/wifibroadcast/src/fec/FEC.h index 3c46e9e1..bac4f47c 100644 --- a/wifibroadcast/src/fec/FEC.h +++ b/wifibroadcast/src/fec/FEC.h @@ -9,7 +9,7 @@ #include #include -#include "../../external/fec/fec_base.h" +#include "../../lib/fec/fec_base.h" #include "FECConstants.hpp" #include "Helper.hpp" diff --git a/wifibroadcast/src/fec/FECEncoder.cpp b/wifibroadcast/src/fec/FECEncoder.cpp index 6529cd34..700ed1b7 100644 --- a/wifibroadcast/src/fec/FECEncoder.cpp +++ b/wifibroadcast/src/fec/FECEncoder.cpp @@ -5,7 +5,7 @@ #include #include -#include "../../external/fec/fec_base.h" +#include "../../lib/fec/fec_base.h" #include "BlockSizeHelper.hpp" #include "FECConstants.hpp" diff --git a/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp index 8e49bf1f..105ac98e 100644 --- a/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp +++ b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp @@ -3,8 +3,8 @@ #include "Helper.hpp" extern "C" { -#include "../../external/radiotap/radiotap.h" -#include "../../external/radiotap/radiotap_iter.h" +#include "../../lib/radiotap/radiotap.h" +#include "../../lib/radiotap/radiotap_iter.h" }; #include From 91d1c74fe956d9ac1237db70393f36ad72c03b60 Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:59:20 +0100 Subject: [PATCH 5/7] clang-format --- .../src/HelperSources/EmulatedPacketDrop.hpp | 2 +- wifibroadcast/src/HelperSources/SocketHelper.hpp | 4 ++-- wifibroadcast/src/Ieee80211Header.hpp | 15 +++++++-------- wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp | 2 +- wifibroadcast/src/radiotap/radiotap_util.hpp | 2 +- wifibroadcast/src/wifibroadcast_spdlog.h | 2 -- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp index 1d3d7a42..8714ad49 100644 --- a/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp +++ b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp @@ -33,7 +33,7 @@ class PacketDropEmulator { } void log() { const double perc_dropped = - (double)n_dropped_packets / (n_totoal_packets)*100.0; + (double)n_dropped_packets / (n_totoal_packets) * 100.0; // std::cout<<"N // dropped:"<forwardPacketViaUDP(packet, packetSize); } } - [[nodiscard]] const std::list> - &getForwarders() const { + [[nodiscard]] const std::list> & + getForwarders() const { return udpForwarders; } diff --git a/wifibroadcast/src/Ieee80211Header.hpp b/wifibroadcast/src/Ieee80211Header.hpp index d19fd501..40b59bf1 100644 --- a/wifibroadcast/src/Ieee80211Header.hpp +++ b/wifibroadcast/src/Ieee80211Header.hpp @@ -180,14 +180,13 @@ class Ieee80211HeaderRaw { std::array data = { 0x08, 0x01, // first 2 bytes control fiels 0x00, 0x00, // 2 bytes duration (has this even an effect ?!) - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, // something MAC ( 6 bytes), I think Receiver address (MAC of - // AP) - 0x13, 0x22, 0x33, 0x44, - 0x55, 0x66, // something MAC ( 6 bytes), I think SRC MAC (mac of source - // STA)- last byte is used as radio port - 0x13, 0x22, 0x33, 0x44, - 0x55, 0x66, // something MAC ( 6 bytes), I think DEST MAC (mac of dest + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // something MAC ( 6 bytes), I think + // Receiver address (MAC of AP) + 0x13, 0x22, 0x33, 0x44, 0x55, + 0x66, // something MAC ( 6 bytes), I think SRC MAC (mac of source + // STA)- last byte is used as radio port + 0x13, 0x22, 0x33, 0x44, 0x55, + 0x66, // something MAC ( 6 bytes), I think DEST MAC (mac of dest // STA) - last byte is als used as radio port 0x00, 0x00, // iee80211 sequence control ( 2 bytes ) }; diff --git a/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp index 105ac98e..17d11471 100644 --- a/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp +++ b/wifibroadcast/src/radiotap/RadiotapHeaderTx.hpp @@ -116,7 +116,7 @@ class RadiotapHeaderTx { if (params.set_flag_tx_no_ack) { radiotapHeaderData.txFlags = IEEE80211_RADIOTAP_F_TX_NOACK; //| IEEE80211_RADIOTAP_F_TX_CTS | - //IEEE80211_RADIOTAP_F_TX_RTS + // IEEE80211_RADIOTAP_F_TX_RTS } else { radiotapHeaderData.txFlags = 0; } diff --git a/wifibroadcast/src/radiotap/radiotap_util.hpp b/wifibroadcast/src/radiotap/radiotap_util.hpp index a684bda3..44dd6272 100644 --- a/wifibroadcast/src/radiotap/radiotap_util.hpp +++ b/wifibroadcast/src/radiotap/radiotap_util.hpp @@ -179,7 +179,7 @@ static std::string radiotap_header_to_string(const uint8_t *pkt, int pktlen) { case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: { // This field contains a single signed 8-bit value that indicates // the RF signal power at the antenna, in decibels difference - //from 1mW. + // from 1mW. int8_t value = *(int8_t *)iterator.this_arg; ss << "IEEE80211_RADIOTAP_DBM_ANTSIGNAL:" << (int)value << "dBm size:" << curr_arg_size << "\n"; diff --git a/wifibroadcast/src/wifibroadcast_spdlog.h b/wifibroadcast/src/wifibroadcast_spdlog.h index e272a973..3175606c 100644 --- a/wifibroadcast/src/wifibroadcast_spdlog.h +++ b/wifibroadcast/src/wifibroadcast_spdlog.h @@ -5,8 +5,6 @@ #ifndef WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ #define WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ -//#include "wifibroadcast-spdlog-fake.h" - #include #include From fdd25afc25eccb1bad729f6f3f4ed04b8fe88ea5 Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 12:59:39 +0100 Subject: [PATCH 6/7] clang-format --- run_clang_format.sh | 4 ++-- wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp | 2 +- wifibroadcast/src/HelperSources/SocketHelper.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/run_clang_format.sh b/run_clang_format.sh index b9c18b23..aebce46c 100755 --- a/run_clang_format.sh +++ b/run_clang_format.sh @@ -39,5 +39,5 @@ function fix_warnings() { clang-format --verbose -i --style=file $FILE_LIST } -fix_warnings -#check_warning \ No newline at end of file +#fix_warnings +check_warning \ No newline at end of file diff --git a/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp index 8714ad49..1d3d7a42 100644 --- a/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp +++ b/wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp @@ -33,7 +33,7 @@ class PacketDropEmulator { } void log() { const double perc_dropped = - (double)n_dropped_packets / (n_totoal_packets) * 100.0; + (double)n_dropped_packets / (n_totoal_packets)*100.0; // std::cout<<"N // dropped:"<forwardPacketViaUDP(packet, packetSize); } } - [[nodiscard]] const std::list> & - getForwarders() const { + [[nodiscard]] const std::list> + &getForwarders() const { return udpForwarders; } From a3531650ff4a2794cefd8a4341fa91295e913218 Mon Sep 17 00:00:00 2001 From: consti10 Date: Wed, 21 Feb 2024 13:01:14 +0100 Subject: [PATCH 7/7] go back to c++17 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b2754ab..ca159f48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16.3) project(Wifibroadcast) cmake_minimum_required(VERSION 3.16.3) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-address-use-after-scope -fsanitize=address") # Get spdlog from package manager for those tests