diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/CMakePresets.json b/PROJECTS/beginner/network-traffic-analyzer/cpp/CMakePresets.json new file mode 100644 index 00000000..1c9700f5 --- /dev/null +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/CMakePresets.json @@ -0,0 +1,22 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "release", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/Justfile b/PROJECTS/beginner/network-traffic-analyzer/cpp/Justfile new file mode 100644 index 00000000..2fe2ca33 --- /dev/null +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/Justfile @@ -0,0 +1,30 @@ +set shell := ["bash", "-uc"] + +default: + @just --list --unsorted + +build: + cmake -B build/debug -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + cmake --build build/debug + +run *ARGS: + sudo ./build/release/network-traffic-analyzer {{ARGS}} + +run-debug *ARGS: + sudo ./build/debug/network-traffic-analyzer {{ARGS}} + +capture interface="eth0": + sudo ./build/release/network-traffic-analyzer -i {{interface}} + +interfaces: + sudo ./build/release/network-traffic-analyzer --interfaces + +lint: + @sed -i 's/-fdeps-format=p1689r5//g; s/-fmodule-mapper=[^ ]*//g; s/-fmodules-ts//g' build/release/compile_commands.json + clang-tidy -p build/release src/**/*.cpp + +format: + find src include \( -name '*.cpp' -o -name '*.hpp' \) | xargs clang-format -i + +clean: + rm -rf build/ diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/README.md b/PROJECTS/beginner/network-traffic-analyzer/cpp/README.md index b0bd911f..4cdd2f9d 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/README.md +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/README.md @@ -26,6 +26,10 @@ Or grant capabilities: sudo setcap cap_net_raw,cap_net_admin=eip ./network-traffic-analyzer ``` +Or you can use `just` command +``` +just run + ``` --- # Features @@ -59,29 +63,28 @@ sudo setcap cap_net_raw,cap_net_admin=eip ./network-traffic-analyzer - FTXUI - CMake -# Build -``` -mkdir build && cd build -cmake .. -make +# Setup +## 1. clone the repo then +```bash +cd network-traffic-analyzer +./install.sh ``` # Usage Example ### Live capture on eth0 ``` -sudo ./network-traffic-analyzer -i eth0 +just capture -i eth0 ``` ### Capture 100 packets ``` -sudo ./network-traffic-analyzer -i wlan0 -c 100 +just run -i wlan0 -c 100 ``` ### Analyze offline pcap file ``` -sudo ./network-traffic-analyzer --offline traffic.pcap +just run --offline traffic.pcap ``` ### Export results (json / csv) ``` -sudo ./network-traffic-analyzer --json result.json --csv result.csv - +just run --json result.json --csv result.csv ``` diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/TUI/view.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/TUI/view.hpp index 29e78883..c6d9fb69 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/TUI/view.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/TUI/view.hpp @@ -4,37 +4,22 @@ #include class View { -public: - ftxui::Element render( - const StatsSnapshot& data, - const std::string& interface, - const std::string& filter, - bool capture_finished, - std::chrono::seconds timer - ); + public: + ftxui::Element render(const StatsSnapshot &data, const std::string &interface, const std::string &filter, + bool capture_finished, std::chrono::seconds timer); -private: - ftxui::Element render_header( - const StatsSnapshot& data, - const std::string& interface, - const std::string& filter - ); - ftxui::Element render_stats(const StatsSnapshot& data); + private: + ftxui::Element render_header(const StatsSnapshot &data, const std::string &interface, const std::string &filter); + ftxui::Element render_stats(const StatsSnapshot &data); - ftxui::Element render_transport(const StatsSnapshot& data); - ftxui::Element render_application(const StatsSnapshot& data); - ftxui::Element render_ip(const StatsSnapshot& data); - ftxui::Element render_pairs(const StatsSnapshot& data); - ftxui::Element render_bandwidth(const StatsSnapshot& data); - ftxui::Element render_packets(const StatsSnapshot& data); + ftxui::Element render_transport(const StatsSnapshot &data); + ftxui::Element render_application(const StatsSnapshot &data); + ftxui::Element render_ip(const StatsSnapshot &data); + ftxui::Element render_pairs(const StatsSnapshot &data); + ftxui::Element render_bandwidth(const StatsSnapshot &data); + ftxui::Element render_packets(const StatsSnapshot &data); - ftxui::Element render_footer( - bool capture_finished, - std::chrono::seconds timer - ); + ftxui::Element render_footer(bool capture_finished, std::chrono::seconds timer); }; - - - -#endif //VIEW_HPP +#endif // VIEW_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/capture/pcapCapture.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/capture/pcapCapture.hpp index 1320b558..9ace9b43 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/capture/pcapCapture.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/capture/pcapCapture.hpp @@ -1,28 +1,25 @@ #ifndef PCAPCAPTURE_HPP #define PCAPCAPTURE_HPP -#include -#include -#include -#include #include +#include +#include +#include +#include -//include C libraries -extern "C" { -#include -#include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include #define SNAP_LEN 1518 -} -#include "../packet/IP.hpp" #include "../../include/stats/protocolStats.hpp" +#include "../packet/IP.hpp" #include "../packet/packet.hpp" /** @@ -43,7 +40,7 @@ extern "C" { */ class PcapCapture { -private: + private: /* libpcap error buffer */ char errbuf[PCAP_ERRBUF_SIZE]; @@ -52,7 +49,10 @@ private: /* compiled filter program (expression) */ struct bpf_program fp = {}; /* Active pcap handle */ - pcap_t *handle = nullptr; + std::unique_ptr handle{nullptr, &pcap_close}; + void datalink_type(int type); + uint16_t offset = 0; + std::function get_ether_type; /* Network mask and IP */ bpf_u_int32 mask = 0; @@ -72,7 +72,7 @@ private: * Since libpcap expects a C function pointer, * we use a static function and forward the call * to the class instance. - */ + */ static void callback(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); // packet processing logic void got_packet(const struct pcap_pkthdr *header, const u_char *packet); @@ -80,28 +80,23 @@ private: /* Separate thread used for live capture */ std::thread thread; std::atomic running{false}; + void stop(); + Stats *stats; -public: - + public: + ~PcapCapture(); void print_interfaces(); - bool isRunning() { - return running; - } - void setRunning(bool running) { - this->running = running; - } + bool isRunning() { return running; } + void setRunning(bool running) { this->running = running; } /* Pointer to statistics engine */ - Stats* stats; - void set_capabilities(std::string& interface, int num_packets, std::string& filter_exp, int packets_limit, Stats* stats); + void set_capabilities(const std::string &interface, int num_packets, const std::string &filter_exp, + int packets_limit, Stats *stats); void initialize(); - void start(); - void start_offline(std::string fpath); - void stop(); + void start_offline(const std::string &fpath); }; - -#endif //PCAPCAPTURE_HPP +#endif // PCAPCAPTURE_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/argsParse.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/argsParse.hpp index 9795e854..03b8e742 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/argsParse.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/argsParse.hpp @@ -8,7 +8,7 @@ struct argsParser { po::variables_map vm; void print_help() const; - argsParser(int argc, char** argv); + argsParser(int argc, char **argv); }; -#endif //ARGSPARSE_HPP +#endif // ARGSPARSE_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/filter.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/filter.hpp index 95c66e24..da42f85e 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/filter.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/cli/filter.hpp @@ -3,24 +3,14 @@ #include #include -enum filter_type { - PROTOCOL, - PORT, - IP_TYPE, - IP_SRC, - IP_DEST, - NONE -}; +enum filter_type { PROTOCOL, PORT, IP_TYPE, IP_SRC, IP_DEST, NONE }; struct filter { filter_type type; std::string val; }; +filter parse(const std::string &str); +std::string get_bpf_filter(const std::vector &f); -filter parse(std::string str); -std::string get_bpf_filter(std::vector& f); - - - -#endif //FILTER_HPP +#endif // FILTER_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/IP.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/IP.hpp index c1a4860a..0ac5c288 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/IP.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/IP.hpp @@ -1,72 +1,75 @@ #ifndef IP_HPP #define IP_HPP -#include -#include -#include -#include #include "packet.hpp" +#include +#include +#include +#include /* virtual class for our IPv4, IPv6 classes */ class IP_class { -protected: + protected: virtual void handle_tcp() = 0; virtual void handle_udp() = 0; virtual void handle_icmp() = 0; virtual void handle_icmpv6() = 0; virtual void handle_igmp() = 0; + uint16_t payload_len = 0; -public: - //getters - virtual std::string get_source() = 0; - virtual std::string get_dest() = 0; + TransportProtocol protocol = TransportProtocol::UNKNOWN; + std::string src; + std::string dst; + + public: + std::string get_source(); + std::string get_dest(); + // getters + /*virtual std::string get_source() = 0; + virtual std::string get_dest() = 0;*/ virtual uint16_t get_src_port() = 0; virtual uint16_t get_dest_port() = 0; - virtual TransportProtocol get_protocol() = 0; - uint16_t get_payload_len(); + TransportProtocol get_protocol() const; + uint16_t get_payload_len() const; - const uint8_t* payload_ptr = nullptr; - const uint8_t* get_payload_ptr() const { return payload_ptr; } + const uint8_t *payload_ptr = nullptr; + const uint8_t *get_payload_ptr() const { return payload_ptr; } virtual ~IP_class() = default; - }; /*** ip4 ***/ class IPv4 : public IP_class { -private: - const ip* ip_hdr = nullptr; + private: + const ip *ip_hdr = nullptr; int ip_hdr_len = 0; uint16_t src_port = 0; uint16_t dest_port = 0; -protected: + + protected: void handle_tcp() override; void handle_udp() override; void handle_icmp() override; void handle_icmpv6() override; void handle_igmp() override; -public: - TransportProtocol get_protocol() override; - std::string get_source() override; - std::string get_dest() override; + public: uint16_t get_src_port() override; uint16_t get_dest_port() override; - explicit IPv4(const u_char* data); + explicit IPv4(const u_char *data); }; - - /*** ipv6 ***/ -class IPv6 : public IP_class{ -protected: +class IPv6 : public IP_class { + protected: void handle_tcp() override; void handle_udp() override; void handle_icmp() override; void handle_icmpv6() override; void handle_igmp() override; -private: - const ip6_hdr* ip_hdr = nullptr; + + private: + const ip6_hdr *ip_hdr = nullptr; int ip_hdr_len = 40; in6_addr ip_source; @@ -75,17 +78,13 @@ private: uint16_t src_port; uint16_t dest_port; - const uint8_t* ptr = nullptr; + const uint8_t *ptr = nullptr; -public: - explicit IPv6(const u_char* data); + public: + explicit IPv6(const u_char *data); - TransportProtocol get_protocol() override; - std::string get_source() override; - std::string get_dest() override; uint16_t get_src_port() override; uint16_t get_dest_port() override; - }; -#endif //IP_HPP +#endif // IP_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/packet.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/packet.hpp index 39e55bba..80bd8e94 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/packet.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/packet/packet.hpp @@ -1,10 +1,11 @@ #ifndef PACKET_HPP #define PACKET_HPP #include -#include #include +#include enum IPVersion { - v4, v6, + v4, + v6, }; enum class TransportProtocol { @@ -32,9 +33,9 @@ struct Packet { IPVersion ip_version; TransportProtocol transport_protocol; ApplicationProtocol application_protocol; - //src address + // src address std::string src; - //dest address + // dest address std::string dst; uint16_t src_port; uint16_t dst_port; @@ -42,18 +43,17 @@ struct Packet { uint32_t total_len; uint16_t payload_len; - const uint8_t* payload_ptr; + const uint8_t *payload_ptr; - Packet(IPVersion version, TransportProtocol protocol, std::string src, std::string dst, - uint16_t src_port, uint16_t dst_port, uint32_t total_len, uint16_t payload, const uint8_t* payload_ptr) : ip_version(version), transport_protocol(protocol), - src(std::move(src)), dst(std::move(dst)), src_port(src_port), dst_port(dst_port), total_len(total_len), payload_len(payload), - payload_ptr(payload_ptr) - { - this->application_protocol = get_application_protocol(); + Packet(IPVersion version, TransportProtocol protocol, std::string src, std::string dst, uint16_t src_port, + uint16_t dst_port, uint32_t total_len, uint16_t payload, const uint8_t *payload_ptr) + : ip_version(version), transport_protocol(protocol), src(std::move(src)), dst(std::move(dst)), + src_port(src_port), dst_port(dst_port), total_len(total_len), payload_len(payload), payload_ptr(payload_ptr) { + application_protocol = get_application_protocol(); + this->payload_ptr = nullptr; } - Packet() { } -private: + private: ApplicationProtocol get_application_protocol(); }; -#endif //PACKET_HPP +#endif // PACKET_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/stats/protocolStats.hpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/stats/protocolStats.hpp index 13e77383..dd40dad4 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/include/stats/protocolStats.hpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/include/stats/protocolStats.hpp @@ -1,17 +1,14 @@ #ifndef PROTOCOLSTATS_HPP #define PROTOCOLSTATS_HPP -#include -#include #include "../packet/packet.hpp" -#include +#include "ftxui/dom/elements.hpp" +#include #include -#include #include #include -#include "ftxui/dom/elements.hpp" +#include -using namespace ftxui; struct protocolStats { uint32_t packets = 0; uint32_t bytes = 0; @@ -43,14 +40,12 @@ struct StatsSnapshot { std::vector> packets_rows; uint32_t total_p = 0, total_b = 0; - //bandwidth + // bandwidth std::vector bandwidth_history; double bandwidth = 0; - mutable double max_bandwidth = 0; - + double max_bandwidth = 0; }; - /** * @brief Thread-safe statistics engine. * @@ -64,14 +59,13 @@ struct StatsSnapshot { * All write operations are protected by mutex. */ class Stats { -private: + private: std::mutex mtx; uint32_t last_b = 0; std::chrono::steady_clock::time_point last_tick; - std::unordered_map transport_map; std::unordered_map application_map; std::unordered_map ip_map; @@ -81,10 +75,10 @@ private: std::deque packets; int limit_packets = 10; + StatsSnapshot snapshot; -public: - - void push(const Packet& p) { + public: + void push(const Packet &p) { std::lock_guard lock(mtx); if (packets.size() > static_cast(limit_packets)) { packets.pop_front(); @@ -92,16 +86,17 @@ public: packets.push_back(p); } - StatsSnapshot snapshot; + StatsSnapshot get_snapshot() { + std::lock_guard lock(mtx); + return snapshot; + } void update_bandwidth(); double smooth_value(size_t i, size_t start); double smooth_bandwidth = 0.0; - void set_packets_limit(int limit) { - limit_packets = limit; - } + void set_packets_limit(int limit) { limit_packets = limit; } - void add_packet(Packet &packet); + void add_packet(const Packet &packet); void update_transport_stats(); void update_application_stats(); @@ -109,12 +104,10 @@ public: void update_pairs(size_t limit = 10); void update_packets(); - void export_csv(const std::string& filename); - void export_json(const std::string& filename); + void export_csv(const std::string &filename); + void export_json(const std::string &filename); Stats(); }; - - -#endif //PROTOCOLSTATS_HPP +#endif // PROTOCOLSTATS_HPP diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/install.sh b/PROJECTS/beginner/network-traffic-analyzer/cpp/install.sh new file mode 100644 index 00000000..cb1cbbc7 --- /dev/null +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/install.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +install_just() { + if command -v just &>/dev/null; then + echo "==> just already installed, skipping." + return + fi + + echo "==> Installing just..." + if command -v apt &>/dev/null; then + sudo apt install -y just || { + curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + } + elif command -v dnf &>/dev/null; then + sudo dnf install -y just + elif command -v pacman &>/dev/null; then + sudo pacman -S --needed just + else + curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + fi +} + +install_deps() { + echo "==> Installing build dependencies..." + if command -v apt &>/dev/null; then + sudo apt install -y libboost-program-options-dev libpcap-dev cmake ninja-build g++ clang-tidy clang-format + elif command -v dnf &>/dev/null; then + sudo dnf install -y boost-devel libpcap-devel cmake ninja-build gcc-c++ clang-tools-extra + elif command -v pacman &>/dev/null; then + sudo pacman -S --needed boost libpcap cmake ninja gcc clang + else + echo "Unsupported package manager. Install manually: boost, libpcap, cmake, ninja, g++, clang-tidy, clang-format" + exit 1 + fi + echo "==> Dependencies ready." +} + +build() { + echo "==> Configuring..." + cmake -B build/release -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + echo "==> Building..." + cmake --build build/release + echo "" + echo "Setup complete! Run 'just' to see available commands." +} + +install_just +install_deps +build diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/main.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/main.cpp index 7aa3910f..e161f318 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/main.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/main.cpp @@ -1,26 +1,22 @@ #include "include/capture/pcapCapture.hpp" -#include -#include -#include #include "include/cli/filter.hpp" -#include +#include #include #include +#include +#include +#include - -#include "include/cli/argsParse.hpp" #include "include/TUI/view.hpp" -#define SNAP_LEN 1518 +#include "include/cli/argsParse.hpp" -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { + /* initialize stats */ + Stats stats; /* initialize capture */ PcapCapture capture; capture.initialize(); - /* initialize stats */ - Stats stats; - /* initializing the command line parser */ argsParser parser(argc, argv); @@ -43,8 +39,8 @@ int main(int argc, char **argv) /* get a filter, use vector for multiple */ std::vector filters; if (parser.vm.contains("filter")) { - auto& f = parser.vm["filter"].as>(); - for (auto& x : f) { + auto &f = parser.vm["filter"].as>(); + for (auto &x : f) { filters.push_back(parse(x)); filterString += x + " "; } @@ -57,97 +53,102 @@ int main(int argc, char **argv) /* set the flags to capture engine */ capture.set_capabilities(interface, count, expression, limit, &stats); - std::atomic capture_finished = false; - + std::atomic ui_running = true; /* if we capture packets offline, we read the file in full, then print the result */ if (isOffline) { - capture.start_offline(parser.vm["offline"].as()); + capture.start_offline(parser.vm["offline"].as()); - /* full recalculation of statistics after file processing */ - stats.update_packets(); - stats.update_application_stats(); - stats.update_transport_stats(); - stats.update_ip_stats(10); - stats.update_pairs(); - stats.update_bandwidth(); - } + /* full recalculation of statistics after file processing */ + stats.update_packets(); + stats.update_application_stats(); + stats.update_transport_stats(); + stats.update_ip_stats(10); + stats.update_pairs(); + stats.update_bandwidth(); + } /* otherwise start live capture */ - else { - capture.start(); - } + else { + capture.start(); + } - /* UI */ - using namespace ftxui; - auto screen = ScreenInteractive::Fullscreen(); - /* our class for UI */ - View view; - - /* timer */ + /* UI */ + // Timer std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - std::chrono::seconds timer{0}; + std::atomic timer; + auto screen = ftxui::ScreenInteractive::Fullscreen(); - auto renderer = Renderer([&] { - auto snapshot = stats.snapshot; - return view.render(snapshot, - interface, - filterString, - capture_finished, - timer); - }); + /* our class for UI */ + View view; + std::mutex render_mtx; + std::mutex event_mtx; + ftxui::Element current_render = isOffline + ? view.render(stats.get_snapshot(), interface, filterString, true, timer.load()) + : ftxui::text("Starting capture..."); - auto component = CatchEvent(renderer, [&](Event e) { - if (e == Event::Character('q') || e == Event::Escape) { - capture.stop(); - screen.Exit(); - } - return true; - }); + std::mutex screen_mtx; + auto component = ftxui::Renderer([&] { + std::lock_guard lock(render_mtx); + return current_render; + }); - std::thread updater; + component |= ftxui::CatchEvent([&](ftxui::Event e) { + if (e == ftxui::Event::Character('q') || e == ftxui::Event::Escape) { + ui_running = false; + screen.Exit(); + return true; + } + return true; + }); + std::thread application_thread; + if (!isOffline) { + application_thread = std::thread([&] { + while (!capture_finished && ui_running) { - if (!isOffline) { - updater = std::thread([&] { - while (capture.isRunning()) { + auto now = std::chrono::steady_clock::now(); + timer.store(std::chrono::duration_cast(now - begin)); - stats.update_packets(); - stats.update_application_stats(); - stats.update_transport_stats(); - stats.update_ip_stats(10); - stats.update_pairs(); - stats.update_bandwidth(); + if (timer.load() >= std::chrono::seconds(time) || !capture.isRunning()) { + capture_finished = true; + } - screen.PostEvent(Event::Custom); - std::this_thread::sleep_for(std::chrono::milliseconds(300)); + stats.update_packets(); + stats.update_application_stats(); + stats.update_transport_stats(); + stats.update_ip_stats(10); + stats.update_pairs(); + stats.update_bandwidth(); - auto current_time = std::chrono::steady_clock::now(); - timer = std::chrono::duration_cast( - current_time - begin); + ftxui::Element new_frame = + view.render(stats.get_snapshot(), interface, filterString, capture_finished, timer.load()); + { + std::lock_guard lock(render_mtx); + current_render = new_frame; + } + if (ui_running) { + screen.PostEvent(ftxui::Event::Custom); + } - if (timer >= std::chrono::seconds(time)) - break; - } + // std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + }); + } - capture_finished = true; - screen.PostEvent(Event::Custom); - }); - } + screen.Loop(component); - screen.Loop(component); + ui_running = false; + capture_finished = true; - if (updater.joinable()) - updater.join(); + if (application_thread.joinable()) + application_thread.join(); - /* check export flags */ - if (parser.vm.contains("csv")) { - stats.export_csv(parser.vm["csv"].as()); - } - if (parser.vm.contains("json")) { - stats.export_json(parser.vm["json"].as()); - } + // Export stats if needed + if (parser.vm.contains("csv")) + stats.export_csv(parser.vm["csv"].as()); + if (parser.vm.contains("json")) + stats.export_json(parser.vm["json"].as()); - return 0; + return 0; } - diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/TUI/view.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/TUI/view.cpp index 588734d9..90c171c3 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/TUI/view.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/TUI/view.cpp @@ -1,53 +1,39 @@ #include "../../include/TUI/view.hpp" #include "ftxui/dom/table.hpp" - -ftxui::Element View::render(const StatsSnapshot& data, const std::string& interface, const std::string& filter, - bool capture_finished, std::chrono::seconds timer) { +using namespace ftxui; +ftxui::Element View::render(const StatsSnapshot &data, const std::string &interface, const std::string &filter, + bool capture_finished, std::chrono::seconds timer) { auto header = render_header(data, interface, filter); - auto transport_section = - hbox({ - render_transport(data) | flex, - separator(), - render_application(data) | flex, - separator(), - render_pairs(data) | flex, - }) | border; + auto transport_section = hbox({ + render_transport(data) | flex, + separator(), + render_application(data) | flex, + separator(), + render_pairs(data) | flex, + }) | + border; - auto ip_section = - hbox({ - render_ip(data) - | border - | size(HEIGHT, LESS_THAN, 10) - | frame - | vscroll_indicator, + auto ip_section = hbox({render_ip(data) | border | size(HEIGHT, LESS_THAN, 10) | frame | vscroll_indicator, - render_bandwidth(data) - | border - | flex - }); + render_bandwidth(data) | border | flex}); - auto left_panel = - vbox({ - transport_section, - separator(), - ip_section, - }) | flex_grow; + auto left_panel = vbox({ + transport_section, + separator(), + ip_section, + }) | + flex_grow; - auto right_panel = - render_packets(data) - | border - | size(WIDTH, EQUAL, 100) - | frame - | vscroll_indicator; + auto right_panel = render_packets(data) | border | size(WIDTH, EQUAL, 100) | frame | vscroll_indicator; - auto body = - hbox({ - left_panel, - separator(), - right_panel, - }) | flex; + auto body = hbox({ + left_panel, + separator(), + right_panel, + }) | + flex; auto footer = render_footer(capture_finished, timer); @@ -68,41 +54,33 @@ ftxui::Element View::render(const StatsSnapshot& data, const std::string& interf * - Active filter * - Traffic summary */ -ftxui::Element View::render_header(const StatsSnapshot& data, const std::string& interface, const std::string& filter) { +ftxui::Element View::render_header(const StatsSnapshot &data, const std::string &interface, const std::string &filter) { return hbox({ - vbox({ - text("Network Traffic Analyzer") | bold, - text("Interface: " + interface), - text("Filter: " + filter), - }) | flex, - separator(), - render_stats(data) | flex, - }) | border; + vbox({ + text("Network Traffic Analyzer") | bold, + text("Interface: " + interface), + text("Filter: " + filter), + }) | flex, + separator(), + render_stats(data) | flex, + }) | + border; } -ftxui::Element View::render_stats(const StatsSnapshot& data) { - return vbox({ - text("=== Traffic summary ===") | bold, - text("Total packets: " + std::to_string(data.total_p)), - text(std::format( - "Total bytes : {:.2f} MB", - data.total_b / (1024.0 * 1024.0) - )) - }) | flex; +ftxui::Element View::render_stats(const StatsSnapshot &data) { + return vbox({text("=== Traffic summary ===") | bold, text("Total packets: " + std::to_string(data.total_p)), + text(std::format("Total bytes : {:.2f} MB", data.total_b / (1024.0 * 1024.0)))}) | + flex; } - ftxui::Element View::render_footer(bool capture_finished, std::chrono::seconds timer) { - return Element({ - capture_finished - ? text("Capture finished (" + std::format("{}", timer) + "). Press 'q' or Esc to exit.") - | bold | color(Color::Yellow) | center - : text("time: " + std::format("{}", timer) + ". Press 'q' or Esc to exit.") | center - | size(HEIGHT, EQUAL, 1) - }); - + return Element({capture_finished + ? text("Capture finished (" + std::format("{}", timer) + "). Press 'q' or Esc to exit.") | + bold | color(Color::Yellow) | center + : text("time: " + std::format("{}", timer) + ". Press 'q' or Esc to exit.") | center | + size(HEIGHT, EQUAL, 1)}); } -ftxui::Element View::render_transport(const StatsSnapshot& data) { +ftxui::Element View::render_transport(const StatsSnapshot &data) { Table table(data.transport_rows); table.SelectAll().Border(LIGHT); @@ -111,12 +89,9 @@ ftxui::Element View::render_transport(const StatsSnapshot& data) { table.SelectRow(0).Decorate(bold); table.SelectRow(0).Border(DOUBLE); - return vbox({ - text("=== Transport protocols === ") | bold, - table.Render() - }) | flex; + return vbox({text("=== Transport protocols === ") | bold, table.Render()}) | flex; } -ftxui::Element View::render_application(const StatsSnapshot& data) { +ftxui::Element View::render_application(const StatsSnapshot &data) { Table table(data.app_rows); table.SelectAll().Border(LIGHT); @@ -125,12 +100,9 @@ ftxui::Element View::render_application(const StatsSnapshot& data) { table.SelectRow(0).Decorate(bold); table.SelectRow(0).Border(DOUBLE); - return vbox({ - text("=== Application protocols ===") | bold, - table.Render() - }) | flex; + return vbox({text("=== Application protocols ===") | bold, table.Render()}) | flex; } -ftxui::Element View::render_ip(const StatsSnapshot& data) { +ftxui::Element View::render_ip(const StatsSnapshot &data) { Table table(data.rows); table.SelectAll().Border(LIGHT); @@ -140,13 +112,12 @@ ftxui::Element View::render_ip(const StatsSnapshot& data) { table.SelectRow(0).Decorate(bold); table.SelectRow(0).Border(DOUBLE); - return vbox({ - text("=== Top IP addresses ===") | bold, + return vbox({text("=== Top IP addresses ===") | bold, - table.Render() - }) | flex; + table.Render()}) | + flex; } -ftxui::Element View::render_pairs(const StatsSnapshot& data) { +ftxui::Element View::render_pairs(const StatsSnapshot &data) { Table table(data.pairs_rows); table.SelectAll().Border(LIGHT); @@ -155,10 +126,7 @@ ftxui::Element View::render_pairs(const StatsSnapshot& data) { table.SelectRow(0).Decorate(bold); table.SelectRow(0).Border(DOUBLE); - return vbox({ - text("=== Top communication pairs ===") | bold, - table.Render() - }) | flex; + return vbox({text("=== Top communication pairs ===") | bold, table.Render()}) | flex; } /** * @brief Renders bandwidth graph. @@ -167,8 +135,7 @@ ftxui::Element View::render_pairs(const StatsSnapshot& data) { * Scales dynamically based on max bandwidth. */ -ftxui::Element View::render_bandwidth(const StatsSnapshot& data) { - using namespace ftxui; +ftxui::Element View::render_bandwidth(const StatsSnapshot &data) { GraphFunction fn = [this, data](int width, int height) { std::vector output(width, 0); @@ -179,7 +146,6 @@ ftxui::Element View::render_bandwidth(const StatsSnapshot& data) { size_t n = data.bandwidth_history.size(); size_t start = n > 50 ? n - 50 : 0; - double max_bw = 1.0; for (size_t i = start; i < n; ++i) max_bw = std::max(max_bw, data.bandwidth_history[i].bytes_per_sec); @@ -188,16 +154,13 @@ ftxui::Element View::render_bandwidth(const StatsSnapshot& data) { double t = (double)x / (width - 1); - double idx_f = start + t * (n - start - 1); size_t i0 = (size_t)idx_f; size_t i1 = std::min(i0 + 1, n - 1); - double frac = idx_f - i0; - double bw = - data.bandwidth_history[i0].bytes_per_sec * (1.0 - frac) + - data.bandwidth_history[i1].bytes_per_sec * frac; + double bw = data.bandwidth_history[i0].bytes_per_sec * (1.0 - frac) + + data.bandwidth_history[i1].bytes_per_sec * frac; double v = bw / max_bw; output[x] = static_cast(v * (height - 1)); @@ -207,13 +170,10 @@ ftxui::Element View::render_bandwidth(const StatsSnapshot& data) { }; return vbox({ text(std::format(" Bandwidth: {:.2f} KB / max: {:.2f} KB", data.bandwidth, data.max_bandwidth)) | bold, - graph(fn) - | size(HEIGHT, EQUAL, 20) | size(WIDTH, EQUAL, 60) - | border - | color(Color::Green), -}) ; + graph(fn) | size(HEIGHT, EQUAL, 20) | size(WIDTH, EQUAL, 60) | border | color(Color::Green), + }); } -ftxui::Element View::render_packets(const StatsSnapshot& data) { +ftxui::Element View::render_packets(const StatsSnapshot &data) { Table table(data.packets_rows); table.SelectAll().Border(LIGHT); @@ -221,9 +181,8 @@ ftxui::Element View::render_packets(const StatsSnapshot& data) { table.SelectRow(0).SeparatorVertical(LIGHT); table.SelectRow(0).Decorate(bold); table.SelectRow(0).Border(DOUBLE); - return vbox({ - text("=== Packets ===") | bold, + return vbox({text("=== Packets ===") | bold, - table.Render() | flex - }) | flex; + table.Render() | flex}) | + flex; } \ No newline at end of file diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/capture/pcapCapture.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/capture/pcapCapture.cpp index c5ed7334..33d541b9 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/capture/pcapCapture.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/capture/pcapCapture.cpp @@ -1,102 +1,120 @@ #include "../../include/capture/pcapCapture.hpp" #include "../../include/stats/protocolStats.hpp" - /* get a list of all available network interfaces */ void PcapCapture::initialize() { /* find all devs available in network, save them to pcap_if_t struct (interfaces) */ if (pcap_findalldevs(&interfaces, errbuf) == -1) { - fprintf(stderr, "Error: pcap_findalldevs has been failed - %s\n", errbuf); + throw std::runtime_error("Error: pcap_findalldevs has been failed"); + /*fprintf(stderr, "Error: pcap_findalldevs has been failed - %s\n", errbuf);*/ } } + +void PcapCapture::datalink_type(int type) { + switch (type) { + + case DLT_EN10MB: { + offset = 14; + get_ether_type = [](const u_char *p) { + auto *eth = reinterpret_cast(p); + return ntohs(eth->ether_type); + }; + break; + } + + case DLT_LINUX_SLL: { + offset = 16; + get_ether_type = [](const u_char *p) { return ntohs(*reinterpret_cast(p + 14)); }; + break; + } + + case DLT_LINUX_SLL2: { + offset = 20; + get_ether_type = [](const u_char *p) { return ntohs(*reinterpret_cast(p + 18)); }; + break; + } + default: + throw std::runtime_error("Unsupported datalink type"); + } +} + /** - * Start live packet capture. - * - * Steps: - * 1. Resolve network mask - * 2. Open device in promiscuous mode - * 3. Compile and apply BPF filter (if provided) - * 4. Start pcap_loop in a separate thread - */ + * Start live packet capture. + * + * Steps: + * 1. Resolve network mask + * 2. Open device in promiscuous mode + * 3. Compile and apply BPF filter (if provided) + * 4. Start pcap_loop in a separate thread + */ void PcapCapture::start() { // getting the netmask of the interface if (pcap_lookupnet(interface.c_str(), &net, &mask, errbuf) == -1) { - fprintf(stderr, "Couldn't get netmask for device %s: %s\n", - interface.c_str(), errbuf); + fprintf(stderr, "Couldn't get netmask for device %s: %s\n", interface.c_str(), errbuf); net = 0; mask = 0; } /* open capture device */ - handle = pcap_open_live(interface.c_str(), SNAP_LEN, 1, 1000, errbuf); + handle.reset(pcap_open_live(interface.c_str(), SNAP_LEN, 1, 1000, errbuf)); if (handle == nullptr) { - fprintf(stderr, "Couldn't open device %s: %s\n", interface.c_str(), errbuf); - exit(EXIT_FAILURE); + throw std::runtime_error("Couldn't open device " + interface + ": " + errbuf); } - if (pcap_datalink(handle) != DLT_EN10MB) { - fprintf(stderr, "%s is not an Ethernet\n", interface.c_str()); - exit(EXIT_FAILURE); - } + datalink_type(pcap_datalink(handle.get())); + if (!filter_exp.empty()) { /* compile the filter expression */ - if (pcap_compile(handle, &fp, filter_exp.c_str(), 0, net) == -1) { - fprintf(stderr, "Couldn't parse filter %s: %s\n", - filter_exp.c_str(), pcap_geterr(handle)); - exit(EXIT_FAILURE); + if (pcap_compile(handle.get(), &fp, filter_exp.c_str(), 0, net) == -1) { + throw std::runtime_error("Couldn't parse filter " + filter_exp + ": " + pcap_geterr(handle.get())); } - /* apply the compiled filter */ - if (pcap_setfilter(handle, &fp) == -1) { - fprintf(stderr, "Couldn't install filter %s: %s\n", - filter_exp.c_str(), pcap_geterr(handle)); - exit(EXIT_FAILURE); + if (pcap_setfilter(handle.get(), &fp) == -1) { + throw std::runtime_error("Couldn't install filter " + filter_exp + ": " + pcap_geterr(handle.get())); } - pcap_freecode(&fp); } - - /* start a separate thread */ running = true; thread = std::thread([this]() { - if (pcap_loop(handle, num_packets, &PcapCapture::callback, reinterpret_cast(this)) < 0) { - //fprintf(stderr, "Error in pcap_loop: %s\n", pcap_geterr(handle)); - //pcap_close(handle); + if (pcap_loop(handle.get(), num_packets, &PcapCapture::callback, reinterpret_cast(this)) < 0) { + // fprintf(stderr, "Error in pcap_loop: %s\n", pcap_geterr(handle)); + // pcap_close(handle); + // throw std::runtime_error("Couldn't start capture"); } running = false; }); - } - +PcapCapture::~PcapCapture() { stop(); } void PcapCapture::stop() { - if (interfaces) { - pcap_freealldevs(interfaces); - } - if (handle) { - if (running == true) pcap_breakloop(handle); - pcap_close(handle); - //handle = nullptr; - } + pcap_freecode(&fp); + if (!handle) + return; - if (thread.joinable()) { + running = false; + + pcap_breakloop(handle.get()); + + if (thread.joinable()) thread.join(); - } - /*if (!filter_exp.empty()) { - pcap_freecode(&fp); - }*/ + handle.reset(); + + if (interfaces) { + pcap_freealldevs(interfaces); + interfaces = nullptr; + } } + /* print all available interfaces */ void PcapCapture::print_interfaces() { int i = 0; for (pcap_if_t *dev = interfaces; dev; dev = dev->next) { - printf("%d. %s ",++i, dev->name); + printf("%d. %s ", ++i, dev->name); if (dev->description) { printf("(%s)\n", dev->description); - } - else { + } else { printf("\n"); } } @@ -111,13 +129,10 @@ void PcapCapture::print_interfaces() { * @param header Packet metadata * @param packet Raw packet bytes */ -void PcapCapture::callback( - u_char* user, - const struct pcap_pkthdr* header, - const u_char* packet -) { - auto* self = reinterpret_cast(user); - if (!self->isRunning()) return; +void PcapCapture::callback(u_char *user, const struct pcap_pkthdr *header, const u_char *packet) { + auto *self = reinterpret_cast(user); + if (!self->isRunning()) + return; self->got_packet(header, packet); } @@ -135,49 +150,41 @@ void PcapCapture::callback( * Other Ethernet types are ignored. */ void PcapCapture::got_packet(const struct pcap_pkthdr *header, const u_char *packet) { - if (!running) return; + if (!running) + return; - // --- Ethernet header --- - const auto* ethernet = reinterpret_cast(packet); - uint16_t ether_type = ntohs(ethernet->ether_type); - - Packet packetView; + // --- Ethernet header --- + // const auto* ethernet = reinterpret_cast(packet + offset); + uint16_t ether_type = get_ether_type(packet); /* if we have a ipv4 type */ if (ether_type == ETHERTYPE_IP) { - IPv4 ip(packet + sizeof(struct ether_header)); + IPv4 ip(packet + offset); TransportProtocol prot = ip.get_protocol(); - packetView = Packet(v4, prot, ip.get_source(), ip.get_dest(), ip.get_src_port(), ip.get_dest_port(), header->len, ip.get_payload_len(), ip.get_payload_ptr()); + Packet packetView(v4, prot, ip.get_source(), ip.get_dest(), ip.get_src_port(), ip.get_dest_port(), header->len, + ip.get_payload_len(), ip.get_payload_ptr()); stats->add_packet(packetView); stats->push(packetView); - } /* ipv6 type */ - if (ether_type == ETHERTYPE_IPV6) { - IPv6 ip(packet + sizeof(struct ether_header)); + else if (ether_type == ETHERTYPE_IPV6) { + IPv6 ip(packet + offset); TransportProtocol prot = ip.get_protocol(); - packetView = Packet(v6, prot, ip.get_source(), ip.get_dest(), ip.get_src_port(), ip.get_dest_port(), header->len, ip.get_payload_len(), ip.get_payload_ptr()); + Packet packetView(v6, prot, ip.get_source(), ip.get_dest(), ip.get_src_port(), ip.get_dest_port(), header->len, + ip.get_payload_len(), ip.get_payload_ptr()); stats->add_packet(packetView); stats->push(packetView); - - } - if (ether_type == ETHERTYPE_VLAN) { - ethernet = (ether_header*)(packet + 4); - - } - if (ether_type == ETHERTYPE_ARP) { - } } -void PcapCapture::set_capabilities(std::string& interface, int num_packets, std::string& filter_exp, int packets_limit, Stats* stats) { +void PcapCapture::set_capabilities(const std::string &interface, int num_packets, const std::string &filter_exp, + const int packets_limit, Stats *stats) { this->interface = interface; this->num_packets = num_packets; this->filter_exp = filter_exp; this->stats = stats; this->stats->set_packets_limit(packets_limit); - } /** * @brief Processes packets from an offline .pcap file. @@ -189,18 +196,17 @@ void PcapCapture::set_capabilities(std::string& interface, int num_packets, std: * * Used for post-capture analysis and exporting results. */ -void PcapCapture::start_offline(std::string fpath) { - handle = pcap_open_offline(fpath.c_str(), errbuf); +void PcapCapture::start_offline(const std::string &fpath) { + handle.reset(pcap_open_offline(fpath.c_str(), errbuf)); if (handle == nullptr) { fprintf(stderr, "Error opening offline file: %s\n", errbuf); return; } + datalink_type(pcap_datalink(handle.get())); running = true; - pcap_loop(handle, num_packets, - &PcapCapture::callback, - reinterpret_cast(this)); + pcap_loop(handle.get(), num_packets, &PcapCapture::callback, reinterpret_cast(this)); running = false; } \ No newline at end of file diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/argsParse.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/argsParse.cpp index 3e10a120..413a4af0 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/argsParse.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/argsParse.cpp @@ -1,64 +1,54 @@ #include "../../include/cli/argsParse.hpp" #include -argsParser::argsParser(int argc, char** argv) { - desc.add_options() - ("help,h", "Display this help message and exit") - ("interfaces, interfaces","Display all possible interfaces") - ("interface,i", po::value()->default_value("wlan0"), - "Network interface to capture packets from (e.g. eth0, wlan0, any)") +argsParser::argsParser(int argc, char **argv) { + desc.add_options()("help,h", "Display this help message and exit")("interfaces, interfaces", + "Display all possible interfaces")( + "interface,i", po::value()->default_value("wlan0"), + "Network interface to capture packets from (e.g. eth0, wlan0, any)") - ("count,c", po::value()->default_value(0), - "Number of packets to capture (0 = unlimited)") - ("time, t", po::value()->default_value(INT_MAX),"Working time (in seconds)") + ("count,c", po::value()->default_value(0), "Number of packets to capture (0 = unlimited)")( + "time, t", po::value()->default_value(INT_MAX), "Working time (in seconds)") - ("offline,r", po::value(), - "Read packets from an offline pcap file") + ("offline,r", po::value(), "Read packets from an offline pcap file") - ("filter,f", po::value>()->composing(), - "Traffic filter (can be used multiple times)\n" - " proto: tcp | udp | icmp | dns\n" - " src: Source IP address\n" - " dst: Destination IP address\n" - " port: Source or destination port") + ("filter,f", po::value>()->composing(), + "Traffic filter (can be used multiple times)\n" + " proto: tcp | udp | icmp | dns\n" + " src: Source IP address\n" + " dst: Destination IP address\n" + " port: Source or destination port") - ("sort,s", po::value()->default_value("bytes"), - "Sort field: bytes | packets | ip") + ("sort,s", po::value()->default_value("bytes"), "Sort field: bytes | packets | ip") - ("order,o", po::value()->default_value("desc"), - "Sort order: asc | desc") + ("order,o", po::value()->default_value("desc"), "Sort order: asc | desc") - ("limit,n", po::value()->default_value(43), - "Limit number of displayed entries") + ("limit,n", po::value()->default_value(43), "Limit number of displayed entries") - ("csv", po::value(), - "Export analysis results to CSV file") + ("csv", po::value(), "Export analysis results to CSV file") - ("json", po::value(), - "Export analysis results to JSON file"); + ("json", po::value(), "Export analysis results to JSON file"); po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); } void argsParser::print_help() const { - std::cout << - "Network Traffic Analyzer\n" - "========================\n\n" - "Usage:\n" - " ./network-traffic-analyzer [options]\n\n" - "Description:\n" - " Captures and analyzes network traffic from live interfaces or\n" - " offline pcap files. Provides protocol statistics, top talkers,\n" - " and bandwidth usage information.\n\n"; + std::cout << "Network Traffic Analyzer\n" + "========================\n\n" + "Usage:\n" + " ./network-traffic-analyzer [options]\n\n" + "Description:\n" + " Captures and analyzes network traffic from live interfaces or\n" + " offline pcap files. Provides protocol statistics, top talkers,\n" + " and bandwidth usage information.\n\n"; std::cout << desc << "\n"; - std::cout << - "Examples:\n" - " ./network-traffic-analyzer -i wlan0 --count 100 --time 10\n" - " ./network-traffic-analyzer -i any --filter port:54\n" - " ./network-traffic-analyzer --offline traffic.pcap --json result.json\n\n"; + std::cout << "Examples:\n" + " ./network-traffic-analyzer -i wlan0 --count 100 --time 10\n" + " ./network-traffic-analyzer -i any --filter port:54\n" + " ./network-traffic-analyzer --offline traffic.pcap --json result.json\n\n"; std::cout << "To end the program, press 'q' or Esc to exit.\n"; } \ No newline at end of file diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/filter.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/filter.cpp index 12dc1570..dc85929d 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/filter.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/cli/filter.cpp @@ -1,60 +1,81 @@ #include "../../include/cli/filter.hpp" - #include +#include -filter parse(std::string str) { +filter parse(const std::string &str) { auto pos = str.find(':'); if (pos == std::string::npos) { - + throw std::invalid_argument("Invalid filter format: '" + str + "' (expected key:value)"); } std::string type = str.substr(0, pos); std::string value = str.substr(pos + 1); - if (type == "protocol") return {PROTOCOL, value}; - if (type == "port") return {PORT, value}; - if (type == "dest") return {IP_DEST, value}; - if (type == "src") return {IP_SRC, value}; - if (type == "ip") return {IP_TYPE, value}; + if (type == "protocol") + return {PROTOCOL, value}; + if (type == "port") + return {PORT, value}; + if (type == "dest") + return {IP_DEST, value}; + if (type == "src") + return {IP_SRC, value}; + if (type == "ip") + return {IP_TYPE, value}; return {NONE, value}; } -std::string get_bpf_filter(std::vector& f) { +std::string get_bpf_filter(const std::vector &f) { std::map> groups; - for (const auto& x : f) { + for (const auto &x : f) { switch (x.type) { - case PROTOCOL: - if (x.val == "dns") - groups[PROTOCOL].push_back("port 53"); - else - groups[PROTOCOL].push_back(x.val); - break; + case PROTOCOL: + if (x.val == "dns") + groups[PROTOCOL].emplace_back("port 53"); + else if (x.val == "http") + groups[PROTOCOL].emplace_back("port 80"); + else if (x.val == "https") + groups[PROTOCOL].emplace_back("port 443"); + else if (x.val == "ssh") + groups[PROTOCOL].emplace_back("port 22"); + else if (x.val == "ftp") + groups[PROTOCOL].emplace_back("port 21"); + else if (x.val == "smtp") + groups[PROTOCOL].emplace_back("port 25"); + else + groups[PROTOCOL].push_back(x.val); + break; - case IP_DEST: - groups[IP_DEST].push_back("dst host " + x.val); - break; + case IP_DEST: + groups[IP_DEST].push_back("dst host " + x.val); + break; - case IP_SRC: - groups[IP_SRC].push_back("src host " + x.val); - break; + case IP_SRC: + groups[IP_SRC].push_back("src host " + x.val); + break; - case PORT: - groups[PORT].push_back("port " + x.val); - break; - case IP_TYPE: - groups[IP_TYPE].push_back(x.val); - break; + case PORT: + groups[PORT].push_back("port " + x.val); + break; + case IP_TYPE: { + if (x.val == "v4" || x.val == "4" || x.val == "ipv4") + groups[IP_TYPE].emplace_back("ip"); + else if (x.val == "v6" || x.val == "6" || x.val == "ipv6") + groups[IP_TYPE].emplace_back("ip6"); + else + throw std::invalid_argument("Unknown IP type: '" + x.val + "'"); + break; + } - default: - break; + default: + break; } } std::string result; bool first_group = true; - for (auto& [type, parts] : groups) { + for (auto &[type, parts] : groups) { if (!first_group) result += " and "; first_group = false; @@ -74,4 +95,3 @@ std::string get_bpf_filter(std::vector& f) { return result; } - diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/IP.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/IP.cpp index 01e8164b..eb9eaf1f 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/IP.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/IP.cpp @@ -1,211 +1,176 @@ #include "../../include/packet/IP.hpp" -#include #include +#include +#include #include #include #include #include +#include +uint16_t IP_class::get_payload_len() const { return payload_len; } -uint16_t IP_class::get_payload_len() { - return payload_len; -} - +TransportProtocol IP_class::get_protocol() const { return protocol; } +std::string IP_class::get_source() { return src; } +std::string IP_class::get_dest() { return dst; } /*** Ipv4 ***/ -IPv4::IPv4(const u_char* data) { - ip_hdr = reinterpret_cast(data); +IPv4::IPv4(const u_char *data) { + ip_hdr = reinterpret_cast(data); + + src = inet_ntoa(ip_hdr->ip_src); + dst = inet_ntoa(ip_hdr->ip_dst); + ip_hdr_len = ip_hdr->ip_hl * 4; if (ip_hdr_len < 20) { - fprintf(stderr, "Failed to initial IPv4 "); + throw std::runtime_error("Failed to initial IPv4 "); } - - -} - -TransportProtocol IPv4::get_protocol() { - switch (ip_hdr->ip_p) { - case IPPROTO_TCP: - handle_tcp(); - return TransportProtocol::TCP; + case IPPROTO_TCP: + IPv4::handle_tcp(); + break; + case IPPROTO_UDP: + IPv4::handle_udp(); + break; - case IPPROTO_UDP: - handle_udp(); - return TransportProtocol::UDP; + case IPPROTO_ICMP: + IPv4::handle_icmp(); + break; - case IPPROTO_ICMP: - handle_icmp(); - return TransportProtocol::ICMP; - - case IPPROTO_ICMPV6: - handle_icmpv6(); - return TransportProtocol::ICMP6; - - case IPPROTO_IGMP: - handle_igmp(); - return TransportProtocol::IGMP; - - default: - return TransportProtocol::UNKNOWN; + case IPPROTO_ICMPV6: + IPv4::handle_icmpv6(); + break; + case IPPROTO_IGMP: + IPv4::handle_igmp(); + break; + default: + protocol = TransportProtocol::UNKNOWN; + break; } } + void IPv4::handle_tcp() { - auto* tcp = reinterpret_cast((u_char*)ip_hdr + ip_hdr_len); + const auto *tcp = reinterpret_cast(reinterpret_cast(ip_hdr) + ip_hdr_len); src_port = ntohs(tcp->source); dest_port = ntohs(tcp->dest); - payload_ptr = reinterpret_cast(tcp + tcp->doff * 4); - + payload_ptr = reinterpret_cast(tcp) + tcp->doff * 4; payload_len = ntohs(ip_hdr->ip_len) - (ip_hdr_len + tcp->doff * 4); + + protocol = TransportProtocol::TCP; } void IPv4::handle_udp() { - auto* udp = reinterpret_cast((u_char*)ip_hdr + ip_hdr_len); + const auto *udp = reinterpret_cast(reinterpret_cast(ip_hdr) + ip_hdr_len); dest_port = ntohs(udp->dest); src_port = ntohs(udp->source); - payload_ptr = (u_char*)udp + sizeof(udphdr); + payload_ptr = reinterpret_cast(udp) + sizeof(udphdr); payload_len = ntohs(udp->len) - sizeof(udphdr); -} -void IPv4::handle_icmp() { - auto* icmp = reinterpret_cast((u_char*)ip_hdr + ip_hdr_len); + protocol = TransportProtocol::UDP; +} +void IPv4::handle_icmp() { protocol = TransportProtocol::ICMP; } +void IPv4::handle_icmpv6() { protocol = TransportProtocol::ICMP6; } +void IPv4::handle_igmp() { protocol = TransportProtocol::IGMP; } -} -void IPv4::handle_icmpv6() { - auto* icmp = reinterpret_cast((u_char*)ip_hdr + ip_hdr_len); - -} -void IPv4::handle_igmp() { - auto* igmp = reinterpret_cast((u_char*)ip_hdr + ip_hdr_len); - -} - -std::string IPv4::get_source() { - return std::string(inet_ntoa(ip_hdr->ip_src)); -} -std::string IPv4::get_dest() { - return std::string(inet_ntoa(ip_hdr->ip_dst)); -} -uint16_t IPv4::get_src_port() { - return src_port; -} -uint16_t IPv4::get_dest_port() { - return dest_port; -} +uint16_t IPv4::get_src_port() { return src_port; } +uint16_t IPv4::get_dest_port() { return dest_port; } /*** Ipv6 ***/ -IPv6::IPv6(const u_char* data) { - ip_hdr = reinterpret_cast(data); - -} - -TransportProtocol IPv6::get_protocol() { +IPv6::IPv6(const u_char *data) { + ip_hdr = reinterpret_cast(data); uint8_t hdr = ip_hdr->ip6_nxt; + std::array src{}; + inet_ntop(AF_INET6, &ip_hdr->ip6_src, src.data(), sizeof(src)); + this->src = src.data(); - ptr = reinterpret_cast(ip_hdr + 1); + std::array dst{}; + inet_ntop(AF_INET6, &ip_hdr->ip6_dst, dst.data(), sizeof(dst)); + this->dst = dst.data(); + + ptr = reinterpret_cast(ip_hdr + 1); while (true) { switch (hdr) { - case IPPROTO_TCP: - handle_tcp(); - return TransportProtocol::TCP; + case IPPROTO_TCP: + IPv6::handle_tcp(); + return; + case IPPROTO_UDP: + IPv6::handle_udp(); + return; + case IPPROTO_ICMP: + IPv6::handle_icmp(); + return; + case IPPROTO_ICMPV6: + IPv6::handle_icmpv6(); + return; - case IPPROTO_UDP: - handle_udp(); - return TransportProtocol::UDP; + case IPPROTO_IGMP: + IPv6::handle_igmp(); + return; + /* if we have an extension headers, dont leave from loop, + * keep find a protocol type + */ + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: { + const auto *ext = reinterpret_cast(ptr); + hdr = ext->ip6e_nxt; + ptr += (ext->ip6e_len + 1) * 8; + break; + } + case IPPROTO_FRAGMENT: { - case IPPROTO_ICMP: - handle_icmp(); - return TransportProtocol::ICMP; - - case IPPROTO_ICMPV6: - handle_icmpv6(); - return TransportProtocol::ICMP6; - - case IPPROTO_IGMP: - handle_igmp(); - return TransportProtocol::IGMP; - /* if we have an extension headers, dont leave from loop, - * keep find a protocol type - */ - case IPPROTO_HOPOPTS: - case IPPROTO_ROUTING: - case IPPROTO_DSTOPTS: { - const auto* ext = reinterpret_cast(ptr); - hdr = ext->ip6e_nxt; - ptr += (ext->ip6e_len + 1) * 8; - break; - } - case IPPROTO_FRAGMENT: { - - const auto* frag = reinterpret_cast(ptr); - hdr = frag->ip6f_nxt; - ptr += sizeof(ip6_frag); - break; - } - default: - return TransportProtocol::UNKNOWN; + const auto *frag = reinterpret_cast(ptr); + hdr = frag->ip6f_nxt; + ptr += sizeof(ip6_frag); + break; + } + default: + protocol = TransportProtocol::UNKNOWN; + return; } } ptr = nullptr; } void IPv6::handle_tcp() { - const auto tcp = reinterpret_cast(ptr); + const auto tcp = reinterpret_cast(ptr); dest_port = ntohs(tcp->dest); src_port = ntohs(tcp->source); - payload_ptr = (const uint8_t*)tcp + tcp->doff * 4; + payload_ptr = reinterpret_cast(tcp) + tcp->doff * 4; payload_len = ntohs(ip_hdr->ip6_plen) - tcp->doff * 4; + protocol = TransportProtocol::TCP; ptr = nullptr; - } void IPv6::handle_udp() { - const auto udp = reinterpret_cast(ptr); + const auto udp = reinterpret_cast(ptr); dest_port = ntohs(udp->dest); src_port = ntohs(udp->source); - payload_ptr = (const uint8_t*)udp + sizeof(udphdr); + payload_ptr = reinterpret_cast(udp) + sizeof(udphdr); payload_len = ntohs(udp->len) - sizeof(udphdr); + protocol = TransportProtocol::UDP; ptr = nullptr; - } void IPv6::handle_icmp() { - + protocol = TransportProtocol::ICMP; ptr = nullptr; } void IPv6::handle_icmpv6() { + protocol = TransportProtocol::ICMP6; payload_len = ntohs(ip_hdr->ip6_plen) - sizeof(icmp6_hdr); ptr = nullptr; } void IPv6::handle_igmp() { - + protocol = TransportProtocol::IGMP; ptr = nullptr; } -std::string IPv6::get_source() { - char src[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &ip_hdr->ip6_src, src, sizeof(src)); - return std::string(src); -} - -std::string IPv6::get_dest() { - char dst[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &ip_hdr->ip6_dst, dst, sizeof(dst)); - return std::string(dst); -} - -uint16_t IPv6::get_src_port() { - return src_port; -} - -uint16_t IPv6::get_dest_port() { - return dest_port; -} - - +uint16_t IPv6::get_src_port() { return src_port; } +uint16_t IPv6::get_dest_port() { return dest_port; } diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/packet.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/packet.cpp index 11b1f81d..c41711cc 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/packet.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/packet/packet.cpp @@ -2,14 +2,12 @@ #include ApplicationProtocol Packet::get_application_protocol() { - if (!payload_ptr || payload_len < 4) goto check_port; + if (!payload_ptr || payload_len < 4) + goto check_port; if (transport_protocol == TransportProtocol::TCP) { - if (!memcmp(payload_ptr, "GET ", 4) || - !memcmp(payload_ptr, "POST", 4) || - !memcmp(payload_ptr, "HEAD", 4) || - !memcmp(payload_ptr, "PUT ", 4) || - !memcmp(payload_ptr, "HTTP", 4)) + if (!memcmp(payload_ptr, "GET ", 4) || !memcmp(payload_ptr, "POST", 4) || !memcmp(payload_ptr, "HEAD", 4) || + !memcmp(payload_ptr, "PUT ", 4) || !memcmp(payload_ptr, "HTTP", 4)) return ApplicationProtocol::HTTP; } @@ -25,25 +23,35 @@ check_port: uint16_t port = (src_port < dst_port) ? src_port : dst_port; if (transport_protocol == TransportProtocol::TCP) { switch (port) { - case 21: return ApplicationProtocol::FTP; - case 22: return ApplicationProtocol::SSH; - case 25: return ApplicationProtocol::SMTP; - case 53: return ApplicationProtocol::DNS; - case 80: return ApplicationProtocol::HTTP; - case 443: return ApplicationProtocol::HTTPS; - default: return ApplicationProtocol::UNKNOWN; + case 21: + return ApplicationProtocol::FTP; + case 22: + return ApplicationProtocol::SSH; + case 25: + return ApplicationProtocol::SMTP; + case 53: + return ApplicationProtocol::DNS; + case 80: + return ApplicationProtocol::HTTP; + case 443: + return ApplicationProtocol::HTTPS; + default: + return ApplicationProtocol::UNKNOWN; } } if (transport_protocol == TransportProtocol::UDP) { switch (port) { - case 53: return ApplicationProtocol::DNS; - case 443: return ApplicationProtocol::QUIC; - case 123: return ApplicationProtocol::NTP; - default: return ApplicationProtocol::UNKNOWN; + case 53: + return ApplicationProtocol::DNS; + case 443: + return ApplicationProtocol::QUIC; + case 123: + return ApplicationProtocol::NTP; + default: + return ApplicationProtocol::UNKNOWN; } } return ApplicationProtocol::UNKNOWN; } - diff --git a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/stats/protocolStats.cpp b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/stats/protocolStats.cpp index 4be6ca44..0ce92ed8 100644 --- a/PROJECTS/beginner/network-traffic-analyzer/cpp/src/stats/protocolStats.cpp +++ b/PROJECTS/beginner/network-traffic-analyzer/cpp/src/stats/protocolStats.cpp @@ -1,11 +1,8 @@ #include "../../include/stats/protocolStats.hpp" - #include "ftxui/dom/table.hpp" +#include - -Stats::Stats() { - last_tick = std::chrono::steady_clock::now(); -} +Stats::Stats() { last_tick = std::chrono::steady_clock::now(); } /** * @brief Aggregates a newly captured packet. * @@ -19,17 +16,17 @@ Stats::Stats() { * Must be called only from capture thread. * Protected by mutex. */ -void Stats::add_packet(Packet &packet) { +void Stats::add_packet(const Packet &packet) { std::lock_guard lock(mtx); ++snapshot.total_p; snapshot.total_b += packet.total_len; - auto& t = transport_map[packet.transport_protocol]; + auto &t = transport_map[packet.transport_protocol]; t.packets++; t.bytes += packet.total_len; - auto& a = application_map[packet.application_protocol]; + auto &a = application_map[packet.application_protocol]; a.packets++; a.bytes += packet.payload_len; @@ -42,32 +39,45 @@ void Stats::add_packet(Packet &packet) { auto key = std::make_pair(packet.src, packet.dst); pairs[key].packets++; pairs[key].bytes += packet.total_len; - } - -const char* transport_to_str(TransportProtocol p) { +const char *transport_to_str(TransportProtocol p) { switch (p) { - case TransportProtocol::TCP: return "TCP"; - case TransportProtocol::UDP: return "UDP"; - case TransportProtocol::ICMP: return "ICMP"; - case TransportProtocol::ICMP6: return "ICMP6"; - case TransportProtocol::IGMP: return "IGMP"; - default: return "UNKNOWN"; + case TransportProtocol::TCP: + return "TCP"; + case TransportProtocol::UDP: + return "UDP"; + case TransportProtocol::ICMP: + return "ICMP"; + case TransportProtocol::ICMP6: + return "ICMP6"; + case TransportProtocol::IGMP: + return "IGMP"; + default: + return "UNKNOWN"; } } -const char* app_to_str(ApplicationProtocol p) { +const char *app_to_str(ApplicationProtocol p) { switch (p) { - case ApplicationProtocol::HTTP: return "HTTP"; - case ApplicationProtocol::HTTPS: return "HTTPS"; - case ApplicationProtocol::DNS: return "DNS"; - case ApplicationProtocol::FTP: return "FTP"; - case ApplicationProtocol::SSH: return "SSH"; - case ApplicationProtocol::SMTP: return "SMTP"; - case ApplicationProtocol::QUIC: return "QUIC"; - case ApplicationProtocol::NTP: return "NTP"; - default: return "UNKNOWN"; + case ApplicationProtocol::HTTP: + return "HTTP"; + case ApplicationProtocol::HTTPS: + return "HTTPS"; + case ApplicationProtocol::DNS: + return "DNS"; + case ApplicationProtocol::FTP: + return "FTP"; + case ApplicationProtocol::SSH: + return "SSH"; + case ApplicationProtocol::SMTP: + return "SMTP"; + case ApplicationProtocol::QUIC: + return "QUIC"; + case ApplicationProtocol::NTP: + return "NTP"; + default: + return "UNKNOWN"; } } @@ -83,29 +93,19 @@ const char* app_to_str(ApplicationProtocol p) { void Stats::update_transport_stats() { std::lock_guard lock(mtx); snapshot.transport_rows.clear(); - snapshot.transport_rows.push_back({ "Proto", "Packets", "Bytes", "%" }); + snapshot.transport_rows.push_back({"Proto", "Packets", "Bytes", "%"}); - std::vector> tps( - transport_map.begin(), transport_map.end() - ); - std::sort(tps.begin(), tps.end(), - [](auto& a, auto& b) { - return a.second.packets > b.second.packets; - }); + std::vector> tps(transport_map.begin(), transport_map.end()); + std::sort(tps.begin(), tps.end(), [](auto &a, auto &b) { return a.second.packets > b.second.packets; }); - for (const auto& [proto, stats] : tps) { + for (const auto &[proto, stats] : tps) { double percent = snapshot.total_b ? stats.bytes * 100.0 / snapshot.total_b : 0.0; - snapshot.transport_rows.push_back({ - transport_to_str(proto), - std::to_string(stats.packets), - std::format("{:.2f}", stats.bytes / (1024.0 * 1024.0)), - std::format("{:.2f}", percent) - }); - + snapshot.transport_rows.push_back({transport_to_str(proto), std::to_string(stats.packets), + std::format("{:.2f}", stats.bytes / (1024.0 * 1024.0)), + std::format("{:.2f}", percent)}); } } - /** * @brief Rebuilds application protocol snapshot. * @@ -114,29 +114,19 @@ void Stats::update_transport_stats() { */ void Stats::update_application_stats() { std::lock_guard lock(mtx); - std::vector> apps( - application_map.begin(), application_map.end() - ); + std::vector> apps(application_map.begin(), application_map.end()); - std::sort(apps.begin(), apps.end(), - [](auto& a, auto& b) { - return a.second.packets > b.second.packets; - }); + std::sort(apps.begin(), apps.end(), [](auto &a, auto &b) { return a.second.packets > b.second.packets; }); snapshot.app_rows.clear(); - snapshot.app_rows.push_back({ "Proto", "Packets", "Bytes (MB)", "%" }); + snapshot.app_rows.push_back({"Proto", "Packets", "Bytes (MB)", "%"}); - for (const auto& [proto, s] : apps) { - double percent = snapshot.total_b - ? s.bytes * 100.0 / snapshot.total_b - : 0.0; + for (const auto &[proto, s] : apps) { + double percent = snapshot.total_b ? s.bytes * 100.0 / snapshot.total_b : 0.0; - snapshot.app_rows.push_back({ - app_to_str(proto), - std::to_string(s.packets), - std::format("{:.2f}", s.bytes / (1024.0 * 1024.0)), - std::format("{:.2f}", percent) - }); + snapshot.app_rows.push_back({app_to_str(proto), std::to_string(s.packets), + std::format("{:.2f}", s.bytes / (1024.0 * 1024.0)), + std::format("{:.2f}", percent)}); } } @@ -152,23 +142,16 @@ void Stats::update_ip_stats(size_t limit) { snapshot.rows.clear(); snapshot.rows.push_back({"IP Address", "Packets TX", "Packets RX"}); - std::vector> ips( - ip_map.begin(), ip_map.end() - ); + std::vector> ips(ip_map.begin(), ip_map.end()); - std::sort(ips.begin(), ips.end(), - [](auto& a, auto& b) { - return a.second.packets_sent > b.second.packets_sent; - }); + std::sort(ips.begin(), ips.end(), [](auto &a, auto &b) { return a.second.packets_sent > b.second.packets_sent; }); size_t count = 0; - for (const auto& [ip, s] : ips) { - if (count++ >= limit) break; - snapshot.rows.push_back({ip, - "TX: " + std::to_string(s.packets_sent), - "RX: " + std::to_string(s.packets_received)} - ); - + for (const auto &[ip, s] : ips) { + if (count++ >= limit) + break; + snapshot.rows.push_back( + {ip, "TX: " + std::to_string(s.packets_sent), "RX: " + std::to_string(s.packets_received)}); } } @@ -183,17 +166,15 @@ void Stats::update_ip_stats(size_t limit) { void Stats::update_pairs(size_t limit) { std::lock_guard lock(mtx); std::vector, protocolStats>> vec(pairs.begin(), pairs.end()); - std::sort(vec.begin(), vec.end(), - [](auto& a, auto& b) { - return a.second.bytes > b.second.bytes; - }); + std::sort(vec.begin(), vec.end(), [](auto &a, auto &b) { return a.second.bytes > b.second.bytes; }); snapshot.pairs_rows.clear(); - snapshot.pairs_rows.push_back({ "Source", "Destination", "bytes received", "%" }); + snapshot.pairs_rows.push_back({"Source", "Destination", "bytes received", "%"}); size_t count = 0; - for (const auto& [pair, s] : vec) { + for (const auto &[pair, s] : vec) { - if (count++ >= limit) break; + if (count++ >= limit) + break; double percent = snapshot.total_b ? (s.bytes * 100.0 / snapshot.total_b) : 0.0; snapshot.pairs_rows.push_back({ pair.first, @@ -202,16 +183,15 @@ void Stats::update_pairs(size_t limit) { std::format("{:.2f}", percent), }); - } } -void Stats::update_packets(){ +void Stats::update_packets() { std::lock_guard lock(mtx); snapshot.packets_rows.clear(); - snapshot.packets_rows.push_back({ "IPVersion", "Transport protocol", "Source", "Destination", "App protocol"}); + snapshot.packets_rows.push_back({"IPVersion", "Transport protocol", "Source", "Destination", "App protocol"}); - for (auto& packet : packets) { + for (auto &packet : packets) { snapshot.packets_rows.push_back({ packet.ip_version == IPVersion::v4 ? "IPv4" : "IPv6", transport_to_str(packet.transport_protocol), @@ -264,13 +244,11 @@ void Stats::update_bandwidth() { last_b = snapshot.total_b; last_tick = now; const double alpha = 0.2; - smooth_bandwidth = - alpha * snapshot.bandwidth + (1.0 - alpha) * smooth_bandwidth; + smooth_bandwidth = alpha * snapshot.bandwidth + (1.0 - alpha) * smooth_bandwidth; - snapshot.bandwidth_history.push_back({ ts, smooth_bandwidth }); + snapshot.bandwidth_history.push_back({ts, smooth_bandwidth}); } snapshot.max_bandwidth = std::max(snapshot.max_bandwidth, snapshot.bandwidth); - } /** @@ -284,27 +262,23 @@ void Stats::update_bandwidth() { * - Bandwidth history */ -void Stats::export_csv(const std::string& filename) { +void Stats::export_csv(const std::string &filename) { std::lock_guard lock(mtx); std::ofstream file(filename); - if (!file.is_open()) return; + if (!file.is_open()) + return; file << "summary\n"; file << "total_packets,total_bytes,bandwidth\n"; - file << snapshot.total_p << "," - << snapshot.total_b << "," - << snapshot.bandwidth << "\n\n"; + file << snapshot.total_p << "," << snapshot.total_b << "," << snapshot.bandwidth << "\n\n"; // ===== Transport protocols ===== file << "transport_protocols\n"; file << "protocol,packets,bytes,percent\n"; - for (const auto& [proto, s] : transport_map) { + for (const auto &[proto, s] : transport_map) { double percent = snapshot.total_b ? (s.bytes * 100.0 / snapshot.total_b) : 0.0; - file << transport_to_str(proto) << "," - << s.packets << "," - << s.bytes << "," - << percent << "\n"; + file << transport_to_str(proto) << "," << s.packets << "," << s.bytes << "," << percent << "\n"; } file << "\n"; @@ -312,10 +286,8 @@ void Stats::export_csv(const std::string& filename) { file << "application_protocols\n"; file << "protocol,packets,payload_bytes\n"; - for (const auto& [proto, s] : application_map) { - file << static_cast(proto) << "," - << s.packets << "," - << s.bytes << "\n"; + for (const auto &[proto, s] : application_map) { + file << static_cast(proto) << "," << s.packets << "," << s.bytes << "\n"; } file << "\n"; @@ -323,24 +295,19 @@ void Stats::export_csv(const std::string& filename) { file << "ip_stats\n"; file << "ip,packets_sent,packets_received,bytes_sent,bytes_received\n"; - for (const auto& [ip, s] : ip_map) { - file << ip << "," - << s.packets_sent << "," - << s.packets_received << "," - << s.bytes_sent << "," + for (const auto &[ip, s] : ip_map) { + file << ip << "," << s.packets_sent << "," << s.packets_received << "," << s.bytes_sent << "," << s.bytes_received << "\n"; } - - //bandwidth + // bandwidth file << "time,bandwidth\n"; - for (const auto& p : snapshot.bandwidth_history) { + for (const auto &p : snapshot.bandwidth_history) { file << p.timestamp << "," << p.bytes_per_sec << "\n"; } file.close(); - } /** * @brief Exports statistics to JSON format. @@ -350,10 +317,11 @@ void Stats::export_csv(const std::string& filename) { * - Visualization tools * - Data pipelines */ -void Stats::export_json(const std::string& filename) { +void Stats::export_json(const std::string &filename) { std::lock_guard lock(mtx); std::ofstream file(filename); - if (!file.is_open()) return; + if (!file.is_open()) + return; file << "{\n"; @@ -367,8 +335,9 @@ void Stats::export_json(const std::string& filename) { // ===== Transport ===== file << " \"transport\": [\n"; bool first = true; - for (const auto& [proto, s] : transport_map) { - if (!first) file << ",\n"; + for (const auto &[proto, s] : transport_map) { + if (!first) + file << ",\n"; first = false; double percent = snapshot.total_b ? (s.bytes * 100.0 / snapshot.total_b) : 0.0; @@ -385,8 +354,9 @@ void Stats::export_json(const std::string& filename) { // ===== IP stats ===== file << " \"top_ips\": [\n"; first = true; - for (const auto& [ip, s] : ip_map) { - if (!first) file << ",\n"; + for (const auto &[ip, s] : ip_map) { + if (!first) + file << ",\n"; first = false; file << " {\n"; @@ -402,8 +372,9 @@ void Stats::export_json(const std::string& filename) { file << ",\n \"communication_pairs\": [\n"; first = true; - for (const auto& [pair, s] : pairs) { - if (!first) file << ",\n"; + for (const auto &[pair, s] : pairs) { + if (!first) + file << ",\n"; first = false; file << " {\n";