From f628479edbf356608d9b135f6505d710880079e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Hut=C3=A1r?= Date: Thu, 15 Feb 2024 15:53:57 +0100 Subject: [PATCH] add udp socket support --- .env | 1 + Makefile | 7 +- include/Arguments.h | 98 ++++++++++ include/Csi.h | 4 +- include/MainController.h | 17 +- include/PacketInjector.h | 4 +- include/UdpSocket.h | 39 ++++ include/WiFiCsiController.h | 3 +- include/main.h | 36 +--- readme.md | 2 +- src/Arguments.cpp | 273 ++++++++++++++++++++++++++++ src/Csi.cpp | 68 ++----- src/CsiProcessor.cpp | 15 +- src/MainController.cpp | 70 +++++--- src/PacketInjector.cpp | 93 +++++----- src/UdpSocket.cpp | 152 ++++++++++++++++ src/WiFIController.cpp | 9 +- src/WiFiCsiController.cpp | 84 +++++++-- src/gui/CsiProcessingWindow.cpp | 15 +- src/gui/GnuPlot.cpp | 8 +- src/gui/MainWindow.cpp | 34 ++-- src/main.cpp | 305 ++------------------------------ 22 files changed, 828 insertions(+), 509 deletions(-) create mode 100644 .env create mode 100644 include/Arguments.h create mode 100644 include/UdpSocket.h create mode 100644 src/Arguments.cpp create mode 100644 src/UdpSocket.cpp diff --git a/.env b/.env new file mode 100644 index 0000000..52ab7eb --- /dev/null +++ b/.env @@ -0,0 +1 @@ +FEITCSI_VERSION=1.1.0 \ No newline at end of file diff --git a/Makefile b/Makefile index e1828d2..1ebfe47 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,8 @@ +include .env + +# Variables +CDEFS += -DFEITCSI_VERSION="\"${FEITCSI_VERSION}"\" + # Target BIN_DIR = bin BIN = $(BIN_DIR)/app @@ -19,7 +24,7 @@ INCLUDE_LIB_DIRS = INCLUDE_LIB = $(foreach includedir,$(INCLUDE_LIB_DIRS),-L$(includedir)) # Set compiler, preprocesor and linker flags -CXXFLAGS += -g -O3 -Wall -std=c++17 $(INCLUDE) +CXXFLAGS += -g -O3 -Wall -std=c++17 $(CDEFS) $(INCLUDE) CPPFLAGS += `pkg-config --cflags gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap` LDFLAGS += $(INCLUDE_LIB) LDLIBS += `pkg-config --libs gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap` diff --git a/include/Arguments.h b/include/Arguments.h new file mode 100644 index 0000000..1e28396 --- /dev/null +++ b/include/Arguments.h @@ -0,0 +1,98 @@ +/* + * FeitCSI is the tool for extracting CSI information from supported intel NICs. + * Copyright (C) 2024 Miroslav Hutar. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ARGUMENTS_PARSER_H +#define ARGUMENTS_PARSER_H + +#include +#include +#include +#include +#include "main.h" + +struct Args +{ + bool verbose; + uint16_t frequency; + bool gui = false; + bool udpSocket = false; + bool plot = false; + std::string bandwidth; + std::string outputFile; + uint8_t mcs; + uint16_t channelWidth; + uint8_t spatialStreams; + uint8_t txPower; + uint32_t antenna; + uint16_t guardInterval; + uint32_t injectDelay; + uint32_t injectRepeat; + std::string coding; + std::string format; + bool inject; + bool measure; + std::string mode; + std::string ltf; + std::string inputFile; + std::map processors; +}; + +class Arguments +{ + +public: + inline static struct Args arguments; + + static void init(); + static void parse(int argc, char *argv[]); + + static error_t parse_opt(int key, char *arg, argp_state *state); + +private: + + /* Program documentation. */ + inline static char doc[] = + "FeitCSI - FeitCSI, the tool that enables CSI extraction and injection IEEE 802.11 frames"; + + /* A description of the arguments we accept. */ + inline static char args_doc[] = "ARG1 ARG2"; + + /* The options we understand. */ + inline static struct argp_option options[] = { + {"frequency", 'f', "FREQUENCY", 0, "Frequency to measure/inject CSI"}, + {"channel-width", 'w', "CHANNELWIDTH", 0, "Channel width to measure/inject CSI. Possible values [20|40|HT40-|80|160]"}, + {"output-file", 'o', "FILE", 0, "Output file where measurements should be stored."}, + {"mcs", 'm', "MCS", 0, "Mcs index [0-11]"}, + {"format", 'r', "FORMAT", 0, "Data frame format [NOHT|HT|VHT|HESU]"}, + {"spatial-streams", 's', "SPATIALSTREAMS", 0, "Number of spatial streams [1|2]"}, + {"guard-interval", 'g', "GUARDINTERVAL", 0, "Guard interval in ns [400|800]"}, + {"ltf", 'l', "LTF", 0, "HE LTF [2xLTF+0.8|2xLTF+1.6|4xLTF+3.2|4xLTF+0.8]"}, + {"coding", 'c', "CODING", 0, "Coding scheme [LDPC|BCC]"}, + {"tx-power", 't', "TXPOWER", 0, "TX power of antenna in dBm [1-22]"}, + {"antenna", 'a', "ANTENNA", 0, "Transmitting antenna 1, 2 or 12 for both"}, + {"mode", 'i', "MODE", 0, "Mode of program[measure|inject|measureinject]"}, + {"inject-delay", 'd', "INJECTDELAY", 0, "Delay between frame injections is us"}, + {"inject-repeat", 'j', "INJECTREPEAT", 0, "How many times inject frame"}, + {"verbose", 'v', 0, OPTION_ARG_OPTIONAL, "Produce verbose output"}, + {"plot", 'p', 0, OPTION_ARG_OPTIONAL, "Plot CSI data"}, + {"gui", 'x', 0, OPTION_ARG_OPTIONAL, "Run application in GUI"}, + {"udp-socket", 'u', 0, OPTION_ARG_OPTIONAL, "Run application and listen to UDP"}, + {0}}; +}; + +#endif \ No newline at end of file diff --git a/include/Csi.h b/include/Csi.h index 9104aac..49cf154 100644 --- a/include/Csi.h +++ b/include/Csi.h @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include #include #include +#include "UdpSocket.h" #define CSI_HEADER_LENGTH 272 @@ -56,6 +57,7 @@ public: void loadFromMemory(uint8_t *pHeader, uint8_t *rawCsiData); void loadFromMemory(uint8_t *rawData); void save(); + void sendUDP(UdpSocket *udpSocket); void backup(); void restore(); void magnitudePhaseToComplex(); diff --git a/include/MainController.h b/include/MainController.h index 14ce182..ca9f058 100644 --- a/include/MainController.h +++ b/include/MainController.h @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,18 +23,20 @@ #include "WiFIController.h" #include "PacketInjector.h" #include "gui/MainWindow.h" +#include "UdpSocket.h" #include class MainController { public: - static MainController *getInstance(); + inline static UdpSocket *udpSocket = nullptr; + static MainController *getInstance(); static void deleteInstance(); - void runNoGui(); + void runNoGui(bool detach = false); void measureCsi(bool stop = false); @@ -42,9 +44,14 @@ public: void runGui(); + void runUdpSocket(); + void initInterface(); - ~MainController(); + void restoreState(); + + ~MainController(); + private: MainController(); @@ -62,14 +69,12 @@ private: std::vector bkpInterfaces; - static void *measureCsi(void *arg); static void intHandler(int dummy); static void *injectPackets(void *arg); - void restoreState(); }; #endif \ No newline at end of file diff --git a/include/PacketInjector.h b/include/PacketInjector.h index c60d112..912029f 100644 --- a/include/PacketInjector.h +++ b/include/PacketInjector.h @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include #include "ieee80211_radiotap.h" #include "rs.h" +#include #define BIT(n) (0x1U << (n)) @@ -38,6 +39,7 @@ public: private: void send(uint32_t rateNFlags); + pcap_t *ppcap = nullptr; }; #endif \ No newline at end of file diff --git a/include/UdpSocket.h b/include/UdpSocket.h new file mode 100644 index 0000000..b4ef7e9 --- /dev/null +++ b/include/UdpSocket.h @@ -0,0 +1,39 @@ +/* + * FeitCSI is the tool for extracting CSI information from supported intel NICs. + * Copyright (C) 2024 Miroslav Hutar. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UDP_SOCKET_H +#define UDP_SOCKET_H + +#include + +class UdpSocket +{ + +public: + void init(); + void send(char *buf, int size); + +private: + + int sfd; + bool running = false; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; +}; + +#endif \ No newline at end of file diff --git a/include/WiFiCsiController.h b/include/WiFiCsiController.h index 288bdae..acee8ac 100644 --- a/include/WiFiCsiController.h +++ b/include/WiFiCsiController.h @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +39,7 @@ public: private: static int listenToCsiHandler(nl80211_state *state, nl_msg *msg, void *arg); static int processListenToCsiHandler(nl_msg *msg, void *arg); + static void printDetail(Csi &c); GnuPlot gnuPlot; }; diff --git a/include/main.h b/include/main.h index 21f9c06..9fe4a94 100644 --- a/include/main.h +++ b/include/main.h @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,10 +19,6 @@ #ifndef MAIN_H #define MAIN_H -#include -#include -#include - #define MONITOR_INTERFACE_NAME "mon0" enum processor @@ -33,34 +29,4 @@ enum processor phaseCalibrationLinearTransform, }; -struct Arguments -{ - bool verbose; - uint16_t frequency; - bool gui = false; - bool plot = false; - std::string bandwidth; - std::string outputFile; - uint8_t mcs; - uint16_t channelWidth; - uint8_t spatialStreams; - uint8_t txPower; - uint32_t antenna; - uint16_t guardInterval; - uint32_t injectDelay; - uint32_t injectRepeat; - std::string coding; - std::string format; - bool inject; - bool measure; - std::string mode; - std::string ltf; - std::string inputFile; - std::map processors; -}; - -/* Get the input argument from argp_parse, which we - know is a pointer to our arguments structure. */ -extern struct Arguments arguments; - #endif \ No newline at end of file diff --git a/readme.md b/readme.md index 2760dfa..706cd07 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ citations to the FeitCSI project: ## Competition There are some platforms, that are used to extract CSI from commodity hardware. However, they don't support current standards or injection packets. The only one competition is PicoScenes. It is a robust and good platform but FeitCSI has several advantages against it: -* **All for free without limits** (Some of the functionality (full 6GHz spectrum, packet injection in 11ac/ax format with 80/160MHz channel bandwidth, ...) are allowed after payment for the license in PicoScenes) +* **All for free without limits** * **No restriction to specific version of Linux OS and computer architecture** (PicoScenes work only on CPU with SSE4.2 or AVX2 and you need to have Ubuntu 20.04 LTS or its variants. **FeitCSI will work on every computer architecture** where can be installed Linux (e.g. x86-64, ARM). But of course, you have to plug NICs via PCI.) * **Support virtual environment** (If you don't want to install software on your computer or you have trouble, just download our prepared image of FeitCSI and enjoy FeitCSI in the virtual environment. Follow the steps in [Getting started - Virtual environment](https://feitcsi.kuskosoft.com/getting_started/#virtual-environment)) * **Live medium** (Our image can be also used in live medium (e.g. USB), not only in the virtual environment) diff --git a/src/Arguments.cpp b/src/Arguments.cpp new file mode 100644 index 0000000..27e8b6a --- /dev/null +++ b/src/Arguments.cpp @@ -0,0 +1,273 @@ +/* + * FeitCSI is the tool for extracting CSI information from supported intel NICs. + * Copyright (C) 2024 Miroslav Hutar. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Arguments.h" +#include "WiFIController.h" +#include "rs.h" + +const std::string VERSION = (std::string("FeitCSI ") + FEITCSI_VERSION); +const char *argp_program_version = VERSION.c_str(); +const char *argp_program_bug_address = "https://github.com/KuskoSoft/FeitCSI/issues"; + +void Arguments::init() +{ + Arguments::arguments = { + .verbose = false, + .frequency = 2412, + .gui = false, + .udpSocket = false, + .plot = false, + .bandwidth = "20", + .mcs = 0, + .channelWidth = 20, + .spatialStreams = 1, + .txPower = 10, + .antenna = RATE_MCS_ANT_A_MSK, + .guardInterval = 400, + .injectDelay = 100000, + .injectRepeat = 0, + .coding = "LDPC", + .format = "NOHT", + .inject = false, + .measure = true, + .mode = "measure", + .ltf = "1xLTF+0.8", + }; +} + +void Arguments::parse(int argc, char *argv[]) +{ + static struct argp argp = {options, parse_opt, args_doc, doc}; + + argp_parse(&argp, argc, argv, 0, 0, &arguments); +} + +error_t Arguments::parse_opt(int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct Args *args = (struct Args *)state->input; + + switch (key) + { + case 'v': + args->verbose = true; + break; + case 'x': + args->gui = true; + break; + case 'u': + args->udpSocket = true; + break; + case 'p': + args->plot = true; + break; + case 'i': + { + args->mode.assign(arg); + if (args->mode == "measure") + { + args->measure = true; + } + else if (args->mode == "inject") + { + args->inject = true; + args->measure = false; + } + else if (args->mode == "measureinject") + { + args->measure = true; + args->inject = true; + } + else + { + argp_failure(state, 1, 0, "Bad mode. Possible values [measure|inject|measureinject]"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'm': + { + int mcs = std::atoi(arg); + if (mcs < 0 || mcs > 11) + { + argp_failure(state, 1, 0, "Bad MCS index. Possible values [0-11]"); + exit(ARGP_ERR_UNKNOWN); + } + args->mcs = (uint8_t)mcs; + break; + } + case 'r': + { + args->format.assign(arg); + if (args->format == "NOHT" || args->format == "HT" || args->format == "VHT" || args->format == "HESU") + { + } + else + { + argp_failure(state, 1, 0, "Bad format. Possible values [NOHT|HT|VHT|HESU]"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'c': + { + args->coding.assign(arg); + if (args->coding == "LDPC" || args->coding == "BCC") + { + } + else + { + argp_failure(state, 1, 0, "Bad coding. Possible values [LDPC|BCC]"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'l': + { + args->ltf.assign(arg); + if (args->ltf == "1xLTF+0.8" || args->ltf == "2xLTF+0.8" || args->ltf == "2xLTF+1.6" || args->ltf == "4xLTF+3.2" || args->ltf == "4xLTF+0.8") + { + } + else + { + argp_failure(state, 1, 0, "Bad LTF. Possible values [2xLTF+0.8|2xLTF+1.6|4xLTF+3.2|4xLTF+0.8]"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'g': + { + int gi = std::atoi(arg); + if (gi == 400 || gi == 800) + { + args->guardInterval = (uint16_t)gi; + } + else + { + argp_failure(state, 1, 0, "Bad guard interval. Possible values [400|800]"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'd': + { + int injd = std::atoi(arg); + if (injd <= 0) + { + argp_failure(state, 1, 0, "Inject delay is not correct number"); + exit(ARGP_ERR_UNKNOWN); + } + args->injectDelay = (uint32_t)injd; + break; + } + case 'j': + { + int injr = std::atoi(arg); + if (injr <= 0) + { + argp_failure(state, 1, 0, "Inject repeat is not correct number"); + exit(ARGP_ERR_UNKNOWN); + } + args->injectRepeat = (uint32_t)injr; + break; + } + case 's': + { + int ss = std::atoi(arg); + if (ss < 1 || ss > 2) + { + argp_failure(state, 1, 0, "Bad spatial stream. Possible values [1|2]"); + exit(ARGP_ERR_UNKNOWN); + } + args->spatialStreams = (uint8_t)ss; + break; + } + case 't': + { + int tx = std::atoi(arg); + if (tx < 1 || tx > 22) + { + argp_failure(state, 1, 0, "Bad tx power. Possible values [1-22]"); + exit(ARGP_ERR_UNKNOWN); + } + args->txPower = (uint8_t)tx; + break; + } + case 'a': + { + int a = std::atoi(arg); + if (a == 1) + { + args->antenna = RATE_MCS_ANT_A_MSK; + } + else if (a == 2) + { + args->antenna = RATE_MCS_ANT_B_MSK; + } + else if (a == 12) + { + args->antenna = RATE_MCS_ANT_AB_MSK; + } + else + { + argp_failure(state, 1, 0, "Bad transmitting antenna value. Possible values 1, 2 or 12 for both"); + exit(ARGP_ERR_UNKNOWN); + } + break; + } + case 'f': + { + int f = std::atoi(arg); + if (f <= 0) + { + argp_failure(state, 1, 0, "Frequency is not correct"); + exit(ARGP_ERR_UNKNOWN); + } + args->frequency = (uint16_t)f; + break; + } + case 'w': + { + struct ChanMode chMode = WiFIController::getChanMode(arg); + if (chMode.width == 0) + { + argp_failure(state, 1, 0, "Bad bandwidth. Possible values of bandwidth are [20|40|HT40-|80|160]"); + exit(ARGP_ERR_UNKNOWN); + } + args->bandwidth = arg; + args->channelWidth = WiFIController::chanModeToWidth(chMode); + break; + } + case 'o': + args->outputFile = arg; + break; + case ARGP_KEY_ARG: + case ARGP_KEY_END: + if (args->frequency == 0 || + args->bandwidth.empty()) + { + argp_failure(state, 1, 0, "Fill required arguments -f -b . See --help for more information"); + exit(ARGP_ERR_UNKNOWN); + } + return 0; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} \ No newline at end of file diff --git a/src/Csi.cpp b/src/Csi.cpp index 7230be5..92dd751 100644 --- a/src/Csi.cpp +++ b/src/Csi.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,9 +24,9 @@ #include #include #include -#include "main.h" #include "rs.h" #include "Logger.h" +#include "Arguments.h" Csi::Csi() { @@ -73,7 +73,7 @@ void Csi::loadFromMemory(uint8_t *rawData) void Csi::save() { std::ofstream outfile; - outfile.open(arguments.outputFile, std::ios_base::app | std::ios::binary); + outfile.open(Arguments::arguments.outputFile, std::ios_base::app | std::ios::binary); if (outfile.fail()) { throw std::ios_base::failure("Open file failed: " + std::string(std::strerror(errno))); @@ -81,7 +81,16 @@ void Csi::save() outfile.write(reinterpret_cast(&this->rawHeaderData), sizeof(RawHeaderData)); outfile.write(reinterpret_cast(this->rawCsiData), this->rawHeaderData.csiDataSize); outfile.close(); - std::filesystem::permissions(arguments.outputFile, std::filesystem::perms::all & ~(std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec), std::filesystem::perm_options::add); + std::filesystem::permissions(Arguments::arguments.outputFile, std::filesystem::perms::all & ~(std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec), std::filesystem::perm_options::add); +} + +void Csi::sendUDP(UdpSocket *udpSocket) +{ + int size = CSI_HEADER_LENGTH + this->rawHeaderData.csiDataSize; + char data[size]; + memcpy(data, &this->rawHeaderData, CSI_HEADER_LENGTH); + memcpy(&data[CSI_HEADER_LENGTH], this->rawCsiData, this->rawHeaderData.csiDataSize); + udpSocket->send(data, size); } void Csi::fixCsiBug() @@ -196,57 +205,6 @@ void Csi::processRawCsi() this->magnitude.push_back(std::abs(c)); this->phase.push_back(std::arg(c)); } - - //this->unwrapPhase(); - - if (arguments.verbose) - { - Logger::log(info) << "Subcarrier count: " << this->rawHeaderData.numSubCarriers << ", "; - Logger::log(info, true) << "RX: " << +this->rawHeaderData.numRx << ", "; - Logger::log(info, true) << "TX: " << +this->rawHeaderData.numTx << ", "; - - switch (channelWidth) - { - case RATE_MCS_CHAN_WIDTH_20: - Logger::log(info, true) << "Channel width: 20, "; - break; - case RATE_MCS_CHAN_WIDTH_40: - Logger::log(info, true) << "Channel width: 40, "; - break; - case RATE_MCS_CHAN_WIDTH_80: - Logger::log(info, true) << "Channel width: 80, "; - break; - case RATE_MCS_CHAN_WIDTH_160: - Logger::log(info, true) << "Channel width: 160, "; - break; - } - switch (format) - { - case RATE_MCS_CCK_MSK: // VERY OLD FORMAT - Logger::log(info, true) << "Format: CCK\n"; - break; - case RATE_MCS_LEGACY_OFDM_MSK: - Logger::log(info, true) << "Format: LEGACY_OFDM\n"; - break; - break; - case RATE_MCS_HT_MSK: - Logger::log(info, true) << "Format: HT\n"; - break; - break; - case RATE_MCS_VHT_MSK: - Logger::log(info, true) << "Format: VHT\n"; - break; - break; - case RATE_MCS_HE_MSK: - Logger::log(info, true) << "Format: HE\n"; - break; - break; - case RATE_MCS_EHT_MSK: - Logger::log(info, true) << "Format: EHT\n"; - break; - break; - } - } } void Csi::backup() diff --git a/src/CsiProcessor.cpp b/src/CsiProcessor.cpp index baa10c9..85bc145 100644 --- a/src/CsiProcessor.cpp +++ b/src/CsiProcessor.cpp @@ -21,6 +21,7 @@ #include "Logger.h" #include "GnuPlot.h" #include "interpolation.h" +#include "Arguments.h" #include #include @@ -30,7 +31,7 @@ bool CsiProcessor::loadCsi() { this->clearState(); - std::ifstream ifs(arguments.inputFile, std::ios::binary); + std::ifstream ifs(Arguments::arguments.inputFile, std::ios::binary); ifs.seekg (0, ifs.end); int length = ifs.tellg(); @@ -60,7 +61,7 @@ bool CsiProcessor::loadCsi() void CsiProcessor::saveCsi() { std::ofstream outfile; - outfile.open(arguments.outputFile, std::ios_base::app | std::ios::binary); + outfile.open(Arguments::arguments.outputFile, std::ios_base::app | std::ios::binary); if (outfile.fail()) { throw std::ios_base::failure("Open file failed: " + std::string(std::strerror(errno))); @@ -74,7 +75,7 @@ void CsiProcessor::saveCsi() outfile.write(reinterpret_cast(c->csi.data()), c->rawHeaderData.csiDataSize); } outfile.close(); - std::filesystem::permissions(arguments.outputFile, std::filesystem::perms::all & ~(std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec), std::filesystem::perm_options::add); + std::filesystem::permissions(Arguments::arguments.outputFile, std::filesystem::perms::all & ~(std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec), std::filesystem::perm_options::add); } CsiProcessor::~CsiProcessor() @@ -132,20 +133,20 @@ void CsiProcessor::process(Csi &csi) csi.backup(); csi.restore(); - if (arguments.processors[processor::interpolateLinear]) + if (Arguments::arguments.processors[processor::interpolateLinear]) { this->interpolate(csi, processor::interpolateLinear); } - else if (arguments.processors[processor::interpolateCubic]) + else if (Arguments::arguments.processors[processor::interpolateCubic]) { this->interpolate(csi, processor::interpolateCubic); } - else if (arguments.processors[processor::interpolateCosine]) + else if (Arguments::arguments.processors[processor::interpolateCosine]) { this->interpolate(csi, processor::interpolateCosine); } - if (arguments.processors[processor::phaseCalibrationLinearTransform]) + if (Arguments::arguments.processors[processor::phaseCalibrationLinearTransform]) { this->phaseCalibLinearTransform(csi); } diff --git a/src/MainController.cpp b/src/MainController.cpp index bfb6451..ea17319 100644 --- a/src/MainController.cpp +++ b/src/MainController.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ */ #include "MainController.h" -#include "main.h" +#include "Arguments.h" #include "Logger.h" #include "gui/MainWindow.h" #include "layout.h" @@ -44,31 +44,42 @@ void MainController::deleteInstance() delete MainController::INSTANCE; } -void MainController::runNoGui() +void MainController::runNoGui(bool detach) { this->initInterface(); - if (arguments.measure) + if (Arguments::arguments.measure) { pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL); } - if (arguments.inject) + if (Arguments::arguments.inject) { pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL); } - if (arguments.measure) + if (Arguments::arguments.measure) { - pthread_join(this->measureCsiThread, NULL); + if (detach) { + pthread_detach(this->measureCsiThread); + } else { + pthread_join(this->measureCsiThread, NULL); + } } - if (arguments.inject) + if (Arguments::arguments.inject) { - pthread_join(this->injectPacketThread, NULL); + if (detach) { + pthread_detach(this->injectPacketThread); + } else { + pthread_join(this->injectPacketThread, NULL); + } + } + + if (!detach) { + this->deleteInstance(); } - this->deleteInstance(); } void MainController::measureCsi(bool stop) { - this->wifiController.setFreq(arguments.frequency, arguments.bandwidth.c_str()); + this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str()); if (stop) { pthread_cancel(this->measureCsiThread); @@ -83,7 +94,7 @@ void MainController::measureCsi(bool stop) void MainController::injectPackets(bool stop) { this->wifiController.setTxPower(); - this->wifiController.setFreq(arguments.frequency, arguments.bandwidth.c_str()); + this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str()); if (stop) { pthread_cancel(this->injectPacketThread); @@ -97,10 +108,10 @@ void MainController::injectPackets(bool stop) void MainController::runGui() { - arguments.plot = true; - arguments.verbose = true; - arguments.measure = false; - arguments.inject = false; + Arguments::arguments.plot = true; + Arguments::arguments.verbose = true; + Arguments::arguments.measure = false; + Arguments::arguments.inject = false; gtk_init(NULL, NULL); Glib::init(); auto app = Gtk::Application::create("com.kuskosoft.feitcsi"); @@ -110,6 +121,12 @@ void MainController::runGui() app->run(*this->mainWindow); } +void MainController::runUdpSocket() +{ + this->udpSocket = new UdpSocket(); + udpSocket->init(); +} + void MainController::initInterface() { try @@ -121,7 +138,7 @@ void MainController::initInterface() // delete actual interfaces for (InterfaceInfo interface : this->wifiController.interfaces) { - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Remove interface " << interface.ifName << "\n"; } @@ -130,7 +147,7 @@ void MainController::initInterface() } this->wifiController.addMonitorDevice(MONITOR_INTERFACE_NAME, NL80211_IFTYPE_MONITOR); this->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, true); - this->wifiController.setFreq(arguments.frequency, arguments.bandwidth.c_str()); + this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str()); if (!MainController::mainWindow) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); //wait on init then set power and go next @@ -181,12 +198,12 @@ void *MainController::injectPackets(void *arg) try { PacketInjector pi; - if (arguments.injectRepeat) + if (Arguments::arguments.injectRepeat) { - for (uint32_t i = 0; i < arguments.injectRepeat; i++) + for (uint32_t i = 0; i < Arguments::arguments.injectRepeat; i++) { pi.inject(); - std::this_thread::sleep_for(std::chrono::microseconds(arguments.injectDelay)); + std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay)); } } else @@ -194,7 +211,7 @@ void *MainController::injectPackets(void *arg) while (true) { pi.inject(); - std::this_thread::sleep_for(std::chrono::microseconds(arguments.injectDelay)); + std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay)); } } } @@ -229,14 +246,16 @@ void MainController::restoreState() mainController->wifiController.removeInterface(MONITOR_INTERFACE_NAME); for (InterfaceInfo interface : mainController->bkpInterfaces) { - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Recovering interface " << interface.ifName << "\n"; } mainController->wifiController.addMonitorDevice(interface.ifName.c_str(), (nl80211_iftype)interface.ifType); } + mainController->bkpInterfaces.clear(); + mainController->wifiController.interfaces.clear(); - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Exiting recovery state...\n"; } @@ -245,6 +264,9 @@ void MainController::restoreState() MainController::~MainController() { this->restoreState(); + if (udpSocket) { + delete udpSocket; + } } void MainController::intHandler(int dummy) diff --git a/src/PacketInjector.cpp b/src/PacketInjector.cpp index 7033dfc..dc3877c 100644 --- a/src/PacketInjector.cpp +++ b/src/PacketInjector.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,8 @@ #include "PacketInjector.h" #include "main.h" #include "Logger.h" +#include "Arguments.h" #include -#include #include #define SPATIAL_STREAM 16 @@ -32,25 +32,25 @@ uint8_t ieee80211Body[] = {}; void PacketInjector::inject() { - if (arguments.verbose) + if (Arguments::arguments.verbose) { - Logger::log(info) << "Injecting " << arguments.format << "\n"; + Logger::log(info) << "Injecting " << Arguments::arguments.format << "\n"; } - if (arguments.format == "NOHT") + if (Arguments::arguments.format == "NOHT") { this->injectNoHT(); } - else if (arguments.format == "HT") + else if (Arguments::arguments.format == "HT") { this->injectHT(); } - else if (arguments.format == "VHT") + else if (Arguments::arguments.format == "VHT") { this->injectVHT(); } - else if (arguments.format == "HESU") + else if (Arguments::arguments.format == "HESU") { this->injectHE(); } @@ -59,11 +59,11 @@ void PacketInjector::inject() void PacketInjector::injectNoHT() { uint8_t mcs = 0; - if (RATE_LEGACY_RATE_MSK >= arguments.mcs) + if (RATE_LEGACY_RATE_MSK >= Arguments::arguments.mcs) { - mcs = RATE_LEGACY_RATE_MSK & arguments.mcs; + mcs = RATE_LEGACY_RATE_MSK & Arguments::arguments.mcs; } - uint32_t rateNFlags = RATE_MCS_LEGACY_OFDM_MSK | mcs | arguments.antenna; + uint32_t rateNFlags = RATE_MCS_LEGACY_OFDM_MSK | mcs | Arguments::arguments.antenna; this->send(rateNFlags); } @@ -71,65 +71,65 @@ void PacketInjector::injectNoHT() void PacketInjector::injectHT() { uint8_t mcs = 0; - if (RATE_HT_MCS_CODE_MSK >= arguments.mcs) + if (RATE_HT_MCS_CODE_MSK >= Arguments::arguments.mcs) { - mcs = RATE_HT_MCS_CODE_MSK & arguments.mcs; + mcs = RATE_HT_MCS_CODE_MSK & Arguments::arguments.mcs; } uint32_t rateNFlags = RATE_MCS_HT_MSK | mcs | - arguments.antenna | - (arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | - (arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | - (arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0) | - (arguments.guardInterval == 400 ? RATE_MCS_SGI_MSK : 0) | - (arguments.coding == "LDPC" ? RATE_MCS_LDPC_MSK : 0); + Arguments::arguments.antenna | + (Arguments::arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | + (Arguments::arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | + (Arguments::arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0) | + (Arguments::arguments.guardInterval == 400 ? RATE_MCS_SGI_MSK : 0) | + (Arguments::arguments.coding == "LDPC" ? RATE_MCS_LDPC_MSK : 0); this->send(rateNFlags); } void PacketInjector::injectVHT() { uint8_t mcs = 0; - if (RATE_MCS_CODE_MSK >= arguments.mcs) + if (RATE_MCS_CODE_MSK >= Arguments::arguments.mcs) { - mcs = RATE_MCS_CODE_MSK & arguments.mcs; + mcs = RATE_MCS_CODE_MSK & Arguments::arguments.mcs; } uint32_t rateNFlags = RATE_MCS_VHT_MSK | mcs | - arguments.antenna | - (arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | - (arguments.channelWidth == 80 ? RATE_MCS_CHAN_WIDTH_80 : 0) | - (arguments.channelWidth == 160 ? RATE_MCS_CHAN_WIDTH_160 : 0) | - (arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | - (arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0) | - (arguments.guardInterval == 400 ? RATE_MCS_SGI_MSK : 0) | - (arguments.coding == "LDPC" ? RATE_MCS_LDPC_MSK : 0); + Arguments::arguments.antenna | + (Arguments::arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | + (Arguments::arguments.channelWidth == 80 ? RATE_MCS_CHAN_WIDTH_80 : 0) | + (Arguments::arguments.channelWidth == 160 ? RATE_MCS_CHAN_WIDTH_160 : 0) | + (Arguments::arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | + (Arguments::arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0) | + (Arguments::arguments.guardInterval == 400 ? RATE_MCS_SGI_MSK : 0) | + (Arguments::arguments.coding == "LDPC" ? RATE_MCS_LDPC_MSK : 0); this->send(rateNFlags); } void PacketInjector::injectHE() { uint8_t mcs = 0; - if (RATE_MCS_CODE_MSK >= arguments.mcs) + if (RATE_MCS_CODE_MSK >= Arguments::arguments.mcs) { - mcs = RATE_MCS_CODE_MSK & arguments.mcs; + mcs = RATE_MCS_CODE_MSK & Arguments::arguments.mcs; } uint32_t ltf = 1; - if (arguments.ltf == "2xLTF+0.8") + if (Arguments::arguments.ltf == "2xLTF+0.8") { ltf = 1; } - else if (arguments.ltf == "2xLTF+1.6") + else if (Arguments::arguments.ltf == "2xLTF+1.6") { ltf = 2; } - else if (arguments.ltf == "4xLTF+3.2") + else if (Arguments::arguments.ltf == "4xLTF+3.2") { ltf = 3; } - else if (arguments.ltf == "4xLTF+0.8") + else if (Arguments::arguments.ltf == "4xLTF+0.8") { ltf = 4; } @@ -139,13 +139,13 @@ void PacketInjector::injectHE() RATE_MCS_HE_MSK | RATE_MCS_LDPC_MSK | mcs | - arguments.antenna | + Arguments::arguments.antenna | ltf | - (arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | - (arguments.channelWidth == 80 ? RATE_MCS_CHAN_WIDTH_80 : 0) | - (arguments.channelWidth == 160 ? RATE_MCS_CHAN_WIDTH_160 : 0) | - (arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | - (arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0); + (Arguments::arguments.channelWidth == 40 ? RATE_MCS_CHAN_WIDTH_40 : 0) | + (Arguments::arguments.channelWidth == 80 ? RATE_MCS_CHAN_WIDTH_80 : 0) | + (Arguments::arguments.channelWidth == 160 ? RATE_MCS_CHAN_WIDTH_160 : 0) | + (Arguments::arguments.spatialStreams == 2 ? SPATIAL_STREAM : 0) | + (Arguments::arguments.spatialStreams == 2 ? RATE_MCS_ANT_AB_MSK : 0); this->send(rateNFlags); } @@ -170,15 +170,20 @@ void PacketInjector::send(uint32_t rateNFlags) memcpy(&sendBuffer[rthdr.it_len], ieee80211Header, sizeof(ieee80211Header)); memcpy(&sendBuffer[rthdr.it_len + sizeof(ieee80211Header)], ieee80211Body, sizeof(ieee80211Body)); - uint16_t totalSize = rthdr.it_len + sizeof(ieee80211Header) + sizeof(ieee80211Body); + int totalSize = rthdr.it_len + sizeof(ieee80211Header) + sizeof(ieee80211Body); char szErrbuf[500]; - pcap_t *ppcap = pcap_open_live("mon0", 800, 1, 20, szErrbuf); + + if (ppcap == nullptr) { + ppcap = pcap_open_live("mon0", 800, 1, 20, szErrbuf); + } + int r = pcap_inject(ppcap, sendBuffer, totalSize); - if (r > 0) + if (r != totalSize) { //pcap_perror(ppcap, "Failed to inject packet"); pcap_close(ppcap); + ppcap = nullptr; //throw std::ios_base::failure("Failed to inject packet\n"); } diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp new file mode 100644 index 0000000..8fa3de8 --- /dev/null +++ b/src/UdpSocket.cpp @@ -0,0 +1,152 @@ +/* + * FeitCSI is the tool for extracting CSI information from supported intel NICs. + * Copyright (C) 2024 Miroslav Hutar. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "UdpSocket.h" + +#include +#include +#include +#include +#include +#include +#include "Logger.h" +#include "Arguments.h" +#include "MainController.h" + +#define PORT "8008" +#define BUF_SIZE 1024 + +void UdpSocket::init() +{ + MainController *mainController = MainController::getInstance(); + struct addrinfo hints; + struct addrinfo *result, *rp; + ssize_t nread; + int s; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(NULL, PORT, &hints, &result); + if (s != 0) + { + Logger::log(error) << "getaddrinfo: " << gai_strerror(s) << "\n"; + exit(1); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) + { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(sfd); + } + + if (rp == NULL) + { /* No address succeeded */ + Logger::log(error) << "Error bind socket \n"; + exit(1); + } + + freeaddrinfo(result); /* No longer needed */ + + /* Read datagrams and echo them back to sender */ + + while (1) + { + char buf[BUF_SIZE] = {0}; + peer_addr_len = sizeof(struct sockaddr_storage); + nread = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *)&peer_addr, &peer_addr_len); + if (nread == -1) + continue; /* Ignore failed request */ + + char host[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo( + (struct sockaddr *)&peer_addr, + peer_addr_len, + host, + NI_MAXHOST, + service, + NI_MAXSERV, + NI_NUMERICSERV); + if (s == 0) { + if (strncmp(buf, "stop", 4) == 0) { + if (this->running) { + mainController->restoreState(); + this->running = false; + } + } else { + char *args[128]; + std::istringstream iss(buf); + + std::string token; + int index = 0; + while (iss >> token) + { + char *arg = new char[token.size() + 1]; + copy(token.begin(), token.end(), arg); + arg[token.size()] = '\0'; + args[index] = arg; + index++; + } + + Arguments::parse(index, &args[0]); + + for (int i = 0; i < index - 1; i++) + delete[] args[i]; + + mainController->runNoGui(true); + this->running = true; + } + } + else { + Logger::log(error) << "Error getnameinfo " << gai_strerror(s) << "\n"; + } + } +} + +void UdpSocket::send(char *buf, int size) +{ + if ( + sendto( + sfd, + buf, + size, + 0, + (struct sockaddr *)&peer_addr, + peer_addr_len) != size) + { + Logger::log(error) << "Error sending response \n"; + } +} \ No newline at end of file diff --git a/src/WiFIController.cpp b/src/WiFIController.cpp index e733371..0ae54a9 100644 --- a/src/WiFIController.cpp +++ b/src/WiFIController.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ #include #include "main.h" #include "Logger.h" +#include "Arguments.h" const char *IF_MODES[NL80211_IFTYPE_MAX + 1] = { "unspecified", @@ -180,7 +181,7 @@ int WiFIController::setInterfaceUpDown(const char *ifName, bool up) // Close the socket close(sockfd); - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Interface " << ifName << " has been brought " << (up ? "up" : "down") << "\n"; } @@ -347,7 +348,7 @@ int WiFIController::processSetFreq(struct nl80211_state *state, struct nl_msg *m uint16_t settingsFreq = *(uint16_t *)(settings[0]); char *settingsWidth = (char *)(settings[1]); - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Setting frequency " << settingsFreq << " channel width " << settingsWidth << "\n"; } @@ -416,7 +417,7 @@ nla_put_failure: int WiFIController::setTxPowerHandler(nl80211_state * state, nl_msg * msg, void * arg) { enum nl80211_tx_power_setting type = NL80211_TX_POWER_FIXED; - int mbm = arguments.txPower * 100; //dBm to mbm *100 + int mbm = Arguments::arguments.txPower * 100; //dBm to mbm *100 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm); diff --git a/src/WiFiCsiController.cpp b/src/WiFiCsiController.cpp index a73f9f9..374f823 100644 --- a/src/WiFiCsiController.cpp +++ b/src/WiFiCsiController.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "WiFiCsiController.h" #include "Csi.h" #include "GnuPlot.h" +#include "MainController.h" +#include "Arguments.h" #include #include @@ -93,26 +95,33 @@ int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg) Csi c; c.loadFromMemory(header, dataCsi); - instance->gnuPlot.updateChart(c); if ( - (c.channelWidth == RATE_MCS_CHAN_WIDTH_20 && arguments.channelWidth == 20) || - (c.channelWidth == RATE_MCS_CHAN_WIDTH_40 && arguments.channelWidth == 40) || - (c.channelWidth == RATE_MCS_CHAN_WIDTH_80 && arguments.channelWidth == 80) || - (c.channelWidth == RATE_MCS_CHAN_WIDTH_160 && arguments.channelWidth == 160) + (c.channelWidth == RATE_MCS_CHAN_WIDTH_20 && Arguments::arguments.channelWidth == 20) || + (c.channelWidth == RATE_MCS_CHAN_WIDTH_40 && Arguments::arguments.channelWidth == 40) || + (c.channelWidth == RATE_MCS_CHAN_WIDTH_80 && Arguments::arguments.channelWidth == 80) || + (c.channelWidth == RATE_MCS_CHAN_WIDTH_160 && Arguments::arguments.channelWidth == 160) ) { if ( - (c.format == RATE_MCS_LEGACY_OFDM_MSK && arguments.format == "NOHT") || - (c.format == RATE_MCS_HT_MSK && arguments.format == "HT") || - (c.format == RATE_MCS_VHT_MSK && arguments.format == "VHT") || - (c.format == RATE_MCS_HE_MSK && arguments.format == "HESU") || - (c.format == RATE_MCS_EHT_MSK && arguments.format == "EHT") + (c.format == RATE_MCS_LEGACY_OFDM_MSK && Arguments::arguments.format == "NOHT") || + (c.format == RATE_MCS_HT_MSK && Arguments::arguments.format == "HT") || + (c.format == RATE_MCS_VHT_MSK && Arguments::arguments.format == "VHT") || + (c.format == RATE_MCS_HE_MSK && Arguments::arguments.format == "HESU") || + (c.format == RATE_MCS_EHT_MSK && Arguments::arguments.format == "EHT") ) { - c.save(); + if (Arguments::arguments.verbose) { + printDetail(c); + } + instance->gnuPlot.updateChart(c); + if ( MainController::getInstance()->udpSocket ) { + c.sendUDP(MainController::getInstance()->udpSocket); + } else { + c.save(); + } } } } @@ -121,9 +130,58 @@ int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg) return NL_SKIP; } +void WiFiCsiController::printDetail(Csi &c) +{ + Logger::log(info) << "Subcarrier count: " << c.rawHeaderData.numSubCarriers << ", "; + Logger::log(info, true) << "RX: " << +c.rawHeaderData.numRx << ", "; + Logger::log(info, true) << "TX: " << +c.rawHeaderData.numTx << ", "; + + switch (c.channelWidth) + { + case RATE_MCS_CHAN_WIDTH_20: + Logger::log(info, true) << "Channel width: 20, "; + break; + case RATE_MCS_CHAN_WIDTH_40: + Logger::log(info, true) << "Channel width: 40, "; + break; + case RATE_MCS_CHAN_WIDTH_80: + Logger::log(info, true) << "Channel width: 80, "; + break; + case RATE_MCS_CHAN_WIDTH_160: + Logger::log(info, true) << "Channel width: 160, "; + break; + } + switch (c.format) + { + case RATE_MCS_CCK_MSK: // VERY OLD FORMAT + Logger::log(info, true) << "Format: CCK\n"; + break; + case RATE_MCS_LEGACY_OFDM_MSK: + Logger::log(info, true) << "Format: LEGACY_OFDM\n"; + break; + break; + case RATE_MCS_HT_MSK: + Logger::log(info, true) << "Format: HT\n"; + break; + break; + case RATE_MCS_VHT_MSK: + Logger::log(info, true) << "Format: VHT\n"; + break; + break; + case RATE_MCS_HE_MSK: + Logger::log(info, true) << "Format: HE\n"; + break; + break; + case RATE_MCS_EHT_MSK: + Logger::log(info, true) << "Format: EHT\n"; + break; + break; + } +} + void WiFiCsiController::enableCsi(bool enable) { - if (arguments.verbose) + if (Arguments::arguments.verbose) { if (enable) { diff --git a/src/gui/CsiProcessingWindow.cpp b/src/gui/CsiProcessingWindow.cpp index 2818a42..376391e 100644 --- a/src/gui/CsiProcessingWindow.cpp +++ b/src/gui/CsiProcessingWindow.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include "GnuPlot.h" #include "main.h" #include "Logger.h" +#include "Arguments.h" #include "CsiProcessingWindow.h" void CsiProcessingWindow::init(Glib::RefPtr &builder) @@ -53,7 +54,7 @@ void CsiProcessingWindow::init(Glib::RefPtr &builder) void CsiProcessingWindow::inputFileChange() { - arguments.inputFile = this->inputFile->get_filename(); + Arguments::arguments.inputFile = this->inputFile->get_filename(); csiProcessor.loadCsi(); this->refresh(); } @@ -95,7 +96,7 @@ void CsiProcessingWindow::interpolationLinearRadioButtonClicked() { if (!this->csiProcessor.csiData.empty()) { - arguments.processors[processor::interpolateLinear] = this->interpolationLinearRadioButton->get_active(); + Arguments::arguments.processors[processor::interpolateLinear] = this->interpolationLinearRadioButton->get_active(); this->csiProcessor.process(*this->csiProcessor.csiData[this->currentIndex]); this->refresh(); } @@ -105,7 +106,7 @@ void CsiProcessingWindow::interpolationCubicRadioButtonClicked() { if (!this->csiProcessor.csiData.empty()) { - arguments.processors[processor::interpolateCubic] = this->interpolationCubicRadioButton->get_active(); + Arguments::arguments.processors[processor::interpolateCubic] = this->interpolationCubicRadioButton->get_active(); this->csiProcessor.process(*this->csiProcessor.csiData[this->currentIndex]); this->refresh(); } @@ -115,7 +116,7 @@ void CsiProcessingWindow::interpolationCosineButtonClicked() { if (!this->csiProcessor.csiData.empty()) { - arguments.processors[processor::interpolateCosine] = this->interpolationCosineButton->get_active(); + Arguments::arguments.processors[processor::interpolateCosine] = this->interpolationCosineButton->get_active(); this->csiProcessor.process(*this->csiProcessor.csiData[this->currentIndex]); this->refresh(); } @@ -125,7 +126,7 @@ void CsiProcessingWindow::phaseLinearTransformCheckButtonClicked() { if (!this->csiProcessor.csiData.empty()) { - arguments.processors[processor::phaseCalibrationLinearTransform] = this->phaseLinearTransformCheckButton->get_active(); + Arguments::arguments.processors[processor::phaseCalibrationLinearTransform] = this->phaseLinearTransformCheckButton->get_active(); this->csiProcessor.process(*this->csiProcessor.csiData[this->currentIndex]); this->refresh(); } @@ -135,7 +136,7 @@ void CsiProcessingWindow::processingSaveGtkButtonClicked() { if (!this->csiProcessor.csiData.empty()) { - arguments.outputFile = "processedCsi.bin"; + Arguments::arguments.outputFile = "processedCsi.bin"; this->csiProcessor.saveCsi(); } } diff --git a/src/gui/GnuPlot.cpp b/src/gui/GnuPlot.cpp index c8f5924..0af3bcd 100644 --- a/src/gui/GnuPlot.cpp +++ b/src/gui/GnuPlot.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +17,12 @@ */ #include "gui/GnuPlot.h" -#include "main.h" +#include "Arguments.h" #include "GnuPlot.h" void GnuPlot::init() { - if (!arguments.plot || gnuPlotPipe) + if (!Arguments::arguments.plot || gnuPlotPipe) { return; } @@ -66,7 +66,7 @@ void GnuPlot::setBlank() void GnuPlot::updateChart(Csi &csi) { - if (!arguments.plot) + if (!Arguments::arguments.plot) { return; } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0c5f1cb..ce25b31 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +17,12 @@ */ #include "gui/MainWindow.h" -#include "main.h" #include #include "MainWindow.h" #include "MainController.h" #include "WiFIController.h" #include "Logger.h" +#include "Arguments.h" MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr const &builder) : Gtk::ApplicationWindow(obj), builder{builder} { @@ -157,71 +157,71 @@ void MainWindow::onResize() void MainWindow::frequencyChange() { int v = std::atoi(this->frequency->get_text().raw().c_str()); - arguments.frequency = (uint16_t)v; + Arguments::arguments.frequency = (uint16_t)v; } void MainWindow::channelWidthChange() { - arguments.bandwidth = this->channelWidth->get_active_text().raw(); - struct ChanMode chMode = WiFIController::getChanMode(arguments.bandwidth.c_str()); - arguments.channelWidth = WiFIController::chanModeToWidth(chMode); + Arguments::arguments.bandwidth = this->channelWidth->get_active_text().raw(); + struct ChanMode chMode = WiFIController::getChanMode(Arguments::arguments.bandwidth.c_str()); + Arguments::arguments.channelWidth = WiFIController::chanModeToWidth(chMode); } void MainWindow::outputFileChange() { - arguments.outputFile = this->outputFile->get_filename(); - this->filePath->set_text(arguments.outputFile); + Arguments::arguments.outputFile = this->outputFile->get_filename(); + this->filePath->set_text(Arguments::arguments.outputFile); } void MainWindow::formatChange() { - arguments.format = this->format->get_active_text().raw(); + Arguments::arguments.format = this->format->get_active_text().raw(); } void MainWindow::mcsChange() { int v = std::atoi(this->mcs->get_active_text().raw().c_str()); - arguments.mcs = (uint8_t)v; + Arguments::arguments.mcs = (uint8_t)v; } void MainWindow::spatialStreamsChange() { int v = std::atoi(this->spatialStreams->get_active_text().raw().c_str()); - arguments.spatialStreams = (uint8_t)v; + Arguments::arguments.spatialStreams = (uint8_t)v; } void MainWindow::ltfChange() { - arguments.ltf = this->ltf->get_active_text().raw(); + Arguments::arguments.ltf = this->ltf->get_active_text().raw(); } void MainWindow::guardIntervalChange() { int v = std::atoi(this->guardInterval->get_active_text().raw().c_str()); - arguments.guardInterval = (uint16_t)v; + Arguments::arguments.guardInterval = (uint16_t)v; } void MainWindow::txPowerChange() { int v = std::atoi(this->txPower->get_active_text().raw().c_str()); - arguments.txPower = (uint8_t)v; + Arguments::arguments.txPower = (uint8_t)v; } void MainWindow::codingChange() { - arguments.coding = this->coding->get_active_text().raw(); + Arguments::arguments.coding = this->coding->get_active_text().raw(); } void MainWindow::injectDelayChange() { int v = std::atoi(this->injectDelay->get_text().raw().c_str()); - arguments.injectDelay = (uint32_t)v; + Arguments::arguments.injectDelay = (uint32_t)v; } void MainWindow::injectRepeatChange() { int v = std::atoi(this->injectRepeat->get_text().raw().c_str()); - arguments.injectRepeat = (uint32_t)v; + Arguments::arguments.injectRepeat = (uint32_t)v; } void MainWindow::updateErrorMessages() diff --git a/src/main.cpp b/src/main.cpp index a131f5b..b963a6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* * FeitCSI is the tool for extracting CSI information from supported intel NICs. - * Copyright (C) 2023 Miroslav Hutar. + * Copyright (C) 2023-2024 Miroslav Hutar. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,6 @@ #include #include -#include #include #include "Csi.h" #include "WiFIController.h" @@ -28,308 +27,38 @@ #include "PacketInjector.h" #include "MainController.h" #include "Logger.h" - -struct Arguments arguments; - -const char *argp_program_version = - "argp-ex3 1.0"; -const char *argp_program_bug_address = - ""; - -/* Program documentation. */ -static char doc[] = - "Argp example #3 -- a program with options and arguments using argp"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "ARG1 ARG2"; - -/* The options we understand. */ -static struct argp_option options[] = { - {"frequency", 'f', "FREQUENCY", 0, "Frequency to measure/inject CSI"}, - {"channel-width", 'w', "CHANNELWIDTH", 0, "Channel width to measure/inject CSI. Possible values [20|40|HT40-|80|160]"}, - {"output-file", 'o', "FILE", 0, "Output file where measurements should be stored."}, - {"mcs", 'm', "MCS", 0, "Mcs index [0-11]"}, - {"format", 'r', "FORMAT", 0, "Data frame format [NOHT|HT|VHT|HESU]"}, - {"spatial-streams", 's', "SPATIALSTREAMS", 0, "Number of spatial streams [1|2]"}, - {"guard-interval", 'g', "GUARDINTERVAL", 0, "Guard interval in ns [400|800]"}, - {"ltf", 'l', "LTF", 0, "HE LTF [2xLTF+0.8|2xLTF+1.6|4xLTF+3.2|4xLTF+0.8]"}, - {"coding", 'c', "CODING", 0, "Coding scheme [LDPC|BCC]"}, - {"tx-power", 't', "TXPOWER", 0, "TX power of antenna in dBm [1-22]"}, - {"antenna", 'a', "ANTENNA", 0, "Transmitting antenna 1, 2 or 12 for both"}, - {"mode", 'i', "MODE", 0, "Mode of program[measure|inject|measureinject]"}, - {"inject-delay", 'd', "INJECTDELAY", 0, "Delay between frame injections is us"}, - {"inject-repeat", 'j', "INJECTREPEAT", 0, "How many times inject frame"}, - {"verbose", 'v', 0, OPTION_ARG_OPTIONAL, "Produce verbose output"}, - {"plot", 'p', 0, OPTION_ARG_OPTIONAL, "Plot CSI data"}, - {"gui", 'x', 0, OPTION_ARG_OPTIONAL, "Run application in GUI"}, - {0}}; - -/* Used by main to communicate with parse_opt. */ - -/* Parse a single option. */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - /* Get the input argument from argp_parse, which we - know is a pointer to our arguments structure. */ - struct Arguments *args = (struct Arguments *)state->input; - - switch (key) - { - case 'v': - args->verbose = true; - break; - case 'x': - args->gui = true; - break; - case 'p': - args->plot = true; - break; - case 'i': - { - args->mode.assign(arg); - if (args->mode == "measure") - { - args->measure = true; - } - else if (args->mode == "inject") - { - args->inject = true; - args->measure = false; - } - else if (args->mode == "measureinject") - { - args->measure = true; - args->inject = true; - } - else - { - argp_failure(state, 1, 0, "Bad mode. Possible values [measure|inject|measureinject]"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'm': - { - int mcs = std::atoi(arg); - if (mcs < 0 || mcs > 11) - { - argp_failure(state, 1, 0, "Bad MCS index. Possible values [0-11]"); - exit(ARGP_ERR_UNKNOWN); - } - args->mcs = (uint8_t)mcs; - break; - } - case 'r': - { - args->format.assign(arg); - if (args->format == "NOHT" || args->format == "HT" || args->format == "VHT" || args->format == "HESU") - { - } else - { - argp_failure(state, 1, 0, "Bad format. Possible values [NOHT|HT|VHT|HESU]"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'c': - { - args->coding.assign(arg); - if (args->coding == "LDPC" || args->coding == "BCC") - { - } - else - { - argp_failure(state, 1, 0, "Bad coding. Possible values [LDPC|BCC]"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'l': - { - args->ltf.assign(arg); - if (args->ltf == "1xLTF+0.8" || args->ltf == "2xLTF+0.8" || args->ltf == "2xLTF+1.6" || args->ltf == "4xLTF+3.2" || args->ltf == "4xLTF+0.8") - { - } else - { - argp_failure(state, 1, 0, "Bad LTF. Possible values [2xLTF+0.8|2xLTF+1.6|4xLTF+3.2|4xLTF+0.8]"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'g': - { - int gi = std::atoi(arg); - if (gi == 400 || gi == 800) - { - args->guardInterval = (uint16_t)gi; - } - else - { - argp_failure(state, 1, 0, "Bad guard interval. Possible values [400|800]"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'd': - { - int injd = std::atoi(arg); - if (injd <= 0) - { - argp_failure(state, 1, 0, "Inject delay is not correct number"); - exit(ARGP_ERR_UNKNOWN); - } - args->injectDelay = (uint32_t)injd; - break; - } - case 'j': - { - int injr = std::atoi(arg); - if (injr <= 0) - { - argp_failure(state, 1, 0, "Inject repeat is not correct number"); - exit(ARGP_ERR_UNKNOWN); - } - args->injectRepeat = (uint32_t)injr; - break; - } - case 's': - { - int ss = std::atoi(arg); - if (ss < 1 || ss > 2) - { - argp_failure(state, 1, 0, "Bad spatial stream. Possible values [1|2]"); - exit(ARGP_ERR_UNKNOWN); - } - args->spatialStreams = (uint8_t)ss; - break; - } - case 't': - { - int tx = std::atoi(arg); - if (tx < 1 || tx > 22) - { - argp_failure(state, 1, 0, "Bad tx power. Possible values [1-22]"); - exit(ARGP_ERR_UNKNOWN); - } - args->txPower = (uint8_t)tx; - break; - } - case 'a': - { - int a = std::atoi(arg); - if (a == 1) - { - args->antenna = RATE_MCS_ANT_A_MSK; - } - else if (a == 2) - { - args->antenna = RATE_MCS_ANT_B_MSK; - } - else if (a == 12) - { - args->antenna = RATE_MCS_ANT_AB_MSK; - } - else { - argp_failure(state, 1, 0, "Bad transmitting antenna value. Possible values 1, 2 or 12 for both"); - exit(ARGP_ERR_UNKNOWN); - } - break; - } - case 'f': - { - int f = std::atoi(arg); - if (f <= 0) - { - argp_failure(state, 1, 0, "Frequency is not correct"); - exit(ARGP_ERR_UNKNOWN); - } - args->frequency = (uint16_t)f; - break; - } - case 'w': - { - struct ChanMode chMode = WiFIController::getChanMode(arg); - if (chMode.width == 0) - { - argp_failure(state, 1, 0, "Bad bandwidth. Possible values of bandwidth are [20|40|HT40-|80|160]"); - exit(ARGP_ERR_UNKNOWN); - } - args->bandwidth = arg; - args->channelWidth = WiFIController::chanModeToWidth(chMode); - break; - } - case 'o': - args->outputFile = arg; - break; - case ARGP_KEY_ARG: - case ARGP_KEY_END: - if (args->frequency == 0 || - args->bandwidth.empty()) - { - argp_failure(state, 1, 0, "Fill required arguments -f -b . See --help for more information"); - exit(ARGP_ERR_UNKNOWN); - } - return 0; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc}; +#include "Arguments.h" int main(int argc, char *argv[]) { - struct Arguments defValues = { - .verbose = false, - .frequency = 2412, - .gui = false, - .plot = false, - .bandwidth = "20", - .mcs = 0, - .channelWidth = 20, - .spatialStreams = 1, - .txPower = 10, - .antenna = RATE_MCS_ANT_A_MSK, - .guardInterval = 400, - .injectDelay = 100000, - .injectRepeat = 0, - .coding = "LDPC", - .format = "HT", - .inject = false, - .measure = true, - .mode = "measure", - .ltf = "1xLTF+0.8", - }; + Arguments args; + args.init(); + args.parse(argc, argv); - arguments = defValues; - - if (arguments.outputFile.empty()) + if (Arguments::arguments.outputFile.empty()) { const auto t = std::chrono::system_clock::now(); int64_t tInt = std::chrono::duration_cast(t.time_since_epoch()).count(); - arguments.outputFile = "FeitCSI_" + std::to_string(tInt) + ".dat"; + Arguments::arguments.outputFile = "FeitCSI_" + std::to_string(tInt) + ".dat"; } - /* Default values. */ - - argp_parse(&argp, argc, argv, 0, 0, &arguments); - // all arguments ok and sanitized go next MainController *mainController = MainController::getInstance(); - if (!arguments.gui) + if (Arguments::arguments.gui) { - mainController->runNoGui(); - } else - { - arguments = defValues; mainController->runGui(); } + else if (Arguments::arguments.udpSocket) + { + mainController->runUdpSocket(); + } + else + { + mainController->runNoGui(); + } - if (arguments.verbose) + if (Arguments::arguments.verbose) { Logger::log(info) << "Exiting...\n"; }