Repository: SeanMcGoff/lunar-network-daemon
Pull request #13: Feat/bit and burst errors
Author: login: SeanMcGoff
Created: 2025-03-31T18:49:10Z | Merged: 2025-04-02T08:09:29Z

Implements both bit and burst errors:
- Burst error periods are determined by three separate threads that randomly enable/disable given config params
- If a burst error mode is enabled for the same link type as the packet, that packet is dropped
- Bit errors first detect where the ip/transport headers end, then flip bits given the params in config.

Throughput limit is also removed in this PR


Diff:
diff --git a/config/config.json b/config/config.json
index 00ff1a4..1e51171 100644
--- a/config/config.json
+++ b/config/config.json
@@ -5,11 +5,10 @@
     "latency_jitter_stddev": 0,
     "base_bit_error_rate": 0,
     "bit_error_rate_stddev": 0,
-    "base_packet_loss_burst_freq_per_hour": 0,
+    "base_packet_loss_burst_freq_per_minute": 0,
     "packet_loss_burst_freq_stddev": 0,
     "base_packet_loss_burst_duration_ms": 0,
-    "base_packet_loss_burst_duration_stddev": 0,
-    "throughput_limit_mbps": 0
+    "base_packet_loss_burst_duration_stddev": 0
   },
   "earth_to_moon": {
     "base_latency_ms": 1280.0,
@@ -17,11 +16,10 @@
     "latency_jitter_stddev": 50.0,
     "base_bit_error_rate": 1e-5,
     "bit_error_rate_stddev": 5e-6,
-    "base_packet_loss_burst_freq_per_hour": 1.0,
+    "base_packet_loss_burst_freq_per_minute": 1.0,
     "packet_loss_burst_freq_stddev": 0.5,
     "base_packet_loss_burst_duration_ms": 500.0,
-    "base_packet_loss_burst_duration_stddev": 100.0,
-    "throughput_limit_mbps": 0
+    "base_packet_loss_burst_duration_stddev": 100.0
   },
   "moon_to_earth": {
     "base_latency_ms": 1280.0,
@@ -29,11 +27,10 @@
     "latency_jitter_stddev": 50.0,
     "base_bit_error_rate": 1e-5,
     "bit_error_rate_stddev": 5e-6,
-    "base_packet_loss_burst_freq_per_hour": 1.0,
+    "base_packet_loss_burst_freq_per_minute": 1.0,
     "packet_loss_burst_freq_stddev": 0.5,
     "base_packet_loss_burst_duration_ms": 500.0,
-    "base_packet_loss_burst_duration_stddev": 100.0,
-    "throughput_limit_mbps": 7.5
+    "base_packet_loss_burst_duration_stddev": 100.0
   },
   "moon_to_moon": {
     "base_latency_ms": 30.0,
@@ -41,10 +38,9 @@
     "latency_jitter_stddev": 5.0,
     "base_bit_error_rate": 2e-6,
     "bit_error_rate_stddev": 1e-6,
-    "base_packet_loss_burst_freq_per_hour": 0.2,
+    "base_packet_loss_burst_freq_per_minute": 0.2,
     "packet_loss_burst_freq_stddev": 0.1,
     "base_packet_loss_burst_duration_ms": 50.0,
-    "base_packet_loss_burst_duration_stddev": 10.0,
-    "throughput_limit_mbps": 7.5
+    "base_packet_loss_burst_duration_stddev": 10.0
   }
 }
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 236c377..458851c 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -147,9 +147,9 @@ void loadLinkProperties(const nm::json &j, Config::LinkProperties &props,
       getDoubleWithLog(j, "base_bit_error_rate", defaults.base_bit_error_rate);
   props.bit_error_rate_stddev = getDoubleWithLog(
       j, "bit_error_rate_stddev", defaults.bit_error_rate_stddev);
-  props.base_packet_loss_burst_freq_per_hour =
-      getDoubleWithLog(j, "base_packet_loss_burst_freq_per_hour",
-                       defaults.base_packet_loss_burst_freq_per_hour);
+  props.base_packet_loss_burst_freq_per_minute =
+      getDoubleWithLog(j, "base_packet_loss_burst_freq_per_minute",
+                       defaults.base_packet_loss_burst_freq_per_minute);
   props.packet_loss_burst_freq_stddev =
       getDoubleWithLog(j, "packet_loss_burst_freq_stddev",
                        defaults.packet_loss_burst_freq_stddev);
@@ -159,8 +159,6 @@ void loadLinkProperties(const nm::json &j, Config::LinkProperties &props,
   props.base_packet_loss_burst_duration_stddev =
       getDoubleWithLog(j, "base_packet_loss_burst_duration_stddev",
                        defaults.base_packet_loss_burst_duration_stddev);
-  props.throughput_limit_mbps = getDoubleWithLog(
-      j, "throughput_limit_mbps", defaults.throughput_limit_mbps);
 }
 
 // Helper function: Load a configuration section
diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp
index 25ea5c3..2aa518b 100644
--- a/src/config/ConfigManager.hpp
+++ b/src/config/ConfigManager.hpp
@@ -38,14 +38,11 @@ struct Config {
     double bit_error_rate_stddev;
 
     // Packet loss burst params
-    double base_packet_loss_burst_freq_per_hour;
+    double base_packet_loss_burst_freq_per_minute;
     double packet_loss_burst_freq_stddev;
     double base_packet_loss_burst_duration_ms;
     double base_packet_loss_burst_duration_stddev;
 
-    // Throughput limit (note: 0 = no limit)
-    double throughput_limit_mbps;
-
     auto operator<=>(const LinkProperties &) const = default;
   };
 
diff --git a/src/config/configs.hpp b/src/config/configs.hpp
index 0efdbba..f12d6d1 100644
--- a/src/config/configs.hpp
+++ b/src/config/configs.hpp
@@ -12,20 +12,20 @@
 // 2 => latency_jitter_stddev;
 // 3 => base_bit_error_rate;
 // 4 => bit_error_rate_stddev;
-// 5 => base_packet_loss_burst_freq_per_hour;
+// 5 => base_packet_loss_burst_freq_per_minute;
 // 6 => packet_loss_burst_freq_stddev;
 // 7 => base_packet_loss_burst_duration_ms;
 // 8 => base_packet_loss_burst_duration_stddev;
 // 9 => throughput_limit_mbps;
 
 constexpr const Config::LinkProperties DEFAULT_EARTH_TO_EARTH{0, 0, 0, 0, 0,
-                                                              0, 0, 0, 0, 0};
+                                                              0, 0, 0, 0};
 constexpr const Config::LinkProperties DEFAULT_EARTH_TO_MOON{
-    1280.0, 100.0, 50.0, 1e-5, 5e-6, 1.0, 0.5, 500.0, 100.0, 0.0};
+    1280.0, 100.0, 50.0, 1e-5, 5e-6, 1.0, 0.5, 500.0, 100.0};
 constexpr const Config::LinkProperties DEFAULT_MOON_TO_EARTH{
-    1280.0, 100.0, 50.0, 1e-5, 5e-6, 1.0, 0.5, 500.0, 100.0, 7.5};
+    1280.0, 100.0, 50.0, 1e-5, 5e-6, 1.0, 0.5, 500.0, 100.0};
 constexpr const Config::LinkProperties DEFAULT_MOON_TO_MOON{
-    30.0, 10.0, 5.0, 2e-6, 1e-6, 0.2, 0.1, 50.0, 10.0, 7.5};
+    30.0, 10.0, 5.0, 2e-6, 1e-6, 0.2, 0.1, 50.0, 10.0};
 
 constexpr uint32_t ROVER_IP_MIN =
     (10 << 24 | 237 << 16 | 0 << 8 | 2); // minimum is 10.237.0.2
diff --git a/src/main.cpp b/src/main.cpp
index 6f289ca..a1bac31 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -38,7 +38,7 @@ int main() {
     // Set up TC/Netem rules, torn down on destruction
     TcNetemManager tc_netem(config_manager);
 
-    g_queue = std::make_unique<NetfilterQueue>();
+    g_queue = std::make_unique<NetfilterQueue>(config_manager);
 
     // blocks until stopped by signal
     g_queue->run();
diff --git a/src/netfilter/NetfilterQueue.cpp b/src/netfilter/NetfilterQueue.cpp
index 5f966dc..1585227 100644
--- a/src/netfilter/NetfilterQueue.cpp
+++ b/src/netfilter/NetfilterQueue.cpp
@@ -1,5 +1,6 @@
 // src/netfilter/NetfilterQueue.cpp
 
+#include <cassert>
 #include <cerrno>
 #include <chrono>
 #include <csignal>
@@ -12,11 +13,15 @@
 #include <netinet/in.h>
 
 #include "NetfilterQueue.hpp"
-#include "Packet.hpp"
-#include "configs.hpp"
-
-NetfilterQueue::NetfilterQueue()
-    : running_(true), handle_(nullptr, nfq_close),
+#include <random>
+#include <set>
+
+NetfilterQueue::NetfilterQueue(ConfigManager &config_manager)
+    : config_manager_(config_manager), burst_error_moon_to_earth_(false),
+      burst_error_earth_to_moon_(false), burst_error_moon_to_moon_(false),
+      running_(true), burst_threads_running_(false),
+      // Initialize handles with custom deleters
+      handle_(nullptr, nfq_close),
       queue_handle_(nullptr, [](struct nfq_q_handle *qh) {
         if (qh) {
           std::cout << "Destroying queue.\n";
@@ -73,6 +78,18 @@ NetfilterQueue::NetfilterQueue()
 void NetfilterQueue::run() {
   std::cout << "Starting main packet processing loop.\n";
 
+  // Start burst error simulation threads
+  burst_threads_running_ = true;
+  moon_to_earth_burst_thread_ =
+      std::thread(&NetfilterQueue::burstErrorSimulation, this,
+                  Packet::LinkType::MOON_TO_EARTH);
+  earth_to_moon_burst_thread_ =
+      std::thread(&NetfilterQueue::burstErrorSimulation, this,
+                  Packet::LinkType::EARTH_TO_MOON);
+  moon_to_moon_burst_thread_ =
+      std::thread(&NetfilterQueue::burstErrorSimulation, this,
+                  Packet::LinkType::MOON_TO_MOON);
+
   char buffer[MAX_PACKET_SIZE] __attribute__((aligned));
 
   while (running_) {
@@ -104,9 +121,32 @@ void NetfilterQueue::run() {
   }
 
   std::cout << "Exiting main packet processing loop.\n";
+
+  // Join threads
+  if (moon_to_earth_burst_thread_.joinable()) {
+    moon_to_earth_burst_thread_.join();
+  }
+
+  if (earth_to_moon_burst_thread_.joinable()) {
+    earth_to_moon_burst_thread_.join();
+  }
+
+  if (moon_to_moon_burst_thread_.joinable()) {
+    moon_to_moon_burst_thread_.join();
+  }
+
+  std::cout << "All burst simulation threads terminated.\n";
 }
 
-void NetfilterQueue::stop() { running_ = false; }
+void NetfilterQueue::stop() {
+  burst_threads_running_ = false;
+  running_ = false;
+
+  // Notify all waiting threads to check their conditions
+  moon_to_earth_cv_.notify_all();
+  earth_to_moon_cv_.notify_all();
+  moon_to_moon_cv_.notify_all();
+}
 
 bool NetfilterQueue::isRunning() const { return running_; }
 
@@ -157,20 +197,24 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     // PACKET PROCESSING LOGIC
 
-    // Apply mark based on link type
+    // Apply mark and burst error_condition based on link type
     uint32_t new_mark = 0;
+    bool is_in_burst_error = false;
     switch (packet.getLinkType()) {
     case Packet::LinkType::EARTH_TO_EARTH:
       new_mark = MARK_EARTH_TO_EARTH;
       break;
     case Packet::LinkType::EARTH_TO_MOON:
       new_mark = MARK_EARTH_TO_MOON;
+      is_in_burst_error = burst_error_earth_to_moon_;
       break;
     case Packet::LinkType::MOON_TO_EARTH:
       new_mark = MARK_MOON_TO_EARTH;
+      is_in_burst_error = burst_error_moon_to_earth_;
       break;
     case Packet::LinkType::MOON_TO_MOON:
       new_mark = MARK_MOON_TO_MOON;
+      is_in_burst_error = burst_error_moon_to_moon_;
       break;
     default:
       new_mark = 0; // Unclassified traffic gets no mark
@@ -186,6 +230,50 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      std::cout << "Dropped packet due to burst error mode activated."
+                << std::endl;
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // hacky temporary fix?
+      size_t size = modifiedData.size();
+      while (modifiedData.size() % 4 != 0)
+        modifiedData.push_back(0);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, size,
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
@@ -193,4 +281,207 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    // ms/burst error = 60s * 1000ms / (burst error/min)
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max((60.0 * 1000.0) / freq_dist(random_generator), 0.0));
+    ms_burst_duration =
+        static_cast<uint64_t>(std::max(burst_dist(random_generator), 0.0));
+
+    // Sleep until next burst or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_to_next_burst),
+                      [this] { return !burst_threads_running_; })) {
+        // If predicate returns true, it means we were interrupted
+        break;
+      }
+    }
+
+    // Check again if the thread should stop
+    if (!burst_threads_running_)
+      break;
+
+    // Enable burst error mode
+    burst_error_mode = true;
+
+    // Sleep for the burst duration or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_burst_duration),
+                      [this] { return !burst_threads_running_; })) {
+        break;
+      }
+    }
+
+    burst_error_mode = false;
+  }
+
+  // Reset the burst error mode
+  burst_error_mode = false;
+}
+
+std::vector<uint8_t>
+NetfilterQueue::applyBitErrors(Packet &packet,
+                               const Config::LinkProperties &props) {
+  // Create a copy of the packet data for modification
+  std::vector<uint8_t> modifiedData(packet.getData(),
+                                    packet.getData() + packet.getLength());
+
+  // If the packet is too small, skip modification
+  if (packet.getLength() < 20) {
+    return modifiedData;
+  }
+
+  // Verify this is an IPv4 packet
+  if ((modifiedData[0] >> 4) != 4) {
+    return modifiedData;
+  }
+
+  // Skip if bit error rate is zero
+  if (props.base_bit_error_rate <= 0.0) {
+    return modifiedData;
+  }
+
+  // Random number generator for bit error simulation
+  static std::mt19937 gen(std::random_device{}());
+
+  // Use normal distribution based on config parameters
+  std::normal_distribution<double> error_rate_dist(props.base_bit_error_rate,
+                                                   props.bit_error_rate_stddev);
+
+  // Get actual bit error rate for this packet
+  double actual_bit_error_rate = std::max(0.0, error_rate_dist(gen));
+
+  // Uniform distributions for bit flipping
+  std::uniform_real_distribution<> flip_dist(0.0, 1.0);
+  std::uniform_int_distribution<> bit_dist(0, 7);
+
+  // Calculate the size of the protected header
+  size_t protectedHeaderSize = 0;
+
+  // Section off IP header
+  uint8_t ip_header_len = (modifiedData[0] & 0x0F) * 4;
+  protectedHeaderSize = ip_header_len;
+
+  // Check IP protocol field for TCP or UDP
+  // to determine transport layer header size
+  if (packet.getLength() > ip_header_len) {
+    uint8_t protocol = modifiedData[9];
+
+    // TCP (protocol 6)
+    if (protocol == 6 && packet.getLength() >= ip_header_len + 20) {
+      uint8_t tcp_header_len =
+          ((modifiedData[ip_header_len + 12] >> 4) & 0x0F) * 4;
+      protectedHeaderSize += tcp_header_len;
+    }
+    // UDP (protocol 17)
+    else if (protocol == 17 && packet.getLength() >= ip_header_len + 8) {
+      // If UDP, set the checksum to 0 to avoid checksum errors
+      modifiedData[ip_header_len + 6] = 0;
+      modifiedData[ip_header_len + 7] = 0;
+      // UDP header is 8 bytes
+      protectedHeaderSize += 8;
+    }
+  }
+
+  // Ensure we don't exceed packet bounds
+  if (protectedHeaderSize >= packet.getLength()) {
+    return modifiedData;
+  }
+
+  // Do the bit flipping
+  for (size_t byte_index = protectedHeaderSize;
+       byte_index < modifiedData.size(); ++byte_index) {
+    for (int bit = 0; bit < 8; ++bit) {
+      if (flip_dist(gen) < actual_bit_error_rate) {
+        modifiedData[byte_index] ^= (1 << bit);
+        std::cout << "Flipped a bit!" << std::endl;
+      }
+    }
+  }
+
+  return modifiedData;
 }
\ No newline at end of file
diff --git a/src/netfilter/NetfilterQueue.hpp b/src/netfilter/NetfilterQueue.hpp
index 50f834f..83cf46e 100644
--- a/src/netfilter/NetfilterQueue.hpp
+++ b/src/netfilter/NetfilterQueue.hpp
@@ -23,18 +23,24 @@
 #pragma once
 
 #include <atomic>
+#include <condition_variable>
 #include <csignal>
 #include <functional>
 #include <memory>
+#include <mutex>
+#include <thread>
 
 #include <libnetfilter_queue/libnetfilter_queue.h>
 
+#include "Packet.hpp"
+#include "configs.hpp"
+
 class NetfilterQueue {
   // NOTE: I'm using the very obtuse netfilter naming conventions for
   // convenience (easier to follow examples), I've explained it all (mostly) at
   // the bottom
 public:
-  NetfilterQueue();
+  NetfilterQueue(ConfigManager &config_manager);
   void run();
   void stop();
   bool isRunning() const;
@@ -53,12 +59,39 @@ class NetfilterQueue {
   int packetCallback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
                      struct nfq_data *nfa);
 
+  // Applies bit errors to the packet data
+  std::vector<uint8_t> applyBitErrors(Packet &packet,
+                                      const Config::LinkProperties &link_type);
+
+  // This method will be called in a separate thread to simulate burst errors
+  void burstErrorSimulation(const Packet::LinkType link_type);
+
   // file descriptor for netlink socket
   int fd_;
 
+  // ConfigManager instance for accessing config values
+  ConfigManager &config_manager_;
+
+  // Threads for timing burst errors given the config parameters
+  std::thread moon_to_earth_burst_thread_, earth_to_moon_burst_thread_,
+      moon_to_moon_burst_thread_;
+
+  // thread safe flags for controlling burst-error simulation
+  std::atomic<bool> burst_error_moon_to_earth_, burst_error_earth_to_moon_,
+      burst_error_moon_to_moon_;
+
+  // Burst error simulation mutexes and condition variables
+  std::mutex moon_to_earth_cv_mutex_, earth_to_moon_cv_mutex_,
+      moon_to_moon_cv_mutex_;
+  std::condition_variable moon_to_earth_cv_, earth_to_moon_cv_,
+      moon_to_moon_cv_;
+
   // thread safe flag for controlling the processing loop
   std::atomic<bool> running_;
 
+  // flag for controlling burst error simulation threads
+  std::atomic<bool> burst_threads_running_{true};
+
   // smart pointers for resource management
   std::unique_ptr<struct nfq_handle, decltype(&nfq_close)> handle_;
   std::unique_ptr<struct nfq_q_handle,


Reviews:
user: login: merkelmarrow
body: Looks good to me, will test now. But if you want flipped bits to be corrected on your application's side, you'll have to update the UDP/TCP checksum which I imagine is tricky enough to implement
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-01T03:15:44Z
user: login: merkelmarrow
body: Just tested the program and it crashed as soon as packets started being transmitted, we should look into this. 
![image](https://github.com/user-attachments/assets/b5a80de6-364c-4732-862d-f7690482a900)

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T03:31:05Z
user: login: SeanMcGoff
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-01T10:36:13Z
user: login: SeanMcGoff
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-01T10:41:35Z
user: login: SeanMcGoff
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-01T10:41:47Z
user: login: merkelmarrow
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-02T04:30:12Z

Review comments:
diff_hunk: @@ -186,11 +230,245 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, modifiedData.size(),
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
   } catch (std::exception &error) {
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max(6000.0 / freq_dist(random_generator), 0.0));
+    ms_burst_duration =
+        static_cast<uint64_t>(std::max(burst_dist(random_generator), 0.0));
+
+    // Sleep until next burst or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_to_next_burst),
+                      [this] { return !burst_threads_running_; })) {
+        // If predicate returns true, it means we were interrupted
+        break;
+      }
+    }
+
+    // Check again if the thread should stop
+    if (!burst_threads_running_)
+      break;
+
+    // Enable burst error mode
+    burst_error_mode = true;
+
+    // Sleep for the burst duration or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_burst_duration),
+                      [this] { return !burst_threads_running_; })) {
+        break;
+      }
+    }
+
+    burst_error_mode = false;
+  }
+
+  // Reset the burst error mode
+  burst_error_mode = false;
+}
+
+std::vector<uint8_t>
+NetfilterQueue::applyBitErrors(Packet &packet,
+                               const Config::LinkProperties &props) {
+  // Create a copy of the packet data for modification
+  std::vector<uint8_t> modifiedData(packet.getData(),
+                                    packet.getData() + packet.getLength());
+
+  // If the packet is too small, skip modification
+  if (packet.getLength() < 20) {
+    return modifiedData;
+  }
+
+  // Verify this is an IPv4 packet
+  if ((modifiedData[0] >> 4) != 4) {
+    return modifiedData;
+  }
+
+  // Skip if bit error rate is zero
+  if (props.base_bit_error_rate <= 0.0) {
+    return modifiedData;
+  }
+
+  // Random number generator for bit error simulation
+  static std::mt19937 gen(std::random_device{}());
+
+  // Use normal distribution based on config parameters
+  std::normal_distribution<double> error_rate_dist(props.base_bit_error_rate,
+                                                   props.bit_error_rate_stddev);
+
+  // Get actual bit error rate for this packet
+  double actual_bit_error_rate = std::max(0.0, error_rate_dist(gen));
+
+  // Uniform distributions for bit flipping
+  std::uniform_real_distribution<> flip_dist(0.0, 1.0);
+  std::uniform_int_distribution<> bit_dist(0, 7);
+
+  // Calculate the size of the protected header
+  size_t protectedHeaderSize = 0;
+
+  // Section off IP header
+  uint8_t ip_header_len = (modifiedData[0] & 0x0F) * 4;
+  protectedHeaderSize = ip_header_len;
+
+  // Check IP protocol field for TCP or UDP
+  // to determine transport layer header size
+  if (packet.getLength() > ip_header_len) {
+    uint8_t protocol = modifiedData[9];
+
+    // TCP (protocol 6)
+    if (protocol == 6 && packet.getLength() >= ip_header_len + 20) {
+      uint8_t tcp_header_len =
+          ((modifiedData[ip_header_len + 12] >> 4) & 0x0F) * 4;
+      protectedHeaderSize += tcp_header_len;
+    }
+    // UDP (protocol 17)
+    else if (protocol == 17 && packet.getLength() >= ip_header_len + 8) {
+      protectedHeaderSize += 8;
+    }
+  }
+
+  // Ensure we don't exceed packet bounds
+  if (protectedHeaderSize >= packet.getLength()) {
+    return modifiedData;
+  }
+
+  // Do the bit flipping
+  for (size_t byte_index = protectedHeaderSize;
+       byte_index < modifiedData.size(); ++byte_index) {
+    for (int bit = 0; bit < 8; ++bit) {
+      if (flip_dist(gen) < actual_bit_error_rate) {
+        modifiedData[byte_index] ^= (1 << bit);
+      }
+    }
+  }
path: src/netfilter/NetfilterQueue.cpp
user: login: merkelmarrow
body: If you don't update the TCP/UDP checksum before returning, the packet just gets dropped anyway so your error-correction won't be tested - not an urgent problem but if you want to show off your Reed-Solomon you'll have to update this.
created_at: 2025-04-01T03:07:46Z
author_association: COLLABORATOR
diff_hunk: @@ -186,11 +230,245 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, modifiedData.size(),
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
   } catch (std::exception &error) {
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max(6000.0 / freq_dist(random_generator), 0.0));
path: src/netfilter/NetfilterQueue.cpp
user: login: merkelmarrow
body: Could you clarify what this calculation is doing
created_at: 2025-04-01T03:08:58Z
author_association: COLLABORATOR
diff_hunk: @@ -186,11 +230,245 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, modifiedData.size(),
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
   } catch (std::exception &error) {
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max(6000.0 / freq_dist(random_generator), 0.0));
path: src/netfilter/NetfilterQueue.cpp
user: login: SeanMcGoff
body: Yeah sure, the config gives a value of burst error per minute, and I need ms per burst error:
- minutes/burst error = 1/(burst error/min)
- ms / burst error = 60 * 1000 * (minutes/burst error)

.. and now i'm realizing I'm off by a factor of 10
created_at: 2025-04-01T10:36:13Z
author_association: OWNER
diff_hunk: @@ -186,11 +230,245 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, modifiedData.size(),
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
   } catch (std::exception &error) {
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max(6000.0 / freq_dist(random_generator), 0.0));
+    ms_burst_duration =
+        static_cast<uint64_t>(std::max(burst_dist(random_generator), 0.0));
+
+    // Sleep until next burst or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_to_next_burst),
+                      [this] { return !burst_threads_running_; })) {
+        // If predicate returns true, it means we were interrupted
+        break;
+      }
+    }
+
+    // Check again if the thread should stop
+    if (!burst_threads_running_)
+      break;
+
+    // Enable burst error mode
+    burst_error_mode = true;
+
+    // Sleep for the burst duration or until interrupted
+    {
+      std::unique_lock<std::mutex> lock(cv_mutex);
+      if (cv.wait_for(lock, std::chrono::milliseconds(ms_burst_duration),
+                      [this] { return !burst_threads_running_; })) {
+        break;
+      }
+    }
+
+    burst_error_mode = false;
+  }
+
+  // Reset the burst error mode
+  burst_error_mode = false;
+}
+
+std::vector<uint8_t>
+NetfilterQueue::applyBitErrors(Packet &packet,
+                               const Config::LinkProperties &props) {
+  // Create a copy of the packet data for modification
+  std::vector<uint8_t> modifiedData(packet.getData(),
+                                    packet.getData() + packet.getLength());
+
+  // If the packet is too small, skip modification
+  if (packet.getLength() < 20) {
+    return modifiedData;
+  }
+
+  // Verify this is an IPv4 packet
+  if ((modifiedData[0] >> 4) != 4) {
+    return modifiedData;
+  }
+
+  // Skip if bit error rate is zero
+  if (props.base_bit_error_rate <= 0.0) {
+    return modifiedData;
+  }
+
+  // Random number generator for bit error simulation
+  static std::mt19937 gen(std::random_device{}());
+
+  // Use normal distribution based on config parameters
+  std::normal_distribution<double> error_rate_dist(props.base_bit_error_rate,
+                                                   props.bit_error_rate_stddev);
+
+  // Get actual bit error rate for this packet
+  double actual_bit_error_rate = std::max(0.0, error_rate_dist(gen));
+
+  // Uniform distributions for bit flipping
+  std::uniform_real_distribution<> flip_dist(0.0, 1.0);
+  std::uniform_int_distribution<> bit_dist(0, 7);
+
+  // Calculate the size of the protected header
+  size_t protectedHeaderSize = 0;
+
+  // Section off IP header
+  uint8_t ip_header_len = (modifiedData[0] & 0x0F) * 4;
+  protectedHeaderSize = ip_header_len;
+
+  // Check IP protocol field for TCP or UDP
+  // to determine transport layer header size
+  if (packet.getLength() > ip_header_len) {
+    uint8_t protocol = modifiedData[9];
+
+    // TCP (protocol 6)
+    if (protocol == 6 && packet.getLength() >= ip_header_len + 20) {
+      uint8_t tcp_header_len =
+          ((modifiedData[ip_header_len + 12] >> 4) & 0x0F) * 4;
+      protectedHeaderSize += tcp_header_len;
+    }
+    // UDP (protocol 17)
+    else if (protocol == 17 && packet.getLength() >= ip_header_len + 8) {
+      protectedHeaderSize += 8;
+    }
+  }
+
+  // Ensure we don't exceed packet bounds
+  if (protectedHeaderSize >= packet.getLength()) {
+    return modifiedData;
+  }
+
+  // Do the bit flipping
+  for (size_t byte_index = protectedHeaderSize;
+       byte_index < modifiedData.size(); ++byte_index) {
+    for (int bit = 0; bit < 8; ++bit) {
+      if (flip_dist(gen) < actual_bit_error_rate) {
+        modifiedData[byte_index] ^= (1 << bit);
+      }
+    }
+  }
path: src/netfilter/NetfilterQueue.cpp
user: login: SeanMcGoff
body: check most recent commits
created_at: 2025-04-01T10:41:35Z
author_association: OWNER
diff_hunk: @@ -186,11 +230,245 @@ int NetfilterQueue::packetCallback(struct nfq_q_handle *qh,
 
     ///////////////////////////////////////////////////////////////////
 
+    // If the link types' burst mode is enabled, drop the packet
+    if (is_in_burst_error) {
+      return nfq_set_verdict2(qh, id, NF_DROP, new_mark, packet.getLength(),
+                              packet.getData());
+    }
+
+    // Get link properties for bit error simulation
+    Config::LinkProperties props;
+    switch (packet.getLinkType()) {
+    case Packet::LinkType::EARTH_TO_EARTH:
+      props = config_manager_.getEToEConfig();
+      break;
+    case Packet::LinkType::EARTH_TO_MOON:
+      props = config_manager_.getEToMConfig();
+      break;
+    case Packet::LinkType::MOON_TO_EARTH:
+      props = config_manager_.getMToEConfig();
+      break;
+    case Packet::LinkType::MOON_TO_MOON:
+      props = config_manager_.getMToMConfig();
+      break;
+    default:
+      // Use earth to earth as default for unclassified traffic
+      props = config_manager_.getEToEConfig();
+      break;
+    }
+
+    // Apply bit errors if configured
+    if (props.base_bit_error_rate > 0) {
+      // Create a copy of the packet data for modification
+      std::vector<uint8_t> modifiedData = applyBitErrors(packet, props);
+
+      // Use the modified data in the verdict
+      return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, modifiedData.size(),
+                              modifiedData.data());
+    }
+
     // using packet.getData() in case the packet was modified
     return nfq_set_verdict2(qh, id, NF_ACCEPT, new_mark, packet.getLength(),
                             packet.getData());
   } catch (std::exception &error) {
     std::cerr << "Error processing packet: " << error.what() << "\n";
     return nfq_set_verdict2(qh, id, NF_ACCEPT, MARK_EARTH_TO_EARTH, 0, nullptr);
   }
+}
+
+void NetfilterQueue::burstErrorSimulation(const Packet::LinkType link_type) {
+  // Get the correct burst_error atomic variable based on the link type
+  std::atomic<bool> &burst_error_mode = [this,
+                                         link_type]() -> std::atomic<bool> & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return burst_error_earth_to_moon_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return burst_error_moon_to_earth_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return burst_error_moon_to_moon_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct mutex and condition variable based on the link type
+  std::mutex &cv_mutex = [this, link_type]() -> std::mutex & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_mutex_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_mutex_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_mutex_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  std::condition_variable &cv = [this,
+                                 link_type]() -> std::condition_variable & {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return earth_to_moon_cv_;
+    case Packet::LinkType::MOON_TO_EARTH:
+      return moon_to_earth_cv_;
+    case Packet::LinkType::MOON_TO_MOON:
+      return moon_to_moon_cv_;
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Get the correct link properties based on the link type
+  Config::LinkProperties props = [this, link_type]() -> Config::LinkProperties {
+    switch (link_type) {
+    case Packet::LinkType::EARTH_TO_MOON:
+      return config_manager_.getEToMConfig();
+    case Packet::LinkType::MOON_TO_EARTH:
+      return config_manager_.getMToEConfig();
+    case Packet::LinkType::MOON_TO_MOON:
+      return config_manager_.getMToMConfig();
+    default:
+      throw std::invalid_argument(
+          "Invalid link type for burst error simulation.");
+    }
+  }();
+
+  // Random number generator for burst error simulation
+  std::mt19937 random_generator(time(0));
+
+  // Initialize the burst error mode
+  burst_error_mode = false;
+
+  std::normal_distribution<double> freq_dist(
+      props.base_packet_loss_burst_freq_per_minute,
+      props.packet_loss_burst_freq_stddev);
+
+  std::normal_distribution<double> burst_dist(
+      props.base_packet_loss_burst_duration_ms,
+      props.base_packet_loss_burst_duration_stddev);
+
+  // Define the burst error simulation parameters
+  uint64_t ms_to_next_burst, ms_burst_duration;
+
+  while (burst_threads_running_) {
+    // Generate random values for the frequency and duration
+    ms_to_next_burst = static_cast<uint64_t>(
+        std::max(6000.0 / freq_dist(random_generator), 0.0));
path: src/netfilter/NetfilterQueue.cpp
user: login: SeanMcGoff
body: (fixed in commit)
created_at: 2025-04-01T10:41:47Z
author_association: OWNER

PR comments:
user: login: merkelmarrow
created_at: 2025-04-01T07:50:25Z
body: > Just tested the program and it crashed as soon as packets started being transmitted, we should look into this. 
> ![image](https://github.com/user-attachments/assets/b5a80de6-364c-4732-862d-f7690482a900)
> 

Could honestly be the teeny weeny RAM
author_association: COLLABORATOR
user: login: merkelmarrow
created_at: 2025-04-02T04:32:11Z
body: I would bother to make the hacky fix more elegant but we have like one day until the demos and it's working
author_association: COLLABORATOR