diff --git a/executables/example_hello.cpp b/executables/example_hello.cpp index 78857e39..fd749a63 100644 --- a/executables/example_hello.cpp +++ b/executables/example_hello.cpp @@ -20,8 +20,9 @@ */ int main(int argc, char *const *argv) { std::string card="wlxac9e17596103"; - bool pcap_setdirection= true; + bool advanced_debugging= false; bool is_air= false; + bool air_or_ground_explicitly_specified= false; int opt; while ((opt = getopt(argc, argv, "w:agd")) != -1) { switch (opt) { @@ -30,12 +31,14 @@ int main(int argc, char *const *argv) { break; case 'a': is_air= true; + air_or_ground_explicitly_specified= true; break ; case 'g': is_air= false; + air_or_ground_explicitly_specified= true; break ; case 'd': - pcap_setdirection= false; + advanced_debugging= true; break ; default: /* '?' */ show_usage: @@ -45,17 +48,22 @@ int main(int argc, char *const *argv) { exit(1); } } + if(!air_or_ground_explicitly_specified){ + std::cerr<<"Warning - please specify air or ground, air only talks to ground and vice versa"< cards{card}; WBTxRx::Options options_txrx{}; options_txrx.rtl8812au_rssi_fixup= true; - //options_txrx.set_direction= false; - options_txrx.set_direction= pcap_setdirection; - options_txrx.log_all_received_validated_packets= true; - // For easier debugging + options_txrx.set_direction= true; options_txrx.enable_encryption= false; + options_txrx.use_gnd_identifier=!is_air; + if(advanced_debugging){ + options_txrx.log_all_received_validated_packets= true; + options_txrx.advanced_debugging_rx= true; + } std::shared_ptr txrx=std::make_shared(cards,options_txrx); diff --git a/executables/example_udp.cpp b/executables/example_udp.cpp index 2f5527a9..68e4674a 100644 --- a/executables/example_udp.cpp +++ b/executables/example_udp.cpp @@ -34,6 +34,7 @@ int main(int argc, char *const *argv) { std::string card="wlxac9e17596103"; bool pcap_setdirection= true; bool is_air= false; + bool air_or_ground_explicitly_specified= false; bool enable_fec= false; int opt; while ((opt = getopt(argc, argv, "w:agdf")) != -1) { @@ -43,9 +44,11 @@ int main(int argc, char *const *argv) { break; case 'a': is_air= true; + air_or_ground_explicitly_specified= true; break ; case 'g': is_air= false; + air_or_ground_explicitly_specified= true; break ; case 'f': enable_fec= true; @@ -61,6 +64,9 @@ int main(int argc, char *const *argv) { exit(1); } } + if(!air_or_ground_explicitly_specified){ + std::cerr<<"Warning - please specify air or ground, air only talks to ground and vice versa"<info("Running as {} on card {}",(is_air ? "Air" : "Ground"),card); diff --git a/executables/unit_test.cpp b/executables/unit_test.cpp index 1b512bb0..0ee60f93 100644 --- a/executables/unit_test.cpp +++ b/executables/unit_test.cpp @@ -30,6 +30,7 @@ #include "../src/HelperSources/Helper.hpp" #include "../src/Encryption.hpp" #include "../src/wifibroadcast-spdlog.h" +#include "../src/Ieee80211Header.hpp" // Simple unit testing for the FEC lib that doesn't require wifi cards @@ -106,12 +107,16 @@ static void test_fec_stream_random_bs_fs_overhead_dropped(){ } -namespace TestEncryption { - -static void test(const bool useGeneratedFiles,bool message_signing_only) { +// Test encryption+packet validation and packet validation only +static void test_encrypt_decrypt_validate(const bool useGeneratedFiles,bool message_signing_only) { std::cout << "Using generated keypair (default seed otherwise):" << (useGeneratedFiles ? "y" : "n") << "\n"; std::optional encKey = useGeneratedFiles ? std::optional("gs.key") : std::nullopt; std::optional decKey = useGeneratedFiles ? std::optional("drone.key") : std::nullopt; + if(message_signing_only){ + std::cout<<"Testing message signing\n"; + }else{ + std::cout<<"Testing encryption & signing\n"; + } Encryptor encryptor{encKey,message_signing_only}; Decryptor decryptor{decKey,message_signing_only}; @@ -126,15 +131,31 @@ static void test(const bool useGeneratedFiles,bool message_signing_only) { assert( decryptor.onNewPacketSessionKeyData(sessionKeyPacket.sessionKeyNonce, sessionKeyPacket.sessionKeyData) == true); // now encrypt a couple of packets and decrypt them again afterwards - for (uint64_t nonce = 0; nonce < 20; nonce++) { + for (uint64_t nonce = 0; nonce < 200; nonce++) { const auto data = GenericHelper::createRandomDataBuffer(FEC_PACKET_MAX_PAYLOAD_SIZE); const auto encrypted=encryptor.encrypt3(nonce,data.data(),data.size()); - const auto decrypted = decryptor.decrypt3(nonce, encrypted->data(), encrypted->size()); - //assert(decrypted != std::nullopt); - assert(GenericHelper::compareVectors(data, *decrypted) == true); + { + // Correct usage - let packets through and get the original data back + const auto decrypted = decryptor.decrypt3(nonce, encrypted->data(), encrypted->size()); + assert(GenericHelper::compareVectors(data, *decrypted) == true); + } + { + // tamper with the nonce - shouldn't let packets through + const auto decrypted = decryptor.decrypt3(nonce+1, encrypted->data(), encrypted->size()); + assert(decrypted== nullptr); + } + { + // tamper with the encryption suffix - shouldn't let data through + auto encrypted_wrong_sing=encrypted; + encrypted_wrong_sing->at(encrypted_wrong_sing->size()-1)=0; + encrypted_wrong_sing->at(encrypted_wrong_sing->size()-2)=0; + const auto decrypted = decryptor.decrypt3(nonce, encrypted_wrong_sing->data(), encrypted_wrong_sing->size()); + assert(decrypted== nullptr); + } + } - // and make sure we don't let invalid packets thrugh - for (uint64_t nonce = 0; nonce < 20; nonce++) { + // and make sure we don't let packets with an invalid signing suffix through + for (uint64_t nonce = 0; nonce < 200; nonce++) { const auto data = GenericHelper::createRandomDataBuffer(FEC_PACKET_MAX_PAYLOAD_SIZE); const auto enrypted_wrong_sign=std::make_shared>(); enrypted_wrong_sign->resize(data.size()+ENCRYPTION_ADDITIONAL_VALIDATION_DATA); @@ -144,7 +165,7 @@ static void test(const bool useGeneratedFiles,bool message_signing_only) { } std::cout << "encryption test passed\n"; } -} + int main(int argc, char *argv[]) { std::cout << "Tests for Wifibroadcast\n"; @@ -164,6 +185,7 @@ int main(int argc, char *argv[]) { } } print_optimization_method(); + test::test_nonce(); try { if (test_mode == 0 || test_mode == 1) { @@ -177,10 +199,9 @@ int main(int argc, char *argv[]) { } if (test_mode == 0 || test_mode == 2) { std::cout << "Testing Encryption"< create_onetimeauth_subkey(const uint64_t nonce,const std::array session_key){ + // sub-key for this packet + std::array subkey{}; + 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; +} + class Encryptor { public: /** @@ -54,7 +65,11 @@ class Encryptor { fclose(fp); } } - // Don't forget to send the session key after creating a new one ! + /** + * Creates a new session key, simply put, the data we can send publicly + * @param sessionKeyNonce filled with public nonce + * @param sessionKeyData filled with public data + */ void makeNewSessionKey(std::array &sessionKeyNonce, std::array &sessionKeyData) { @@ -76,7 +91,8 @@ class Encryptor { if(DISABLE_ENCRYPTION_FOR_PERFORMANCE){ memcpy(dest,src, src_len); uint8_t* sign=dest+src_len; - crypto_onetimeauth(sign,src,src_len,session_key.data()); + const auto sub_key=create_onetimeauth_subkey(nonce,session_key); + crypto_onetimeauth(sign,src,src_len,sub_key.data()); return src_len+crypto_onetimeauth_BYTES; } long long unsigned int ciphertext_len; @@ -171,7 +187,8 @@ class Decryptor { 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 int res=crypto_onetimeauth_verify(sign,encrypted,payload_size,session_key.data()); + const auto sub_key=create_onetimeauth_subkey(nonce,session_key); + const int res=crypto_onetimeauth_verify(sign,encrypted,payload_size,sub_key.data()); if(res!=-1){ memcpy(dest,encrypted,payload_size); return true; diff --git a/src/FECDisabled.hpp b/src/FECDisabled.hpp index 6f8efea7..230e5ff5 100644 --- a/src/FECDisabled.hpp +++ b/src/FECDisabled.hpp @@ -32,7 +32,7 @@ struct FECDisabledHeader{ }__attribute__ ((packed)); static_assert(sizeof(FECDisabledHeader)==8); -// usage of nonce: Simple, uint64_t number increasing with each packet +// Really simple, adds a sequence number, nothing else class FECDisabledEncoder { public: typedef std::function diff --git a/src/HelperSources/SeqNrHelper.hpp b/src/HelperSources/SeqNrHelper.hpp index bcc9aa4c..cada0d01 100644 --- a/src/HelperSources/SeqNrHelper.hpp +++ b/src/HelperSources/SeqNrHelper.hpp @@ -13,7 +13,7 @@ namespace seq_nr{ 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"); + wifibroadcast::log::get_default()->debug("Duplicate in seq nr {}-{}, invalid usage",last_packet,curr_packet); } if(curr_packet static std::string arrayAsString(const std::array &a) { diff --git a/src/Ieee80211Header.hpp b/src/Ieee80211Header.hpp index 946b7ef1..50fbda97 100644 --- a/src/Ieee80211Header.hpp +++ b/src/Ieee80211Header.hpp @@ -18,12 +18,141 @@ #include #include +// Helper 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; + +// Helper 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); + +// Helper 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 src 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)); + } + // 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 Ieee80211Header { +class Ieee80211HeaderRaw { public: static constexpr auto SIZE_BYTES = 24; //the last byte of the mac address is recycled as a port number @@ -41,113 +170,20 @@ class Ieee80211Header { 0x00, 0x00, // iee80211 sequence control ( 2 bytes ) }; // default constructor - Ieee80211Header() = default; - // write the port re-using the MAC address (which is unused for broadcast) - // write sequence number (not used on rx right now) - void writeParams(const uint8_t radioPort, const uint16_t seqenceNumber) { - write_radio_port(radioPort); - write_ieee80211_seq_nr(seqenceNumber); - } - void write_ieee80211_seq_nr(const uint16_t seq_nr){ - data[FRAME_SEQ_LB] = seq_nr & 0xff; - data[FRAME_SEQ_HB] = (seq_nr >> 8) & 0xff; - } - void write_radio_port(const uint8_t radioPort){ - data[SRC_MAC_LASTBYTE] = radioPort; - data[DST_MAC_LASTBYTE] = radioPort; - } - // Except the last byte (radio port) the mac has to match the openhd default - bool has_valid_openhd_src_mac()const{ - return data[10]==0x13 && data[11]==0x22 && data[12]==0x33 && data[13]==0x44 && data[14]==0x55; //data[15]==radio port - } - bool has_valid_openhd_dst_mac()const{ - return data[16]==0x13 && data[17]==0x22 && data[18]==0x33 && data[19]==0x44 && data[20]==0x55; //data[21]==radio port - } - uint8_t get_src_mac_radio_port() const { - return data[SRC_MAC_LASTBYTE]; - } - uint8_t get_dst_mac_radio_port()const{ - return data[DST_MAC_LASTBYTE]; - } - uint16_t getSequenceNumber() const { - uint16_t ret; - memcpy(&ret, &data[FRAME_SEQ_LB], sizeof(uint16_t)); - return ret; - } - const uint8_t *getData() const { - return data.data(); - } - constexpr std::size_t getSize() const { - return data.size(); - } - uint16_t getFrameControl() const { - uint16_t ret; - memcpy(&ret, &data[0], 2); - return ret; - } - uint16_t getDurationOrConnectionId() const { - uint16_t ret; - memcpy(&ret, &data[2], 2); - return ret; - } - bool isDataFrame() const { - return data[0] == 0x08 && data[1] == 0x01; - } - //https://witestlab.poly.edu/blog/802-11-wireless-lan-2/ - //Sequence Control: Contains a 4-bit fragment number subfield, used for frag- mentation 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; - }__attribute__ ((packed)); - static_assert(sizeof(SequenceControl) == 2); - void setSequenceControl(const SequenceControl &sequenceControl) { - memcpy(&data[FRAME_SEQ_LB], (void *) &sequenceControl, sizeof(SequenceControl)); - }; - [[nodiscard]] SequenceControl getSequenceControl() const { - SequenceControl ret{}; - memcpy(&ret, &data[FRAME_SEQ_LB], sizeof(SequenceControl)); - return ret; - } - void printSequenceControl() const { - const auto tmp = getSequenceControl(); - std::cout << "SequenceControl subfield:" << (int) tmp.subfield << " sequenceNr:" << (int) tmp.sequence_nr << "\n"; - } - + 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<> 8) & 0xff; - // now print it - ieee80211Header.printSequenceControl(); - seqenceNumber += 16; - } -} - -// Unfortunately / luckily the sequence number is overwritten by the TX. This means we can't get -// lost packets per stream, but rather lost packets per all streams only -class Ieee80211HeaderSeqNrCounter { - public: - void onNewPacket(const Ieee80211Header &ieee80211Header) { - const auto seqCtrl = ieee80211Header.getSequenceControl(); - if (lastSeqNr == -1) { - lastSeqNr = seqCtrl.sequence_nr; - countPacketsOutOfOrder = 0; - return; - } - const int32_t delta = seqCtrl.sequence_nr - lastSeqNr; - std::cout << "Delta: " << delta << "\n"; - lastSeqNr = seqCtrl.sequence_nr; - } - private: - int64_t lastSeqNr = -1; - int countPacketsOutOfOrder = 0; -}; +static_assert(sizeof(Ieee80211HeaderRaw) == Ieee80211HeaderRaw::SIZE_BYTES, "ALWAYS TRUE"); namespace Ieee80211ControllFrames { static uint8_t u8aIeeeHeader_rts[] = { @@ -182,4 +218,22 @@ static uint8_t u8aIeeeHeader_rts[] = { 0xff // 1st byte of MAC will be overwritten with encoded port }; } + +namespace test{ +static void test_nonce(){ + Ieee80211HeaderOpenHD tmp{}; + assert(tmp.is_data_frame()); + for(uint64_t nonce=0;nonce<10;nonce++){ + tmp.write_nonce(nonce); + assert(tmp.get_nonce()==nonce); + } +} +static void test_sequence_number(){ + +} + +} + +// Everything in here assumes little endian +static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, "This code is written for little endian only !"); #endif \ No newline at end of file diff --git a/src/WBTxRx.cpp b/src/WBTxRx.cpp index 93d5d2d9..a5a80315 100644 --- a/src/WBTxRx.cpp +++ b/src/WBTxRx.cpp @@ -12,7 +12,7 @@ WBTxRx::WBTxRx(std::vector wifi_cards,Options options1) : m_options(options1), m_wifi_cards(std::move(wifi_cards)), - m_radiotap_header(RadiotapHeader::UserSelectableParams{}) + m_tx_radiotap_header(RadiotapHeader::UserSelectableParams{}) { assert(!m_wifi_cards.empty()); m_console=wifibroadcast::log::create_or_get("WBTxRx"); @@ -35,9 +35,11 @@ WBTxRx::WBTxRx(std::vector wifi_cards,Options options1) auto wifi_card=m_wifi_cards[i]; PcapTxRx pcapTxRx{}; pcapTxRx.rx=wifibroadcast::pcap_helper::open_pcap_rx(wifi_card); - //pcapTxRx.tx=wifibroadcast::pcap_helper::open_pcap_tx(wifi_card); + //pcapTxRx.tx=pcapTxRx.rx; + pcapTxRx.tx=wifibroadcast::pcap_helper::open_pcap_tx(wifi_card); if(m_options.set_direction){ - pcap_setdirection(pcapTxRx.rx, PCAP_D_IN); + const auto ret=pcap_setdirection(pcapTxRx.rx, PCAP_D_IN); + m_console->debug("pcap_setdirection() returned {}",ret); } m_pcap_handles.push_back(pcapTxRx); auto fd = pcap_get_selectable_fd(pcapTxRx.rx); @@ -59,7 +61,15 @@ WBTxRx::~WBTxRx() { close(fd.fd); } for(auto& pcapTxRx:m_pcap_handles){ - pcap_close(pcapTxRx.rx); + if(pcapTxRx.rx==pcapTxRx.tx){ + pcap_close(pcapTxRx.rx); + pcapTxRx.rx= nullptr; + pcapTxRx.tx= nullptr; + }else{ + pcap_close(pcapTxRx.rx); + pcap_close(pcapTxRx.tx); + } + //pcap_close(pcapTxRx.rx); //pcap_close(pcapTxRx.tx); } } @@ -77,9 +87,7 @@ void WBTxRx::tx_inject_packet(const uint8_t radioPort, // Radiotap header comes first RadiotapHeader::SIZE_BYTES+ // Then the Ieee80211 header - Ieee80211Header::SIZE_BYTES+ - // after that, the nonce (sequence number) - sizeof(uint64_t)+ + Ieee80211HeaderRaw::SIZE_BYTES+ // actual data data_len+ // encryption suffix @@ -87,30 +95,40 @@ void WBTxRx::tx_inject_packet(const uint8_t radioPort, std::vector packet = std::vector(packet_size); uint8_t* packet_buff=packet.data(); // radiotap header comes first - memcpy(packet_buff, m_radiotap_header.getData(), RadiotapHeader::SIZE_BYTES); + memcpy(packet_buff, m_tx_radiotap_header.getData(), RadiotapHeader::SIZE_BYTES); // Iee80211 header comes next - mIeee80211Header.writeParams(radioPort,m_ieee80211_seq); - memcpy(packet_buff+RadiotapHeader::SIZE_BYTES,mIeee80211Header.getData(),Ieee80211Header::SIZE_BYTES); - m_ieee80211_seq++; - // create a new nonce - uint64_t nonce=++m_nonce; - // copy over the nonce and fill with the rest of the packet with the encrypted data - memcpy(packet_buff+RadiotapHeader::SIZE_BYTES+Ieee80211Header::SIZE_BYTES,(uint8_t*)&nonce,sizeof(uint64_t)); - uint8_t* encrypted_data_p=packet_buff+RadiotapHeader::SIZE_BYTES+Ieee80211Header::SIZE_BYTES+sizeof(uint64_t); - const auto ciphertext_len=m_encryptor->encrypt2(m_nonce,data,data_len,encrypted_data_p); + // 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++; + m_tx_ieee80211_hdr_openhd.write_radio_port_src_dst(radioPort); + 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); + //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, + (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; + const auto ciphertext_len=m_encryptor->encrypt2(this_packet_nonce,data,data_len,encrypted_data_p); // 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); // inject via pcap // we inject the packet on whatever card has the highest rx rssi right now - pcap_t *tx= m_pcap_handles[m_curr_tx_card].rx; + pcap_t *tx= m_pcap_handles[m_curr_tx_card].tx; const auto before_injection = std::chrono::steady_clock::now(); - //const auto len_injected=pcap_inject(tx, packet.data(), packet.size()); - const auto len_injected=write(m_receive_pollfds.at(0).fd,packet.data(),packet.size()); + const auto len_injected=pcap_inject(tx, packet.data(), packet.size()); + //const auto len_injected=write(m_receive_pollfds.at(0).fd,packet.data(),packet.size()); const auto delta_inject=std::chrono::steady_clock::now()-before_injection; if(delta_inject>=MAX_SANE_INJECTION_TIME){ m_tx_stats.count_tx_injections_error_hint++; } + if(m_options.advanced_debugging_tx){ + 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 ?! m_console->warn("pcap -unable to inject packet size:{} ret:{} err:[{}]",packet.size(),len_injected, pcap_geterr(tx)); @@ -230,6 +248,9 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr, 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++; + } if (parsedPacket->frameFailedFCSCheck) { if(m_options.advanced_debugging_rx){ @@ -237,38 +258,62 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr, } return; } - if (!parsedPacket->ieee80211Header->isDataFrame()) { + 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 {}",(int) parsedPacket->ieee80211Header->getFrameControl()); + 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) { + 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; } - const auto radio_port_src_mac=parsedPacket->ieee80211Header->get_src_mac_radio_port(); - const auto radio_port_dst_mac=parsedPacket->ieee80211Header->get_dst_mac_radio_port(); - if(radio_port_src_mac!=radio_port_dst_mac){ - // cannot be an openhd packet (probably someone elses normal wifi packet + if(!rx_iee80211_hdr_openhd.has_valid_air_gnd_id()){ if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet with mismatching radio port {}-{]",radio_port_src_mac,radio_port_dst_mac); + 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 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(!(parsedPacket->ieee80211Header->has_valid_openhd_src_mac()&&parsedPacket->ieee80211Header->has_valid_openhd_dst_mac())){ + if(!rx_iee80211_hdr_openhd.has_valid_radio_port()){ if(m_options.advanced_debugging_rx){ - m_console->debug("Got packet with invalid src / dst mac"); + m_console->debug("Got packet that has not a valid radio port{}",rx_iee80211_hdr_openhd.debug_radio_ports()); } - return ; + return; } - const auto radio_port=radio_port_src_mac; + const auto radio_port=rx_iee80211_hdr_openhd.get_valid_radio_port(); + const auto nonce=rx_iee80211_hdr_openhd.get_nonce(); + // Quite likely an openhd packet (I'd say pretty much 100%) but not validated yet + m_rx_stats.curr_n_likely_openhd_packets++; if(radio_port==RADIO_PORT_SESSION_KEY_PACKETS){ if (pkt_payload_size != sizeof(SessionKeyPacket)) { if(m_options.advanced_debugging_rx){ @@ -294,17 +339,21 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr, opt_cb_session(); } } + if(wlan_idx==0){ + m_pollution_openhd_rx_packets++; + recalculate_pollution_perc(); + } } }else{ - // the payload needs to include at least the nonce, the encryption suffix and 1 byte of actual payload - static constexpr auto MIN_PACKET_PAYLOAD_SIZE=sizeof(uint64_t)+crypto_aead_chacha20poly1305_ABYTES+1; + // 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,pkt_payload,pkt_payload_size); + const bool valid=process_received_data_packet(wlan_idx,radio_port,nonce,pkt_payload,pkt_payload_size); if(valid){ m_rx_stats.count_p_valid++; m_rx_stats.count_bytes_valid+=pkt_payload_size; @@ -328,10 +377,14 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr, //m_console->debug("Signal quality: {}",parsedPacket->signal_quality.value()); this_wifi_card_stats.signal_quality=parsedPacket->signal_quality.value(); } + if(wlan_idx==0){ + m_pollution_openhd_rx_packets++; + recalculate_pollution_perc(); + } { // 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); + //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()); } // Adjustment of which card is used for injecting packets in case there are multiple RX card(s) @@ -363,19 +416,16 @@ void WBTxRx::on_new_packet(const uint8_t wlan_idx, const pcap_pkthdr &hdr, } } -bool WBTxRx::process_received_data_packet(int wlan_idx,uint8_t radio_port,const uint8_t *pkt_payload,const size_t pkt_payload_size) { - std::shared_ptr> decrypted=std::make_shared>(pkt_payload_size-sizeof(uint64_t)-crypto_aead_chacha20poly1305_ABYTES); - // nonce comes first - auto* nonce_p=(uint64_t*) pkt_payload; - uint64_t nonce=*nonce_p; +bool WBTxRx::process_received_data_packet(int wlan_idx,uint8_t radio_port,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=pkt_payload+sizeof(uint64_t); - const auto encrypted_data_with_suffix_len = pkt_payload_size-sizeof(uint64_t); + 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 res=m_decryptor->decrypt2(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:{} radio_port:{} size:{}",nonce,wlan_idx,radio_port,pkt_payload_size); + m_console->debug("Got valid packet nonce:{} wlan_idx:{} radio_port:{} size:{}",nonce,wlan_idx,radio_port,payload_and_enc_suffix_size); } on_valid_packet(nonce,wlan_idx,radio_port,decrypted->data(),decrypted->size()); if(wlan_idx==0){ @@ -433,10 +483,17 @@ void WBTxRx::announce_session_key_if_needed() { } void WBTxRx::send_session_key() { - RadiotapHeader tmp_radiotap_header=m_radiotap_header; - Ieee80211Header tmp_ieee_hdr=mIeee80211Header; - tmp_ieee_hdr.writeParams(RADIO_PORT_SESSION_KEY_PACKETS,0); - auto packet=wifibroadcast::pcap_helper::create_radiotap_wifi_packet(tmp_radiotap_header,tmp_ieee_hdr, + RadiotapHeader tmp_radiotap_header= m_tx_radiotap_header; + /*Ieee80211HeaderRaw tmp_ieee_hdr= m_tx_ieee80211_header; + tmp_ieee_hdr.writeParams(RADIO_PORT_SESSION_KEY_PACKETS,0);*/ + 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); + tmp_tx_hdr.write_radio_port_src_dst(RADIO_PORT_SESSION_KEY_PACKETS); + tmp_tx_hdr.write_ieee80211_seq_nr(m_ieee80211_seq++); + tmp_tx_hdr.write_nonce(m_nonce++); + + auto packet=wifibroadcast::pcap_helper::create_radiotap_wifi_packet(tmp_radiotap_header,*(Ieee80211HeaderRaw*)&tmp_tx_hdr, (uint8_t *)&m_tx_sess_key_packet, sizeof(SessionKeyPacket)); // 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 ! @@ -485,7 +542,7 @@ void WBTxRx::tx_update_ldpc(bool ldpc) { void WBTxRx::tx_threadsafe_update_radiotap_header(const RadiotapHeader::UserSelectableParams ¶ms) { auto newRadioTapHeader=RadiotapHeader{params}; std::lock_guard guard(m_tx_mutex); - m_radiotap_header = newRadioTapHeader; + m_tx_radiotap_header = newRadioTapHeader; } WBTxRx::TxStats WBTxRx::get_tx_stats() { @@ -539,9 +596,10 @@ 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:{} decrypted:{} Loss:{} pps:{} bps:{}]", + return fmt::format("RxStats[packets any:{} session:{} decrypted:{} Loss:{}% pps:{} bps:{} foreign:{}%]", data.count_p_any,data.n_received_valid_session_key_packets,data.count_p_valid, - data.curr_packet_loss,data.curr_packets_per_second,data.curr_bits_per_second); + data.curr_packet_loss,data.curr_packets_per_second,data.curr_bits_per_second, + data.curr_link_pollution_perc); } void WBTxRx::tx_reset_stats() { @@ -550,3 +608,23 @@ void WBTxRx::tx_reset_stats() { 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 ; + } + if(m_pollution_total_rx_packets<=0 || m_pollution_openhd_rx_packets<=0 ){ + 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() + } + m_pollution_total_rx_packets=0; + m_pollution_openhd_rx_packets=0; +} diff --git a/src/WBTxRx.h b/src/WBTxRx.h index e13662b7..d45a5a9c 100644 --- a/src/WBTxRx.h +++ b/src/WBTxRx.h @@ -20,19 +20,22 @@ #include "TimeHelper.hpp" /** - * Wraps one or more wifi card in monitor mode - * Provides easy interface to inject data packets and register a callback to - * process received data packets. - * Adds packet encryption and authentication via libsodium (can be disabled for - * performance) Allows multiplexing of multiple data streams (radio_port) Quick - * usage description by example: - * # System 1: card 1 - * # System 2: card 2 - * air in between card 1 and card 2 - * Create an instance of WBTxRx on both system 1 - * and system 2 inject packets using WBTxRx on system 1 -> receive them - * using WBTxRx on system 2 inject packets using WBTxRx on system 2 - * -> receive them using WBTxRx on system 1 + * This class exists to provide a clean, working interface to create a broadcast-like + * bidirectional wifi link between an fpv air and (one or more) ground unit(s). + * It hides away some nasty driver quirks, and offers + * 1) A lot of usefully stats like packet loss, dbm, ... + * 2) Multiplexing (radio_port) - multiple streams from air to ground / ground to air are possible + * 3) Packet validation / encryption + * 4) Multiple RX-cards (only one active tx at a time though) + * Packets sent by an "air unit" are received by any listening ground unit (broadcast) that uses the same (encryption/validation) key-pair + * Packets sent by an "ground unit" are received by any listening air unit (broadcast) that uses the same (encryption/validation) key-pair + * Packets sent by an "air unit" are never received by another air unit (and reverse for ground unit) + * (This is necessary due to AR9271 driver quirk - it gives injected packets back on the cb for received packets) + * + * It adds a minimal overhead of 16 bytes per data packet for validation / encryption + * And - configurable - a couple of packets per second for the session key. + * + * See example_hello for how to use this class. * * NOTE: Receiving of data is not started until startReceiving() is called ! */ @@ -44,7 +47,7 @@ class WBTxRx { std::optional encryption_key = std::nullopt; // dirty, rssi on rtl8812au is "bugged", this discards the first rssi value reported by the card. bool rtl8812au_rssi_fixup=false; - // TODO + // on the rx pcap fd, set direction PCAP_D_IN (aka only packets received by the card) - doesn't work on AR9271 bool set_direction= true; // thy spam the console, but usefully for debugging // log all received packets (regardless where they are from) @@ -67,6 +70,8 @@ class WBTxRx { // enable encryption, by default, only packet validation (without encryption) is done since // encryption needs a lot of CPU processing. bool enable_encryption= false; + // You need to set this to air / gnd on the air / gnd unit since AR9271 has a bug where it reports injected packets as received packets + bool use_gnd_identifier= false; }; explicit WBTxRx(std::vector wifi_cards,Options options1); WBTxRx(const WBTxRx &) = delete; @@ -161,6 +166,11 @@ class WBTxRx { 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; + // Usefully for channel scan - n packets that are quite likely coming from an openhd air / ground unit (respective depending on if air/gnd mode) + // But not validated - e.g. on a channel scan, session key packet(s) have not been received yet + int curr_n_likely_openhd_packets=0; }; struct RxStatsPerCard{ RSSIForWifiCard rssi_for_wifi_card{}; @@ -193,11 +203,12 @@ 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 - Ieee80211Header::SIZE_BYTES); + static constexpr const auto RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE = (PCAP_MAX_PACKET_SIZE - RadiotapHeader::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 - static constexpr const auto MAX_PACKET_PAYLOAD_SIZE=RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE-sizeof(uint64_t)-crypto_aead_chacha20poly1305_ABYTES; - static_assert(MAX_PACKET_PAYLOAD_SIZE==1449); + static constexpr const auto MAX_PACKET_PAYLOAD_SIZE=RAW_WIFI_FRAME_MAX_PAYLOAD_SIZE-crypto_aead_chacha20poly1305_ABYTES; + static_assert(MAX_PACKET_PAYLOAD_SIZE==1457); static std::string tx_stats_to_string(const TxStats& data); static std::string rx_stats_to_string(const RxStats& data); private: @@ -206,8 +217,8 @@ class WBTxRx { std::vector m_wifi_cards; std::chrono::steady_clock::time_point m_session_key_next_announce_ts{}; RadiotapHeader::UserSelectableParams m_radioTapHeaderParams{}; - RadiotapHeader m_radiotap_header; - Ieee80211Header mIeee80211Header{}; + RadiotapHeader m_tx_radiotap_header; + Ieee80211HeaderOpenHD m_tx_ieee80211_hdr_openhd{}; uint16_t m_ieee80211_seq = 0; uint64_t m_nonce=0; // For multiple RX cards the card with the highest rx rssi is used to inject packets on @@ -270,9 +281,17 @@ class WBTxRx { void on_new_packet(uint8_t wlan_idx, const pcap_pkthdr &hdr, const uint8_t *pkt); // verify and decrypt the packet if possible // returns true if packet could be decrypted successfully - bool process_received_data_packet(int wlan_idx,uint8_t radio_port,const uint8_t *pkt_payload,size_t pkt_payload_size); + bool process_received_data_packet(int wlan_idx,uint8_t radio_port,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 radioPort,const uint8_t *data, std::size_t data_len); + private: + // These are 'extra' for calculating some channel pollution value + //uint32_t m_pollution_non_openhd_packets=0; + //uint32_t m_pollution_openhd_packets=0; + uint32_t m_pollution_total_rx_packets=0; + uint32_t m_pollution_openhd_rx_packets=0; + std::chrono::steady_clock::time_point m_last_pollution_calculation=std::chrono::steady_clock::now(); + void recalculate_pollution_perc(); }; static std::ostream& operator<<(std::ostream& strm, const WBTxRx::TxStats& data){ diff --git a/src/pcap_helper.hpp b/src/pcap_helper.hpp index 61af2d66..b3266632 100644 --- a/src/pcap_helper.hpp +++ b/src/pcap_helper.hpp @@ -101,7 +101,7 @@ struct RssiForAntenna { struct ParsedRxPcapPacket { // 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 Ieee80211Header *ieee80211Header; + 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) @@ -258,9 +258,9 @@ static std::optional processReceivedPcapPacket(const pcap_pk pkt += iterator._max_length; pktlen -= iterator._max_length; // - const Ieee80211Header *ieee80211Header = (Ieee80211Header *) pkt; - const uint8_t *payload = pkt + Ieee80211Header::SIZE_BYTES; - const std::size_t payloadSize = (std::size_t) pktlen - Ieee80211Header::SIZE_BYTES; + 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:"; @@ -275,18 +275,18 @@ static std::optional processReceivedPcapPacket(const pcap_pk return ParsedRxPcapPacket{allAntennaValues, ieee80211Header, payload, payloadSize, frameFailedFcsCheck,mcs_index,channel_width,signal_quality}; } -// [RadiotapHeader | Ieee80211Header | customHeader (if not size 0) | payload (if not size 0)] +// [RadiotapHeader | Ieee80211HeaderRaw | customHeader (if not size 0) | payload (if not size 0)] static std::vector create_radiotap_wifi_packet(const RadiotapHeader& radiotapHeader, - const Ieee80211Header &ieee80211Header, + const Ieee80211HeaderRaw &ieee80211Header, const uint8_t* data,int data_len){ - std::vector packet(radiotapHeader.getSize() + ieee80211Header.getSize() + 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.getData(), ieee80211Header.getSize()); - p += ieee80211Header.getSize(); + memcpy(p, &ieee80211Header.data, sizeof(ieee80211Header.data)); + p += sizeof(ieee80211Header.data); memcpy(p, data, data_len); p += data_len; return packet;