diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f1cf77d..780ce551 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,9 @@ target_link_libraries(injection_rate_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) add_executable(example_pollute executables/example_pollute.cpp) target_link_libraries(example_pollute PRIVATE ${WB_TARGET_LINK_LIBRARIES}) +add_executable(test_listen executables/test_listen.cpp) +target_link_libraries(test_listen PRIVATE ${WB_TARGET_LINK_LIBRARIES}) + # When it is a static library, we don't need to install it. # But if it is a shared library, we need to install it. #install(TARGETS wifibroadcast DESTINATION lib) diff --git a/WBLib.cmake b/WBLib.cmake index 07e8bb13..ed8eb61d 100644 --- a/WBLib.cmake +++ b/WBLib.cmake @@ -30,11 +30,12 @@ target_sources(wifibroadcast PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/WBStreamTx.cpp ${CMAKE_CURRENT_LIST_DIR}/src/WBTxRx.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Ieee80211Header.hpp - ${CMAKE_CURRENT_LIST_DIR}/src/RadiotapHeader.hpp - ${CMAKE_CURRENT_LIST_DIR}/src/RadiotapHeaderHolder.hpp - ${CMAKE_CURRENT_LIST_DIR}/src/RSSIAccumulator.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/radiotap/RadiotapHeaderTx.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/radiotap/RadiotapHeaderTxHolder.hpp + ${CMAKE_CURRENT_LIST_DIR}/src/radiotap/RSSIAccumulator.hpp ${CMAKE_CURRENT_LIST_DIR}/src/wifibroadcast_spdlog.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Encryption.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/radiotap/RadiotapRxRfAggregator.cpp ) target_include_directories(wifibroadcast PUBLIC diff --git a/executables/example_hello.cpp b/executables/example_hello.cpp index 4c05125c..459fa221 100644 --- a/executables/example_hello.cpp +++ b/executables/example_hello.cpp @@ -62,7 +62,7 @@ int main(int argc, char *const *argv) { options_txrx.log_all_received_validated_packets= true; options_txrx.advanced_debugging_rx= true; } - auto radiotap_header_holder=std::make_shared(); + auto radiotap_header_holder=std::make_shared(); std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header_holder); txrx->start_receiving(); diff --git a/executables/example_pollute.cpp b/executables/example_pollute.cpp index ef10bc5e..a796d2bb 100644 --- a/executables/example_pollute.cpp +++ b/executables/example_pollute.cpp @@ -44,7 +44,7 @@ int main(int argc, char *const *argv) { options_txrx.debug_tx_injection_time= true; options_txrx.tx_without_pcap=true; - auto radiotap_header_holder=std::make_shared(); + auto radiotap_header_holder=std::make_shared(); std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header_holder); // We do not need receive in this mode //txrx->start_receiving(); diff --git a/executables/example_udp.cpp b/executables/example_udp.cpp index 9e99ce45..6f690dc9 100644 --- a/executables/example_udp.cpp +++ b/executables/example_udp.cpp @@ -77,7 +77,7 @@ int main(int argc, char *const *argv) { //options_txrx.pcap_rx_set_direction= false; options_txrx.pcap_rx_set_direction = pcap_setdirection; options_txrx.log_all_received_validated_packets= false; - auto radiotap_header_holder=std::make_shared(); + auto radiotap_header_holder=std::make_shared(); std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header_holder); if(is_air){ diff --git a/executables/injection_rate_test.cpp b/executables/injection_rate_test.cpp index 5507b5f8..ee1d5e01 100644 --- a/executables/injection_rate_test.cpp +++ b/executables/injection_rate_test.cpp @@ -31,7 +31,7 @@ struct TestResult { int fail_bps_measured; }; -static TestResult increase_pps_until_fail(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs,const int pps_start,const int pps_increment){ +static TestResult increase_pps_until_fail(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs,const int pps_start,const int pps_increment){ auto m_console=wifibroadcast::log::create_or_get("main"); m_console->info("Testing MCS {}", mcs); hdr->update_mcs_index(mcs); @@ -80,7 +80,7 @@ static TestResult increase_pps_until_fail(std::shared_ptr txrx,std::shar return {mcs,0,0,0,0,0}; } -static void calculate_max_possible_pps_quick(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs){ +static void calculate_max_possible_pps_quick(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs){ auto m_console=wifibroadcast::log::create_or_get("main"); m_console->info("Testing MCS {}", mcs); hdr->update_mcs_index(mcs); @@ -99,7 +99,7 @@ static void calculate_max_possible_pps_quick(std::shared_ptr txrx,std::s m_console->info("MCS {} max {} {}",mcs,stats.curr_packets_per_second,StringHelper::bitrate_readable(stats.curr_bits_per_second_excluding_overhead)); } -static std::string validate_specific_rate(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs,const int rate_kbits){ +static std::string validate_specific_rate(std::shared_ptr txrx,std::shared_ptr hdr,const int mcs,const int rate_kbits){ auto m_console=wifibroadcast::log::create_or_get("main"); const auto rate_bps=(rate_kbits*1000)+10; // add a bit more to actually hit the target const auto pps=rate_bps / (TEST_PACKETS_SIZE*8); @@ -132,7 +132,7 @@ static std::string validate_specific_rate(std::shared_ptr txrx,std::shar m_console->info(ss.str()); return ss.str(); } -static void validate_rtl8812au_rates(std::shared_ptr txrx,std::shared_ptr hdr,const bool is_40mhz){ +static void validate_rtl8812au_rates(std::shared_ptr txrx,std::shared_ptr hdr,const bool is_40mhz){ hdr->update_channel_width(is_40mhz ? 40 : 20); std::stringstream log; for(int mcs=0;mcs<12;mcs++){ @@ -165,7 +165,7 @@ static void print_test_results_and_theoretical(const std::vector& te } } -static std::vector all_mcs_increase_pps_until_fail(std::shared_ptr txrx,std::shared_ptr hdr,const int pps_increment,const int max_mcs=12){ +static std::vector all_mcs_increase_pps_until_fail(std::shared_ptr txrx,std::shared_ptr hdr,const int pps_increment,const int max_mcs=12){ assert(max_mcs>=0); assert(max_mcs<=32); std::vector ret; @@ -195,7 +195,7 @@ static std::vector all_mcs_increase_pps_until_fail(std::shared_ptr txrx,std::shared_ptr hdr,bool use_40mhz){ +void long_test(std::shared_ptr txrx,std::shared_ptr hdr,bool use_40mhz){ auto m_console=wifibroadcast::log::create_or_get("main"); const int freq_w=use_40mhz ? 40 : 20; m_console->info("Long test {}",freq_w); @@ -225,7 +225,7 @@ void long_test(std::shared_ptr txrx,std::shared_ptr txrx,std::shared_ptr hdr,bool use_40mhz){ +void test_rates_and_print_results(std::shared_ptr txrx,std::shared_ptr hdr,bool use_40mhz){ const int freq_w=use_40mhz ? 40 : 20; hdr->update_channel_width(freq_w); const auto res_20mhz= all_mcs_increase_pps_until_fail(txrx,hdr, 20); @@ -261,7 +261,7 @@ int main(int argc, char *const *argv) { options_txrx.log_all_received_validated_packets= false; options_txrx.tx_without_pcap= true; - auto radiotap_header=std::make_shared(); + auto radiotap_header=std::make_shared(); std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header); // No idea if and what effect stbc and ldpc have on the rate, but openhd enables them if possible by default // since they greatly increase range / resiliency diff --git a/executables/test_listen.cpp b/executables/test_listen.cpp new file mode 100644 index 00000000..0cf5e375 --- /dev/null +++ b/executables/test_listen.cpp @@ -0,0 +1,55 @@ +// +// Created by consti10 on 07.10.23. +// Uses WBTxRx to listen to all (openhd and non openhd) traffic +// +#include "../src/WBTxRx.h" +#include "../src/wifibroadcast_spdlog.h" + +int main(int argc, char *const *argv) { + std::string card="wlxac9e17596103"; + bool pcap_setdirection= true; + int opt; + while ((opt = getopt(argc, argv, "w:d")) != -1) { + switch (opt) { + case 'w': + card = optarg; + break; + case 'd': + pcap_setdirection= false; + break ; + default: /* '?' */ + show_usage: + fprintf(stderr, + "test_listen -w [wifi card to listen on] %s\n", + argv[0]); + exit(1); + } + } + + std::vector cards; + wifibroadcast::WifiCard tmp_card{card,1}; + cards.push_back(tmp_card); + WBTxRx::Options options_txrx{}; + //options_txrx.pcap_rx_set_direction= false; + options_txrx.pcap_rx_set_direction = pcap_setdirection; + options_txrx.log_all_received_validated_packets= true; + options_txrx.rx_radiotap_debug_level=3; + options_txrx.advanced_debugging_rx= true; + auto radiotap_header_holder_tx=std::make_shared(); + std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header_holder_tx); + + txrx->start_receiving(); + + auto lastLog=std::chrono::steady_clock::now(); + while (true){ + std::this_thread::sleep_for(std::chrono::milliseconds (500)); + //auto txStats=txrx->get_tx_stats(); + auto rxStats=txrx->get_rx_stats(); + auto rx_stats_card0=txrx->get_rx_stats_for_card(0); + auto rx_rf_stats_card0=txrx->get_rx_rf_stats_for_card(0); + //std::cout<(); + auto radiotap_header_holder_tx=std::make_shared(); std::shared_ptr txrx=std::make_shared(cards,options_txrx,radiotap_header_holder_tx); const bool enable_fec= true; WBStreamTx::Options options_tx{}; options_tx.radio_port=10; options_tx.enable_fec= enable_fec; - auto radiotap_header_holder_rx=std::make_shared(); + auto radiotap_header_holder_rx=std::make_shared(); std::unique_ptr wb_tx=std::make_unique(txrx,options_tx,radiotap_header_holder_rx); WBStreamRx::Options options_rx{}; diff --git a/src/HelperSources/UINT16SeqNrHelper.hpp b/src/HelperSources/UINT16SeqNrHelper.hpp index a9788fa4..cf4288ed 100644 --- a/src/HelperSources/UINT16SeqNrHelper.hpp +++ b/src/HelperSources/UINT16SeqNrHelper.hpp @@ -11,7 +11,7 @@ #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 { diff --git a/src/RadiotapHeader.hpp b/src/RadiotapHeader.hpp deleted file mode 100644 index c9dba7e4..00000000 --- a/src/RadiotapHeader.hpp +++ /dev/null @@ -1,631 +0,0 @@ -#ifndef __WIFIBROADCAST_RADIOTAP_HEADER_HPP__ -#define __WIFIBROADCAST_RADIOTAP_HEADER_HPP__ - -#include "HelperSources/Helper.hpp" -extern "C" { -#include "external/radiotap/radiotap_iter.h" -#include "external/radiotap/radiotap.h" -}; - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 !"); - -namespace Radiotap { - -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) { - uint32_t present = 0; - for (const auto &valueToBePresent: valuesToBePresent) { - present |= 1 << valueToBePresent; - } - return present; -} - -// http://www.radiotap.org/fields/MCS.html -struct MCS { - uint8_t known = 0; - uint8_t flags = 0; - uint8_t modulationIndex = 0; -}__attribute__ ((packed)); -} - -// To inject packets we need 2 radiotap fields: "TX flags" and the "MCS field" -struct RadiotapHeaderWithTxFlagsAndMCS { - uint8_t version = 0; - uint8_t padding = 0; - uint16_t length = 13; - // http://www.radiotap.org/ - 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 ! - Radiotap::MCS mcs{}; -}__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 -class RadiotapHeader { - 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. - int bandwidth = 20; - // I do not recommend using a short guard interval - bool short_gi = false; - // https://en.wikipedia.org/wiki/Space%E2%80%93time_block_code - int stbc = 0; - // https://en.wikipedia.org/wiki/Low-density_parity-check_code#:~:text=In%20information%20theory%2C%20a%20low,subclass%20of%20the%20bipartite%20graph). - bool ldpc = false; - // https://www.digitalairwireless.com/articles/blog/demystifying-modulation-and-coding-scheme-mcs-index-values - // https://mcsindex.com/ - int mcs_index = 3; - // depends on the driver - bool set_flag_tx_no_ack= false; - }; - // Make sure that this is the only constructor - explicit RadiotapHeader(const UserSelectableParams ¶ms) { - if (params.mcs_index < Radiotap::MCS_MIN || params.mcs_index > Radiotap::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)); - } - 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::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){ - radiotapHeaderData.txFlags = - 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); - // write the mcs index - radiotapHeaderData.mcs.modulationIndex = params.mcs_index; - - switch (params.bandwidth) { - case 20:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_20; - break; - case 40:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_40; - break; - default:assert(true); - } - - if (params.short_gi) { - radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_SGI; - } - - if (params.ldpc) { - radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; - } - - switch (params.stbc) { - 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); - break; - case 3:radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_3 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); - break; - default:assert(true); - } - }; - const uint8_t *getData() const { - return (const uint8_t *) &radiotapHeaderData; - } - constexpr std::size_t getSize() const { - return SIZE_BYTES; - } - 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); - } - private: - RadiotapHeaderWithTxFlagsAndMCS radiotapHeaderData; -}__attribute__ ((packed)); -static_assert(sizeof(RadiotapHeader) == RadiotapHeader::SIZE_BYTES, "ALWAYS TRUE"); -static_assert(sizeof(RadiotapHeaderWithTxFlagsAndMCS) == RadiotapHeader::SIZE_BYTES, "ALWAYS TRUE"); - -namespace RadiotapHelper { - -static std::string toStringRadiotapFlags(uint8_t flags) { - std::stringstream ss; - ss << "All IEEE80211_RADIOTAP flags: ["; - if (flags & IEEE80211_RADIOTAP_F_CFP) { - ss << "CFP,"; - } - if (flags & IEEE80211_RADIOTAP_F_SHORTPRE) { - ss << "SHORTPRE,"; - } - if (flags & IEEE80211_RADIOTAP_F_WEP) { - ss << "WEP,"; - } - if (flags & IEEE80211_RADIOTAP_F_FRAG) { - ss << "FRAG,"; - } - if (flags & IEEE80211_RADIOTAP_F_FCS) { - ss << "FCS,"; - } - if (flags & IEEE80211_RADIOTAP_F_DATAPAD) { - ss << "DATAPAD,"; - } - if (flags & IEEE80211_RADIOTAP_F_BADFCS) { - ss << "BADFCS"; - } - ss << "]"; - return ss.str(); -} -// http://www.radiotap.org/fields/Channel.html -static std::string toStringRadiotapChannel(uint16_t frequency, uint16_t flags) { - std::stringstream ss; - ss << "All Radiotap channel values: ["; - ss << "Frequency[" << (int) frequency << "],"; - if (flags & IEEE80211_CHAN_CCK) { - ss << "CHAN_CCK,"; - } - if (flags & IEEE80211_CHAN_OFDM) { - ss << "CHAN_OFDM,"; - } - if (flags & IEEE80211_CHAN_2GHZ) { - ss << "CHAN_2GHZ,"; - } - if (flags & IEEE80211_CHAN_5GHZ) { - ss << "CHAN_5GHZ,"; - } - if (flags & IEEE80211_CHAN_DYN) { - ss << "CHAN_DYN,"; - } - if (flags & IEEE80211_CHAN_HALF) { - ss << "CHAN_HALF,"; - } - if (flags & IEEE80211_CHAN_QUARTER) { - ss << "CHAN_QUARTER,"; - } - ss << "]"; - return ss.str(); -} -//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: ["; - if (rxFlags & IEEE80211_RADIOTAP_F_RX_BADPLCP) { - ss << "RX_BADPLCP,"; - } - ss << "]"; - return ss.str(); -} -// http://www.radiotap.org/fields/TX%20flags.html -static std::string toStringRadiotapTXFlags(const uint16_t txFlags) { - std::stringstream ss; - ss << "All TX FLAGS: ["; - if (txFlags & IEEE80211_RADIOTAP_F_TX_FAIL) { - ss << "TX_FAIL,"; - } - if (txFlags & IEEE80211_RADIOTAP_F_TX_CTS) { - ss << "TX_CTS,"; - } - if (txFlags & IEEE80211_RADIOTAP_F_TX_RTS) { - ss << "TX_RTS,"; - } - if (txFlags & IEEE80211_RADIOTAP_F_TX_NOACK) { - ss << "TX_NOACK,"; - } - ss << "]"; - return ss.str(); -} - -// http://www.radiotap.org/fields/MCS.html -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"; - break; - case IEEE80211_RADIOTAP_MCS_BW_40: ss << "BW_40"; - break; - case IEEE80211_RADIOTAP_MCS_BW_20L: ss << "BW_20L"; - break; - case IEEE80211_RADIOTAP_MCS_BW_20U: ss << "BW_20U"; - break; - default:ss << "Unknown"; - } - ss << "],"; - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_MCS) { - ss << "HAVE_MCS[" << (int) mcs << "],"; - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_GI) { - uint8_t gi = flags & IEEE80211_RADIOTAP_MCS_SGI; - ss << "HAVE_GI[" << (gi == 0 ? "long" : "short") << "],"; - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_FMT) { - uint8_t fmt = flags & IEEE80211_RADIOTAP_MCS_FMT_GF; - ss << "HAVE_FMT[" << (fmt == 0 ? "mixed" : "greenfield") << "],"; - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_FEC) { - uint8_t fec_type = flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC; - ss << "HAVE_FEC[" << (fec_type == 0 ? "BBC" : "LDPC") << "]"; - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_STBC) { - uint8_t stbc = flags << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; - ss << "HAVE_STBC[" << (int) stbc << "],"; - } - ss << "]"; - return ss.str(); -} - -static void debugRadiotapHeader(const uint8_t *pkt, int pktlen, std::shared_ptr console= wifibroadcast::log::get_default()) { - struct ieee80211_radiotap_iterator iterator{}; - int ret = ieee80211_radiotap_iterator_init(&iterator, (ieee80211_radiotap_header *) pkt, pktlen, NULL); - if (ret) { - console->warn("ill-formed ieee80211_radiotap header {}",ret); - return; - } - std::stringstream ss; - ss << "Debuging Radiotap Header \n"; - while (ret == 0) { - ret = ieee80211_radiotap_iterator_next(&iterator); - if (iterator.is_radiotap_ns) { - //ss<<"Is in namespace\n"; - } - if (ret) { - continue; - } - const int curr_arg_size=iterator.this_arg_size; - /* see if this argument is something we can use */ - switch (iterator.this_arg_index) { - case IEEE80211_RADIOTAP_TSFT: - ss << "IEEE80211_RADIOTAP_TSFT:"<debug("{}",ss.str().c_str()); -} - -struct RssiForAntenna { - // which antenna the value refers to, - // or -1 this dgm value came before a IEEE80211_RADIOTAP_ANTENNA field and the antenna idx is therefore unknown - const int8_t antennaIdx; - // https://www.radiotap.org/fields/Antenna%20signal.html - const int8_t rssi; -}; -struct ParsedRxRadiotapPacket { - // Size can be anything from size=1 to size== N where N is the number of Antennas of this adapter - const std::vector allAntennaValues; - const Ieee80211HeaderRaw *ieee80211Header; - const uint8_t *payload; - const std::size_t payloadSize; - // Atheros forwards frames even though the fcs check failed ( this packet is corrupted) - const bool frameFailedFCSCheck; - // driver might not support that - std::optional mcs_index=std::nullopt; - // driver might not support that - std::optional channel_width=std::nullopt; - std::optional signal_quality=std::nullopt; -}; -static std::string all_rssi_to_string(const std::vector& all_rssi){ - std::stringstream ss; - ss<<"RSSI for antenna:"; - int idx=0; - for(const auto& rssiForAntenna:all_rssi){ - ss<<" {"<<(int)rssiForAntenna.antennaIdx<<":"<<(int)rssiForAntenna.rssi<<"}"; - idx++; - } - return ss.str(); -} -// It looks as if RTL88xxau reports 3 rssi values - for example, -//RssiForAntenna0{10} -//RssiForAntenna1{10} -//RssiForAntenna2{-18} -//Now this doesn't make sense, so this helper should fix it -static std::optional get_best_rssi_of_card(const std::vector& all_rssi,const bool fixup_rssi_rtl8812au){ - if(all_rssi.empty())return std::nullopt; - // best rssi == highest value - int8_t highest_value=INT8_MIN; - for(int i=0;ihighest_value){ - highest_value=rssi_for_antenna.rssi; - } - } - } - for(const auto& rssiForAntenna:all_rssi){ - if(fixup_rssi_rtl8812au){ - if(rssiForAntenna.antennaIdx==-1){ - continue ; - } - } - if(rssiForAntenna.rssi>highest_value){ - highest_value=rssiForAntenna.rssi; - } - } - return highest_value; -} - -// Returns std::nullopt if radiotap was unable to parse the header -// else return the *parsed information* -// To avoid confusion it might help to treat this method as a big black Box :) -static std::optional process_received_radiotap_packet(const uint8_t *pkt,const int pkt_len) { - //int pktlen = hdr.caplen; - int pktlen=pkt_len; - // - //RadiotapHelper::debugRadiotapHeader(pkt,pktlen); - // Copy the value of this flag once present and process it after the loop is done - uint8_t tmpCopyOfIEEE80211_RADIOTAP_FLAGS = 0; - //RadiotapHelper::debugRadiotapHeader(pkt, pktlen); - 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, NULL); - // weird, unfortunately it is not really documented / specified how raditap reporting dBm values with multiple antennas works - // we store all values reported by IEEE80211_RADIOTAP_ANTENNA in here - // ? there can be multiple ? - //std::vector radiotap_antennas; - // and all values reported by IEEE80211_RADIOTAP_DBM_ANTSIGNAL in here - //std::vector radiotap_antsignals; - // for rtl8812au fixup - bool is_first_reported_antenna_value= true; - // - std::optional mcs_index=std::nullopt; - std::optional channel_width=std::nullopt; - std::optional signal_quality=std::nullopt; - - int8_t currentAntenna = -1; - // not confirmed yet, but one radiotap packet might include stats for multiple antennas - std::vector allAntennaValues; - 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: - // RADIOTAP_DBM_ANTSIGNAL seems to come not before, but after - currentAntenna = iterator.this_arg[0]; - //radiotap_antennas.push_back(iterator.this_arg[0]); - break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:{ - int8_t value; - std::memcpy(&value,iterator.this_arg,1); - allAntennaValues.push_back({currentAntenna,value}); - } - break; - case IEEE80211_RADIOTAP_FLAGS: - tmpCopyOfIEEE80211_RADIOTAP_FLAGS = *(uint8_t *) (iterator.this_arg); - break; - case IEEE80211_RADIOTAP_MCS: - { - uint8_t known = iterator.this_arg[0]; - uint8_t flags = iterator.this_arg[1]; - uint8_t mcs = iterator.this_arg[2]; - if(known & IEEE80211_RADIOTAP_MCS_HAVE_MCS){ - mcs_index=static_cast(mcs); - } - if (known & IEEE80211_RADIOTAP_MCS_HAVE_BW) { - const uint8_t bandwidth = flags & IEEE80211_RADIOTAP_MCS_BW_MASK; - switch (bandwidth) { - case IEEE80211_RADIOTAP_MCS_BW_20: - case IEEE80211_RADIOTAP_MCS_BW_20U: - case IEEE80211_RADIOTAP_MCS_BW_20L: - channel_width=static_cast(20); - break; - case IEEE80211_RADIOTAP_MCS_BW_40: - channel_width=static_cast(40); - break; - default: - break ; - } - } - } - break; - case IEEE80211_RADIOTAP_LOCK_QUALITY:{ - int8_t value; - std::memcpy(&value,iterator.this_arg,1); - signal_quality=static_cast(value); - } break ; - default:break; - } - } /* while more rt headers */ - if (ret != -ENOENT) { - //wifibroadcast::log::get_default()->warn("Error parsing radiotap header!\n"; - return std::nullopt; - } - bool frameFailedFcsCheck = false; - if (tmpCopyOfIEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_BADFCS) { - //wifibroadcast::log::get_default()->warn("Got packet with bad fsc\n"; - frameFailedFcsCheck = true; - } - // the fcs is at the end of the packet - if (tmpCopyOfIEEE80211_RADIOTAP_FLAGS & IEEE80211_RADIOTAP_F_FCS) { - //<<"Packet has IEEE80211_RADIOTAP_F_FCS"; - pktlen -= 4; - } -#ifdef ENABLE_ADVANCED_DEBUGGING - wifibroadcast::log::get_default()->debug(RadiotapFlagsToString::flagsIEEE80211_RADIOTAP_MCS(mIEEE80211_RADIOTAP_MCS)); - wifibroadcast::log::get_default()->debug(RadiotapFlagsToString::flagsIEEE80211_RADIOTAP_FLAGS(mIEEE80211_RADIOTAP_FLAGS)); - // 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 - wifibroadcast::log::get_default()->debug("iterator._max_length was {}",iterator._max_length); -#endif - //assert(iterator._max_length==hdr.caplen); - /* discard the radiotap header part */ - pkt += iterator._max_length; - pktlen -= iterator._max_length; - // - 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; - // - /*std::stringstream ss; - ss<<"Antennas:"; - for(const auto& antenna : radiotap_antennas){ - ss<<(int)antenna<<","; - } - ss<<"\nAntsignals:"; - for(const auto& antsignal : radiotap_antsignals){ - ss<<(int)antsignal<<","; - } - std::cout< create_radiotap_wifi_packet(const RadiotapHeader& 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()); - p += radiotapHeader.getSize(); - // ieee80211 wbDataHeader - memcpy(p, &ieee80211Header.data, sizeof(ieee80211Header.data)); - p += sizeof(ieee80211Header.data); - memcpy(p, data, data_len); - p += data_len; - return packet; -} - -} - -// what people used for whatever reason once on OpenHD / EZ-Wifibroadcast -namespace OldRadiotapHeaders { -// https://github.com/OpenHD/Open.HD/blob/2.0/wifibroadcast-base/tx_telemetry.c#L123 -static uint8_t u8aRadiotapHeader[] = { - 0x00, 0x00, // <-- radiotap version - 0x0c, 0x00, // <- radiotap header length - 0x04, 0x80, 0x00, 0x00, // <-- radiotap present flags - 0x00, // datarate (will be overwritten later) - 0x00, - 0x00, 0x00 -}; -static uint8_t u8aRadiotapHeader80211n[] = { - 0x00, 0x00, // <-- radiotap version - 0x0d, 0x00, // <- radiotap header length - 0x00, 0x80, 0x08, 0x00, // <-- radiotap present flags (tx flags, mcs) - 0x08, 0x00, // tx-flag - 0x37, // mcs have: bw, gi, stbc ,fec - 0x30, // mcs: 20MHz bw, long guard interval, stbc, ldpc - 0x00, // mcs index 0 (speed level, will be overwritten later) -}; - -// this is what's used in -//https://github.com/OpenHD/Open.HD/blob/master/wifibroadcast-rc-Ath9k/rctx.cpp -static std::array radiotap_rc_ath9k = { - 0, // <-- radiotap version (0x00) - 0, // <-- radiotap version (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) - - 8, // <-- RADIOTAP_F_TX_NOACK (0x08) - 0, // (0x00) - 55, // <-- bitmap (0x37) - 48, // <-- flags (0x30) - 0, // <-- mcs_index (0x00) -}; -} - -#endif //__WIFIBROADCAST_RADIOTAP_HEADER_HPP__ \ No newline at end of file diff --git a/src/WBStreamTx.cpp b/src/WBStreamTx.cpp index 2970491c..a10a14bb 100644 --- a/src/WBStreamTx.cpp +++ b/src/WBStreamTx.cpp @@ -9,7 +9,7 @@ #include "BlockSizeHelper.hpp" #include "SchedulingHelper.hpp" -WBStreamTx::WBStreamTx(std::shared_ptr txrx,Options options1,std::shared_ptr radiotap_header_holder) +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)) diff --git a/src/WBStreamTx.h b/src/WBStreamTx.h index c70f9996..2e181415 100644 --- a/src/WBStreamTx.h +++ b/src/WBStreamTx.h @@ -46,7 +46,7 @@ class WBStreamTx { // 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(); @@ -112,7 +112,7 @@ class WBStreamTx { private: const Options options; std::shared_ptr m_txrx; - std::shared_ptr m_radiotap_header_holder; + 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; diff --git a/src/WBTxRx.cpp b/src/WBTxRx.cpp index 410775ab..db1e3c76 100644 --- a/src/WBTxRx.cpp +++ b/src/WBTxRx.cpp @@ -8,9 +8,11 @@ #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) +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)) @@ -35,10 +37,7 @@ WBTxRx::WBTxRx(std::vector wifi_cards1,Options options1 for(int i=0;i(); tmp->seq_nr.set_store_and_debug_gaps(i,m_options.debug_packet_gaps); - tmp->card_rssi.set_debug_invalid_rssi(m_options.debug_rssi>=1,0); - tmp->antenna1_rssi.set_debug_invalid_rssi(m_options.debug_rssi>=1,1); - tmp->antenna2_rssi.set_debug_invalid_rssi(m_options.debug_rssi>=1,2); - tmp->signal_quality.set_debug_invalid_signal_quality(m_options.debug_rssi>=1); + 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; } @@ -106,7 +105,7 @@ WBTxRx::~WBTxRx() { } } -void WBTxRx::tx_inject_packet(const uint8_t stream_index,const uint8_t* data, int data_len,const RadiotapHeader& tx_radiotap_header,bool encrypt) { +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); @@ -117,7 +116,7 @@ void WBTxRx::tx_inject_packet(const uint8_t stream_index,const uint8_t* data, in // new wifi packet const auto packet_size= // Radiotap header comes first - RadiotapHeader::SIZE_BYTES+ + RadiotapHeaderTx::SIZE_BYTES+ // Then the Ieee80211 header Ieee80211HeaderRaw::SIZE_BYTES+ // actual data @@ -126,7 +125,8 @@ void WBTxRx::tx_inject_packet(const uint8_t stream_index,const uint8_t* data, in crypto_aead_chacha20poly1305_ABYTES; uint8_t* packet_buff=m_tx_packet_buff.data(); // radiotap header comes first - memcpy(packet_buff, tx_radiotap_header.getData(), RadiotapHeader::SIZE_BYTES); + 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++; @@ -144,10 +144,10 @@ void WBTxRx::tx_inject_packet(const uint8_t stream_index,const uint8_t* data, in } //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+RadiotapHeader::SIZE_BYTES, + 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+RadiotapHeader::SIZE_BYTES+ Ieee80211HeaderRaw::SIZE_BYTES; + 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); @@ -343,13 +343,16 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx,const uint8_t *pkt,const int p m_console->debug("Got packet {} {}",wlan_idx,pkt_len); } const auto parsedPacket = - RadiotapHelper::process_received_radiotap_packet(pkt,pkt_len); + radiotap::rx::process_received_radiotap_packet(pkt,pkt_len); if (parsedPacket == std::nullopt) { if(m_options.advanced_debugging_rx){ - m_console->warn("Discarding packet due to pcap parsing error!"); + m_console->warn("Discarding packet due to radiotap parsing error!"); } 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++; @@ -358,7 +361,7 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx,const uint8_t *pkt,const int p if(wlan_idx==0){ m_pollution_total_rx_packets++; } - if (parsedPacket->frameFailedFCSCheck) { + if (parsedPacket->radiotap_f_bad_fcs) { if(m_options.advanced_debugging_rx){ m_console->debug("Discarding packet due to bad FCS!"); } @@ -436,15 +439,6 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx,const uint8_t *pkt,const int p } return; } - // Issue when using multiple wifi card(s) on ground - by example: - // When we inject data on card 1, it is intended for the "air unit" - however, - // card 2 on the ground likely picks up such a packet and if we were not to ignore it, we'd get the session key - // TODO make it better - - // for now, ignore session key packets not from card 0 - // Not needed anymore, due to unique air / ground id's - /*if(wlan_idx!=0){ - return ; - }*/ 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){ @@ -489,63 +483,25 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx,const uint8_t *pkt,const int p } 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("{}",all_rssi_to_string(parsedPacket->allAntennaValues)); - } - // assumes driver gives 1st and 2nd antenna as 2nd and 3rd value - if(parsedPacket->allAntennaValues.size()>=1){ - const auto rssi=parsedPacket->allAntennaValues[0].rssi; - auto opt_minmaxavg= this_wifi_card_calc.card_rssi.add_and_recalculate_if_needed(rssi); - if(opt_minmaxavg.has_value()){ - // See below for how this value is calculated on rtl8812au - if(m_wifi_cards[wlan_idx].type!=wifibroadcast::WIFI_CARD_TYPE_RTL8812AU){ - this_wifi_card_stats.card_dbm=opt_minmaxavg.value().avg; - } - if(m_options.debug_rssi>=1){ - m_console->debug("Card{}:{}",wlan_idx, RSSIAccumulator::min_max_avg_to_string(opt_minmaxavg.value(), false)); - } - } - } - if(parsedPacket->allAntennaValues.size()>=2){ - const auto rssi=parsedPacket->allAntennaValues[1].rssi; - auto opt_minmaxavg= this_wifi_card_calc.antenna1_rssi.add_and_recalculate_if_needed(rssi); - if(opt_minmaxavg.has_value()){ - this_wifi_card_stats.antenna1_dbm=opt_minmaxavg.value().avg; - if(m_options.debug_rssi>=1){ - m_console->debug("Card{} Antenna{}:{}",wlan_idx,0, RSSIAccumulator::min_max_avg_to_string(opt_minmaxavg.value(), false)); - } - } + m_console->debug("{}",radiotap::rx::all_rf_path_to_string(parsedPacket->rf_paths)); } - if(parsedPacket->allAntennaValues.size()>=3){ - const auto rssi=parsedPacket->allAntennaValues[2].rssi; - auto opt_minmaxavg= this_wifi_card_calc.antenna2_rssi.add_and_recalculate_if_needed(rssi); - if(opt_minmaxavg.has_value()){ - this_wifi_card_stats.antenna2_dbm=opt_minmaxavg.value().avg; - if(m_options.debug_rssi>=1){ - m_console->debug("Card{} Antenna{}:{}",wlan_idx,1, RSSIAccumulator::min_max_avg_to_string(opt_minmaxavg.value(), false)); - } - } - } - 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); + 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(parsedPacket->mcs_index.has_value()){ - m_rx_stats.last_received_packet_mcs_index=parsedPacket->mcs_index.value(); - } - if(parsedPacket->channel_width.has_value()){ - m_rx_stats.last_received_packet_channel_width=parsedPacket->channel_width.value(); - } - if(parsedPacket->signal_quality.has_value()){ - this_wifi_card_calc.signal_quality.add_signal_quality(parsedPacket->signal_quality.value()); - this_wifi_card_stats.signal_quality=this_wifi_card_calc.signal_quality.get_current_signal_quality(); - } if(wlan_idx==0){ m_pollution_openhd_rx_packets++; } @@ -565,28 +521,29 @@ void WBTxRx::switch_tx_card_if_needed() { 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(); - int idx_card_highest_rssi=0; - int highest_dbm=INT32_MIN; + // 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; - if(delta_valid_packets!=0){ - // Some valid packets on this card, or reset - const auto dbm_average=this_card_stats.card_dbm; - if(dbm_average>highest_dbm){ - idx_card_highest_rssi=i; - highest_dbm=static_cast(dbm_average); // NOLINT(cert-str34-c) - } + 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; } - //m_console->debug("Card {} dbm_average:{}",i,dbm_average); } - if(m_curr_tx_card!=idx_card_highest_rssi){ - // TODO - // to avoid switching too often, only switch if the difference in dBm exceeds a threshold value - m_console->debug("Switching to card {}",idx_card_highest_rssi); - m_curr_tx_card=idx_card_highest_rssi; + 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; } } } @@ -611,7 +568,8 @@ bool WBTxRx::process_received_data_packet(int wlan_idx,uint8_t stream_index,bool m_packet_decrypt_time.reset(); } } - on_valid_packet(nonce,wlan_idx,stream_index,decrypted->data(),decrypted->size()); + 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); @@ -638,7 +596,7 @@ bool WBTxRx::process_received_data_packet(int wlan_idx,uint8_t stream_index,bool return false; } -void WBTxRx::on_valid_packet(uint64_t nonce,int wlan_index,const uint8_t stream_index,const uint8_t *data, const int data_len) { +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); } @@ -677,7 +635,7 @@ void WBTxRx::announce_session_key_if_needed() { } void WBTxRx::send_session_key() { - RadiotapHeader tmp_radiotap_header= m_session_key_radiotap_header->thread_safe_get(); + 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); @@ -709,7 +667,6 @@ WBTxRx::TxStats WBTxRx::get_tx_stats() { WBTxRx::RxStats WBTxRx::get_rx_stats() { WBTxRx::RxStats ret=m_rx_stats; - ret.curr_big_gaps_counter=0; 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; @@ -783,26 +740,28 @@ std::string WBTxRx::tx_stats_to_string(const WBTxRx::TxStats& data) { 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:{}% likely_key_mismatch:{}]", + 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.likely_mismatching_encryption_key); + 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("Card{}[packets total:{} valid:{}, loss:{}% RSSI:{}/{},{}]",data.card_index, - data.count_p_any,data.count_p_valid,data.curr_packet_loss, - (int)data.card_dbm,data.antenna1_dbm,data.antenna2_dbm); + 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(); +} + void WBTxRx::PerCardCalculators::reset_all() { seq_nr.reset(); - card_rssi.reset(); - antenna1_rssi.reset(); - antenna2_rssi.reset(); - signal_quality.reset(); + rf_aggregator.reset(); } diff --git a/src/WBTxRx.h b/src/WBTxRx.h index 1aef01ed..ad748aca 100644 --- a/src/WBTxRx.h +++ b/src/WBTxRx.h @@ -16,14 +16,15 @@ #include #include "Encryption.h" -#include "Ieee80211Header.hpp" -#include "RSSIAccumulator.hpp" -#include "RadiotapHeader.hpp" -#include "RadiotapHeaderHolder.hpp" -#include "SignalQualityAccumulator.hpp" #include "HelperSources/UINT16SeqNrHelper.hpp" #include "HelperSources/UINT64SeqNrHelper.hpp" +#include "Ieee80211Header.hpp" #include "WiFiCard.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 @@ -96,13 +97,15 @@ class WBTxRx { bool tx_without_pcap=false; // a tx error hint is thrown if injecting the packet takes longer than max_sane_injection_time std::chrono::milliseconds max_sane_injection_time=std::chrono::milliseconds(5); + // debugging of rx radiotap header(s) + int rx_radiotap_debug_level=0; }; /** * @param wifi_cards card(s) used for tx / rx * @param options1 see documentation in options string * @param session_key_radiotap_header radiotap header used when injecting session key packets */ - explicit WBTxRx(std::vector wifi_cards,Options options1,std::shared_ptr session_key_radiotap_header); + explicit WBTxRx(std::vector wifi_cards,Options options1,std::shared_ptr session_key_radiotap_header); WBTxRx(const WBTxRx &) = delete; WBTxRx &operator=(const WBTxRx &) = delete; ~WBTxRx(); @@ -120,7 +123,7 @@ class WBTxRx { * @param encrypt: Optionally encrypt the packet, if not encrypted, only a (secure) validation checksum is calculated & checked on rx * Encryption results in more CPU load and is therefore not wanted in all cases (e.g. by default, openhd does not encrypt video) */ - void tx_inject_packet(uint8_t stream_index,const uint8_t* data,int data_len,const RadiotapHeader& tx_radiotap_header,bool encrypt); + void tx_inject_packet(uint8_t stream_index,const uint8_t* data,int data_len,const RadiotapHeaderTx& tx_radiotap_header,bool encrypt); /** * A typical stream RX (aka the receiver for a specific multiplexed stream) needs to react to events during streaming. * For lowest latency, we do this via callback(s) that are called directly. @@ -184,12 +187,6 @@ class WBTxRx { int32_t curr_bits_per_second=-1; // n received valid session key packets int n_received_valid_session_key_packets=0; - // mcs index on the most recent valid data packet, if the card supports reporting it - int last_received_packet_mcs_index=-1; - // channel width (20Mhz or 40Mhz) on the most recent received valid data packet, if the card supports reporting it - int last_received_packet_channel_width=-1; - // complicated but important metric in our case - how many "big gaps" we had in the last 1 second - int16_t curr_big_gaps_counter=-1; // Percentage of non openhd packets over total n of packets int curr_link_pollution_perc=0; // N of non openhd packets in the last second @@ -205,17 +202,11 @@ class WBTxRx { int64_t count_p_any=0; int64_t count_p_valid=0; int32_t curr_packet_loss=-1; - // [0,100] if valid, -1 otherwise - int8_t signal_quality=-1; - // These values are updated in regular intervals as long as packets are coming in - // -128 = invalid, [-127..-1] otherwise - int8_t card_dbm=-128; // Depends on driver - int8_t antenna1_dbm=-128; - int8_t antenna2_dbm=-128; }; TxStats get_tx_stats(); RxStats get_rx_stats(); RxStatsPerCard get_rx_stats_for_card(int card_index); + RadiotapRxRfAggregator::CardKeyRfIndicators get_rx_rf_stats_for_card(int card_index); // used by openhd during frequency scan void rx_reset_stats(); // used by the rate adjustment test executable @@ -243,7 +234,7 @@ class WBTxRx { // the reasoning behind this value: https://github.com/svpcom/wifibroadcast/issues/69 static constexpr const auto PCAP_MAX_PACKET_SIZE = 1510; // This is the max number of bytes usable when injecting - static constexpr const auto RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE = (PCAP_MAX_PACKET_SIZE - RadiotapHeader::SIZE_BYTES - + static constexpr const auto RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE = (PCAP_MAX_PACKET_SIZE - RadiotapHeaderTx::SIZE_BYTES - IEEE80211_HEADER_SIZE_BYTES); static_assert(RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE==1473); // and we use some bytes of that for encryption / packet validation @@ -255,7 +246,7 @@ class WBTxRx { private: const Options m_options; std::shared_ptr m_console; - std::shared_ptr m_session_key_radiotap_header; + std::shared_ptr m_session_key_radiotap_header; const std::vector m_wifi_cards; std::chrono::steady_clock::time_point m_session_key_next_announce_ts{}; Ieee80211HeaderOpenHD m_tx_ieee80211_hdr_openhd{}; @@ -302,10 +293,7 @@ class WBTxRx { // for calculating the loss and more per rx card (when multiple rx cards are used) struct PerCardCalculators{ UINT64SeqNrHelper seq_nr{}; - RSSIAccumulator card_rssi{}; - RSSIAccumulator antenna1_rssi{}; - RSSIAccumulator antenna2_rssi{}; - SignalQualityAccumulator signal_quality{}; + RadiotapRxRfAggregator rf_aggregator; void reset_all(); }; std::vector> m_per_card_calc; @@ -320,7 +308,7 @@ class WBTxRx { // We adjust the TX card in 1 second intervals std::chrono::steady_clock::time_point m_last_highest_rssi_adjustment_tp=std::chrono::steady_clock::now(); static constexpr auto HIGHEST_RSSI_ADJUSTMENT_INTERVAL=std::chrono::seconds(1); - bool m_disable_all_transmissions= false; + std::atomic_bool m_disable_all_transmissions= false; std::vector m_card_is_disconnected; BitrateCalculator m_tx_bitrate_calculator_excluding_overhead{}; BitrateCalculator m_tx_bitrate_calculator_including_overhead{}; @@ -351,7 +339,9 @@ class WBTxRx { // returns true if packet could be decrypted successfully bool process_received_data_packet(int wlan_idx,uint8_t stream_index,bool encrypted,uint64_t nonce,const uint8_t *pkt_payload,int pkt_payload_size); // called avery time we have successfully decrypted a packet - void on_valid_packet(uint64_t nonce,int wlan_index,uint8_t stream_index,const uint8_t *data,int data_len); + void on_valid_data_packet(uint64_t nonce,int wlan_index, + uint8_t stream_index,const uint8_t *data, + int data_len); static std::string options_to_string(const std::vector& wifi_cards,const Options& options); // Adjustment of which card is used for injecting packets in case there are multiple RX card(s) // (Of all cards currently receiving data, find the one with the highest reported dBm) diff --git a/src/legacy/WBStreamTxUDP.h b/src/legacy/WBStreamTxUDP.h index 58d11b03..e8aa4cb5 100644 --- a/src/legacy/WBStreamTxUDP.h +++ b/src/legacy/WBStreamTxUDP.h @@ -14,7 +14,7 @@ class WBStreamTxUDP{ public: WBStreamTxUDP(std::shared_ptr txrx,WBStreamTx::Options options,int fec_k,int in_udp_port){ - radiotap_header_holder=std::make_shared(); + radiotap_header_holder=std::make_shared(); wb_tx=std::make_unique(txrx,options,radiotap_header_holder); last_udp_in_packet_ts_ms=MyTimeHelper:: get_curr_time_ms(); // we need to buffer packets due to udp @@ -44,7 +44,7 @@ class WBStreamTxUDP{ } } std::unique_ptr wb_tx; - std::shared_ptr radiotap_header_holder; + std::shared_ptr radiotap_header_holder; std::unique_ptr m_udp_in; // helps to catch a common newbie mistake of forgetting that this buffers in packets int last_udp_in_packet_ts_ms; diff --git a/src/radiotap/README.md b/src/radiotap/README.md new file mode 100644 index 00000000..27768f2e --- /dev/null +++ b/src/radiotap/README.md @@ -0,0 +1,2 @@ +Code for dealing with creating radiotap header for injection / +parsing rx radiotap header (RF metrics). \ No newline at end of file diff --git a/src/RSSIAccumulator.hpp b/src/radiotap/RSSIAccumulator.hpp similarity index 96% rename from src/RSSIAccumulator.hpp rename to src/radiotap/RSSIAccumulator.hpp index da2c3b8c..1c35d952 100644 --- a/src/RSSIAccumulator.hpp +++ b/src/radiotap/RSSIAccumulator.hpp @@ -7,8 +7,9 @@ #include -#include "HelperSources/TimeHelper.hpp" -#include "wifibroadcast_spdlog.h" +#include "../wifibroadcast_spdlog.h" +#include "TimeHelper.hpp" +#include "spdlog/spdlog.h" /** * UINT16SeqNrHelper to accumulate RSSI values diff --git a/src/radiotap/RadiotapHeaderRx.hpp b/src/radiotap/RadiotapHeaderRx.hpp new file mode 100644 index 00000000..ac935133 --- /dev/null +++ b/src/radiotap/RadiotapHeaderRx.hpp @@ -0,0 +1,206 @@ +// +// 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< + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 !"); + +namespace radiotap::tx { + +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) { + uint32_t present = 0; + for (const auto &valueToBePresent: valuesToBePresent) { + present |= 1 << valueToBePresent; + } + return present; +} + +// http://www.radiotap.org/fields/MCS.html +struct MCS { + uint8_t known = 0; + uint8_t flags = 0; + uint8_t modulationIndex = 0; +}__attribute__ ((packed)); +} + +// To inject packets we need 2 radiotap fields: "TX flags" and the "MCS field" +struct RadiotapHeaderWithTxFlagsAndMCS { + uint8_t version = 0; + uint8_t padding = 0; + uint16_t length = 13; + // http://www.radiotap.org/ + 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 ! + radiotap::tx::MCS mcs{}; +}__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 +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. + int bandwidth = 20; + // I do not recommend using a short guard interval + bool short_gi = false; + // https://en.wikipedia.org/wiki/Space%E2%80%93time_block_code + int stbc = 0; + // https://en.wikipedia.org/wiki/Low-density_parity-check_code#:~:text=In%20information%20theory%2C%20a%20low,subclass%20of%20the%20bipartite%20graph). + bool ldpc = false; + // https://www.digitalairwireless.com/articles/blog/demystifying-modulation-and-coding-scheme-mcs-index-values + // https://mcsindex.com/ + int mcs_index = 3; + // depends on the driver + 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.bandwidth == 20 || params.bandwidth == 40)) { + 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)); + } + // 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}); + + // 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{ + 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); + // write the mcs index + radiotapHeaderData.mcs.modulationIndex = params.mcs_index; + + switch (params.bandwidth) { + case 20:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_20; + break; + case 40:radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_BW_40; + break; + default:assert(true); + } + + if (params.short_gi) { + radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_SGI; + } + + if (params.ldpc) { + radiotapHeaderData.mcs.flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; + } + + switch (params.stbc) { + 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); + break; + case 3:radiotapHeaderData.mcs.flags |= (IEEE80211_RADIOTAP_MCS_STBC_3 << IEEE80211_RADIOTAP_MCS_STBC_SHIFT); + break; + default:assert(true); + } + }; + const uint8_t *getData() const { + return (const uint8_t *) &radiotapHeaderData; + } + constexpr std::size_t getSize() const { + return SIZE_BYTES; + } + 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); + } + private: + RadiotapHeaderWithTxFlagsAndMCS radiotapHeaderData; +}__attribute__ ((packed)); +static_assert(sizeof(RadiotapHeaderTx) == RadiotapHeaderTx::SIZE_BYTES, "ALWAYS TRUE"); +static_assert(sizeof(RadiotapHeaderWithTxFlagsAndMCS) == + 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); + uint8_t *p = packet.data(); + // radiotap header + memcpy(p, radiotapHeader.getData(), radiotapHeader.getSize()); + p += radiotapHeader.getSize(); + // ieee80211 wbDataHeader + memcpy(p, &ieee80211Header.data, sizeof(ieee80211Header.data)); + p += sizeof(ieee80211Header.data); + memcpy(p, data, data_len); + p += data_len; + return packet; +} + +} + +#endif //__WIFIBROADCAST_RADIOTAP_HEADER_HPP__ \ No newline at end of file diff --git a/src/RadiotapHeaderHolder.hpp b/src/radiotap/RadiotapHeaderTxHolder.hpp similarity index 74% rename from src/RadiotapHeaderHolder.hpp rename to src/radiotap/RadiotapHeaderTxHolder.hpp index 2ecd5974..7b64d7d7 100644 --- a/src/RadiotapHeaderHolder.hpp +++ b/src/radiotap/RadiotapHeaderTxHolder.hpp @@ -5,30 +5,32 @@ #ifndef WIFIBROADCAST_RADIOTAPHEADERHOLDER_H #define WIFIBROADCAST_RADIOTAPHEADERHOLDER_H -#include "RadiotapHeader.hpp" #include -#include "wifibroadcast_spdlog.h" + +#include "../wifibroadcast_spdlog.h" +#include "RadiotapHeaderTx.hpp" /** - * Thread-safe holder for a radiotap header. - * ( getter / setter) + * Thread-safe holder for a (TX) radiotap header. + * ( getter / setter) - + * We modify the tx radiotap header in openhd at run time. * This kind of "atomic behaviour" is enough for openhd wifibroadcast. * TODO: Use std::atomic instead of std::mutex */ -class RadiotapHeaderHolder{ +class RadiotapHeaderTxHolder { public: - explicit RadiotapHeaderHolder(){ + explicit RadiotapHeaderTxHolder(){ m_console=wifibroadcast::log::get_default(); } - void thread_safe_set(RadiotapHeader::UserSelectableParams params){ - auto tmp=RadiotapHeader{params}; + void thread_safe_set(RadiotapHeaderTx::UserSelectableParams params){ + auto tmp= RadiotapHeaderTx{params}; thread_safe_set2(tmp); } - void thread_safe_set2(RadiotapHeader radiotap_header) { + void thread_safe_set2(RadiotapHeaderTx radiotap_header) { std::lock_guard guard(m_radiotap_header_mutex); m_radiotap_header = radiotap_header; } - RadiotapHeader thread_safe_get() { + RadiotapHeaderTx thread_safe_get() { std::lock_guard guard(m_radiotap_header_mutex); return m_radiotap_header; } @@ -66,8 +68,8 @@ class RadiotapHeaderHolder{ } private: std::shared_ptr m_console; - RadiotapHeader::UserSelectableParams m_radioTapHeaderParams{}; - RadiotapHeader m_radiotap_header{RadiotapHeader::UserSelectableParams{}}; + RadiotapHeaderTx::UserSelectableParams m_radioTapHeaderParams{}; + RadiotapHeaderTx m_radiotap_header{RadiotapHeaderTx::UserSelectableParams{}}; std::mutex m_radiotap_header_mutex; }; diff --git a/src/radiotap/RadiotapRxRfAggregator.cpp b/src/radiotap/RadiotapRxRfAggregator.cpp new file mode 100644 index 00000000..50601dfc --- /dev/null +++ b/src/radiotap/RadiotapRxRfAggregator.cpp @@ -0,0 +1,80 @@ +// +// 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=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/src/radiotap/RadiotapRxRfAggregator.h b/src/radiotap/RadiotapRxRfAggregator.h new file mode 100644 index 00000000..51155404 --- /dev/null +++ b/src/radiotap/RadiotapRxRfAggregator.h @@ -0,0 +1,67 @@ +// +// Created by consti10 on 05.10.23. +// + +#ifndef WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H +#define WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H + +#include "RadiotapHeaderRx.hpp" +#include "RSSIAccumulator.hpp" +#include "SignalQualityAccumulator.hpp" +#include + +/** + * Aggregates all key rf metrics. + */ +class RadiotapRxRfAggregator { + public: + // Aggregated (average) key rf metrics + struct AggKeyRfIndicators { + // -128 = invalid, [-127..-1] otherwise + int8_t rssi_dbm=-128; + int8_t noise_dbm=-128; + // [0,100] if valid, -1 otherwise + int8_t card_signal_quality_perc=-1; + }; + struct CardKeyRfIndicators { + // ------------- PER ADAPTER ------------ + AggKeyRfIndicators adapter; + // -------------- PER ANTENNA ---------- + AggKeyRfIndicators antenna1; + AggKeyRfIndicators antenna2; + }; + // Called every time a valid openhd packet is received + 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); + void debug_every_one_second(); + private: + struct KeyRfAggregators{ + RSSIAccumulator rssi_dbm; + RSSIAccumulator noise_dbm; + SignalQualityAccumulator signal_quality; + void reset(); + }; + static void add_if_valid(const radiotap::rx::KeyRfIndicators& indicators, + RadiotapRxRfAggregator::KeyRfAggregators& agg, + RadiotapRxRfAggregator::AggKeyRfIndicators& curr); + KeyRfAggregators m_agg_adapter; + 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(); +}; + +static std::ostream& operator<<(std::ostream& strm, const RadiotapRxRfAggregator::CardKeyRfIndicators& data){ + strm< -#include "HelperSources/TimeHelper.hpp" -#include "wifibroadcast_spdlog.h" +#include "../wifibroadcast_spdlog.h" +#include "TimeHelper.hpp" /** - * UINT16SeqNrHelper to accumulate (rtl8812au) signal quality values + * Helper to accumulate (rtl8812au) signal quality values - + * aka values that should always be in [0..100] range. */ class SignalQualityAccumulator{ public: diff --git a/src/radiotap/radiotap_util.hpp b/src/radiotap/radiotap_util.hpp new file mode 100644 index 00000000..925b6555 --- /dev/null +++ b/src/radiotap/radiotap_util.hpp @@ -0,0 +1,280 @@ +// +// Created by consti10 on 05.10.23. +// A lot of simple util code for debugging / printing +// + +#ifndef WIFIBROADCAST_RADIOTAP_UTIL_HPP +#define WIFIBROADCAST_RADIOTAP_UTIL_HPP + +#include "../wifibroadcast_spdlog.h" +#include "RadiotapHeaderTx.hpp" +namespace radiotap::util{ + +static std::string toStringRadiotapFlags(uint8_t flags) { + std::stringstream ss; + ss << "All IEEE80211_RADIOTAP flags: ["; + if (flags & IEEE80211_RADIOTAP_F_CFP) { + ss << "CFP,"; + } + if (flags & IEEE80211_RADIOTAP_F_SHORTPRE) { + ss << "SHORTPRE,"; + } + if (flags & IEEE80211_RADIOTAP_F_WEP) { + ss << "WEP,"; + } + if (flags & IEEE80211_RADIOTAP_F_FRAG) { + ss << "FRAG,"; + } + if (flags & IEEE80211_RADIOTAP_F_FCS) { + ss << "FCS,"; + } + if (flags & IEEE80211_RADIOTAP_F_DATAPAD) { + ss << "DATAPAD,"; + } + if (flags & IEEE80211_RADIOTAP_F_BADFCS) { + ss << "BADFCS"; + } + ss << "]"; + return ss.str(); +} +// http://www.radiotap.org/fields/Channel.html +static std::string toStringRadiotapChannel(uint16_t frequency, uint16_t flags) { + std::stringstream ss; + ss << "All Radiotap channel values: ["; + ss << "Frequency[" << (int) frequency << "],"; + if (flags & IEEE80211_CHAN_CCK) { + ss << "CHAN_CCK,"; + } + if (flags & IEEE80211_CHAN_OFDM) { + ss << "CHAN_OFDM,"; + } + if (flags & IEEE80211_CHAN_2GHZ) { + ss << "CHAN_2GHZ,"; + } + if (flags & IEEE80211_CHAN_5GHZ) { + ss << "CHAN_5GHZ,"; + } + if (flags & IEEE80211_CHAN_DYN) { + ss << "CHAN_DYN,"; + } + if (flags & IEEE80211_CHAN_HALF) { + ss << "CHAN_HALF,"; + } + if (flags & IEEE80211_CHAN_QUARTER) { + ss << "CHAN_QUARTER,"; + } + ss << "]"; + return ss.str(); +} +//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: ["; + if (rxFlags & IEEE80211_RADIOTAP_F_RX_BADPLCP) { + ss << "RX_BADPLCP,"; + } + ss << "]"; + return ss.str(); +} +// http://www.radiotap.org/fields/TX%20flags.html +static std::string toStringRadiotapTXFlags(const uint16_t txFlags) { + std::stringstream ss; + ss << "All TX FLAGS: ["; + if (txFlags & IEEE80211_RADIOTAP_F_TX_FAIL) { + ss << "TX_FAIL,"; + } + if (txFlags & IEEE80211_RADIOTAP_F_TX_CTS) { + ss << "TX_CTS,"; + } + if (txFlags & IEEE80211_RADIOTAP_F_TX_RTS) { + ss << "TX_RTS,"; + } + if (txFlags & IEEE80211_RADIOTAP_F_TX_NOACK) { + ss << "TX_NOACK,"; + } + ss << "]"; + return ss.str(); +} + +// http://www.radiotap.org/fields/MCS.html +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"; + break; + case IEEE80211_RADIOTAP_MCS_BW_40: ss << "BW_40"; + break; + case IEEE80211_RADIOTAP_MCS_BW_20L: ss << "BW_20L"; + break; + case IEEE80211_RADIOTAP_MCS_BW_20U: ss << "BW_20U"; + break; + default:ss << "Unknown"; + } + ss << "],"; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_MCS) { + ss << "HAVE_MCS[" << (int) mcs << "],"; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_GI) { + uint8_t gi = flags & IEEE80211_RADIOTAP_MCS_SGI; + ss << "HAVE_GI[" << (gi == 0 ? "long" : "short") << "],"; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_FMT) { + uint8_t fmt = flags & IEEE80211_RADIOTAP_MCS_FMT_GF; + ss << "HAVE_FMT[" << (fmt == 0 ? "mixed" : "greenfield") << "],"; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_FEC) { + uint8_t fec_type = flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC; + ss << "HAVE_FEC[" << (fec_type == 0 ? "BBC" : "LDPC") << "]"; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_STBC) { + uint8_t stbc = flags << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; + 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{}; + std::stringstream ss; + 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) + + 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) + + 8, // <-- RADIOTAP_F_TX_NOACK (0x08) + 0, // (0x00) + 55, // <-- bitmap (0x37) + 48, // <-- flags (0x30) + 0, // <-- mcs_index (0x00) +}; +} +#endif // WIFIBROADCAST_RADIOTAP_UTIL_HPP diff --git a/src/raw_socket_helper.hpp b/src/raw_socket_helper.hpp index 4c59485e..7ec1de01 100644 --- a/src/raw_socket_helper.hpp +++ b/src/raw_socket_helper.hpp @@ -59,7 +59,7 @@ static int open_wifi_interface_as_raw_socket(const std::string &wifi) { //const int wanted_sendbuff_bytes=128*1024*1024; //SocketHelper::set_socket_send_rcv_buffsize(sock,wanted_sendbuff_bytes, true); // buff size end - console->error("{} socket opened",wifi); + console->debug("{} socket opened",wifi); return sock; }