/* * FeitCSI is the tool for extracting CSI information from supported intel NICs. * Copyright (C) 2023 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 #include #include #include #include "Csi.h" #include "WiFIController.h" #include "WiFiCsiController.h" #include "Netlink.h" #include "main.h" #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]"}, {"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 '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}; 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, .guardInterval = 400, .injectDelay = 100000, .injectRepeat = 0, .coding = "LDPC", .format = "HT", .inject = false, .measure = true, .mode = "measure", .ltf = "1xLTF+0.8", }; arguments = defValues; if (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"; } /* Default values. */ argp_parse(&argp, argc, argv, 0, 0, &arguments); // all arguments ok and sanitized go next MainController *mainController = MainController::getInstance(); if (!arguments.gui) { mainController->runNoGui(); } else { arguments = defValues; mainController->runGui(); } if (arguments.verbose) { Logger::log(info) << "Exiting...\n"; } return 0; } // lib to install compile libgtkmm-3.0-dev libnl-genl-3-dev libiw-dev libpcap-dev // fix default output file when GUI running