From c9781e9b396ae3981cdad2b170250eef61164b03 Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Sat, 25 Jul 2026 11:21:12 +0200 Subject: [PATCH 1/6] ci: add build and test pipeline --- .github/workflows/build_test.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/build_test.yml diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 0000000..1a5c7f9 --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,22 @@ +name: Rust build and test + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From 01cdac64e4396cd2f72a31bfb7c4e8818141d635 Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Sat, 25 Jul 2026 11:55:42 +0200 Subject: [PATCH 2/6] style: rename Suri variable to Suricata --- src/argument.rs | 2 +- src/suricata.yaml | 2 +- src/yaml.rs | 12 ++++++------ suricata.yaml.in | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/argument.rs b/src/argument.rs index 962a999..1ce4f80 100644 --- a/src/argument.rs +++ b/src/argument.rs @@ -47,7 +47,7 @@ pub struct Args { pub enum Commands { /// Change defaults for Suricata - Suri { + Suricata { /// Change path to bin #[clap(short='b', long)] path_to_bin: Option, diff --git a/src/suricata.yaml b/src/suricata.yaml index 6df8e34..e9efa6a 100644 --- a/src/suricata.yaml +++ b/src/suricata.yaml @@ -810,7 +810,7 @@ dpdk: mtu: 1500 # Set MTU of the device in bytes vlan-strip-offload: false # if possible enable hardware vlan stripping # rss-hash-functions: 0x0 # advanced configuration option, use only if you use untested NIC card and experience RSS warnings, - # For `rss-hash-functions` use hexadecimal 0x01ab format to specify RSS hash function flags - DumpRssFlags can help (you can see output if you use -vvv option during Suri startup) + # For `rss-hash-functions` use hexadecimal 0x01ab format to specify RSS hash function flags - DumpRssFlags can help (you can see output if you use -vvv option during Suricata startup) # setting auto to rss_hf sets the default RSS hash functions (based on IP addresses) # linkup-timeout: 0 # how many seconds to wait before giving up, 0 to disable link state checking diff --git a/src/yaml.rs b/src/yaml.rs index 67a8619..9922d52 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -809,42 +809,42 @@ impl Suriconf { } }; - self.suricata_bin = if let Some(Commands::Suri {path_to_bin: Some(p), ..}) = &args.cmd { + self.suricata_bin = if let Some(Commands::Suricata {path_to_bin: Some(p), ..}) = &args.cmd { p.clone() } else { self.find_suricata_bin(suriconf_string).expect("Unable to parse path to Suricata binary file.") }; - self.ethtool_bin = if let Some(Commands::Suri {ethtool_bin: Some(p), ..}) = &args.cmd { + self.ethtool_bin = if let Some(Commands::Suricata {ethtool_bin: Some(p), ..}) = &args.cmd { p.clone() } else { self.find_ethtool_bin(suriconf_string).expect("Unable to parse path to Ethtool binary file.") }; - self.ifconfig_bin = if let Some(Commands::Suri {ifconfig_bin: Some(p), ..}) = &args.cmd { + self.ifconfig_bin = if let Some(Commands::Suricata {ifconfig_bin: Some(p), ..}) = &args.cmd { p.clone() } else { self.find_ifconfig_bin(suriconf_string).expect("Unable to parse path to Ifconfig binary file.") }; - self.ip_bin = if let Some(Commands::Suri {ip_bin: Some(p), ..}) = &args.cmd { + self.ip_bin = if let Some(Commands::Suricata {ip_bin: Some(p), ..}) = &args.cmd { p.clone() } else { self.find_ip_bin(suriconf_string).expect("Unable to parse path to IP binary file.") }; - self.log_dir = if let Some(Commands::Suri { path_to_logs: Some(p), .. }) = &args.cmd { + self.log_dir = if let Some(Commands::Suricata { path_to_logs: Some(p), .. }) = &args.cmd { p.clone() } else { self.find_log_dir(suriconf_string).expect("Unable to parse path to logs.") }; - self.preconf_time = if let Some( Commands::Suri { preconf_time: Some(p), .. }) = &args.cmd { + self.preconf_time = if let Some( Commands::Suricata { preconf_time: Some(p), .. }) = &args.cmd { p.clone() } else { diff --git a/suricata.yaml.in b/suricata.yaml.in index 9b96a59..1a52e6e 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -810,7 +810,7 @@ dpdk: mtu: 1500 # Set MTU of the device in bytes vlan-strip-offload: false # if possible enable hardware vlan stripping # rss-hash-functions: 0x0 # advanced configuration option, use only if you use untested NIC card and experience RSS warnings, - # For `rss-hash-functions` use hexadecimal 0x01ab format to specify RSS hash function flags - DumpRssFlags can help (you can see output if you use -vvv option during Suri startup) + # For `rss-hash-functions` use hexadecimal 0x01ab format to specify RSS hash function flags - DumpRssFlags can help (you can see output if you use -vvv option during Suricata startup) # setting auto to rss_hf sets the default RSS hash functions (based on IP addresses) # linkup-timeout: 0 # how many seconds to wait before giving up, 0 to disable link state checking From 46454c556fc207fe10e5219f65475a803fbd00db Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Sat, 25 Jul 2026 16:47:12 +0200 Subject: [PATCH 3/6] refactor: apply cargo fix to remove unused imports and silence unused-variable warnings --- src/cpu_affinity.rs | 24 ++++++++++++------------ src/flow.rs | 6 +++--- src/flow_threads.rs | 4 ++-- src/json.rs | 8 ++++---- src/memory_usage.rs | 2 +- src/query.rs | 6 +++--- src/regression.rs | 6 +++--- src/structures.rs | 5 +---- src/suricata.rs | 9 +++------ src/yaml.rs | 4 ++-- 10 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/cpu_affinity.rs b/src/cpu_affinity.rs index d6377e9..04476d9 100644 --- a/src/cpu_affinity.rs +++ b/src/cpu_affinity.rs @@ -13,7 +13,7 @@ use std::fs; use crate::json::CpuThread; use crate::module::Module; use crate::structures::{Analysis, Answer, Change, Keys}; -use crate::{CPU_MULTIPLIER, PANIC_THRESHOLD, CPU_USAGE}; +use crate::{CPU_MULTIPLIER, CPU_USAGE}; use std::process::Command; use itertools::{izip}; use std::fs::File; @@ -27,7 +27,7 @@ pub struct CpuAffinityModule { impl Module for CpuAffinityModule { - fn new(analysis: &Analysis, debug: bool) -> Self { + fn new(_analysis: &Analysis, debug: bool) -> Self { let keys = [ Keys::threads_stat, Keys::wrk_cpu_set, @@ -84,7 +84,7 @@ impl Module for CpuAffinityModule { self.module_disable_offloading(answers, &mut nic_file); self.module_set_rss(answers, &mut nic_file); self.module_af_packet_tuning(answers, &mut nic_file); - self.set_af_packet_threads(answers); + self.set_af_packet_threads(); self.module_set_hard_irq(answers, &mut nic_file); Change::collect_changes(&self.questions) @@ -212,7 +212,7 @@ impl CpuAffinityModule { for i in 0..vector.len() { let current = vector.get(i).expect("Unable to get u64."); if i != 0 { - let before = vector.get(i-1).expect("Unable to get u64.");; + let before = vector.get(i-1).expect("Unable to get u64."); clean_vector.push(*current - *before); } else { @@ -443,7 +443,7 @@ impl CpuAffinityModule { self.get_capture_kernel_drops_stat(answers).iter().map(|a| a.value.last().expect("Expected element.")).sum::() } - fn get_wrk_cpu_set(&self, answers: &Vec>) -> Vec { + fn get_wrk_cpu_set(&self) -> Vec { self.questions .get(&Keys::wrk_cpu_set).expect("Unable to get wrk_cpu_set.").as_array() .expect("wrk_cpu_set is not an array").iter() @@ -474,7 +474,7 @@ impl CpuAffinityModule { } else { let mut cpu_counter = 0; - let numa_cpus = self.get_numa_node_with_cpus(answers, numa_node); + let numa_cpus = self.get_numa_node_with_cpus(numa_node); for cpu in &max_cpu_usage_vec { if cpu_counter == new_workers { break; @@ -498,8 +498,8 @@ impl CpuAffinityModule { new_wrk_cpu_set } - fn set_af_packet_threads(&mut self, answers: &Vec>) { - let wrk_cpu_set_len = self.get_wrk_cpu_set(answers).len() as u64; + fn set_af_packet_threads(&mut self) { + let wrk_cpu_set_len = self.get_wrk_cpu_set().len() as u64; *self.questions.get_mut(&Keys::af_packet_interface_threads).expect("Unable to get af_packet_interface_threads") = Value::Number(wrk_cpu_set_len.into()); } @@ -522,7 +522,7 @@ impl CpuAffinityModule { numa_node } - fn get_numa_node_with_cpus(&self, answers: &Vec>, numa_node: i8) -> Vec { + fn get_numa_node_with_cpus(&self, numa_node: i8) -> Vec { let numa_node_cpus = fs::read_to_string( format!("/sys/devices/system/node/node{numa_node}/cpulist") ).expect("Failed to read file."); @@ -533,20 +533,20 @@ impl CpuAffinityModule { let interface = self.get_interface_stat(answers); let ethtool = self.get_ethtool_stat(answers); let ifconfig = answers.iter().find(|h| h.key == &Keys::ifconfig).and_then(|h| h.value.as_str()).expect("Ifconfig cannot be found."); - let wrk_cpu_set_len = self.get_wrk_cpu_set(answers).len() as u64; + let wrk_cpu_set_len = self.get_wrk_cpu_set().len() as u64; yaml::af_packet_tuning(interface, wrk_cpu_set_len, ethtool, ifconfig, nic_file); } fn module_set_rss(&self, answers: &Vec>, nic_file: &mut File) { let interface = self.get_interface_stat(answers); let ethtool = self.get_ethtool_stat(answers); - let wrk_cpu_set_len = self.get_wrk_cpu_set(answers).len() as u64; + let wrk_cpu_set_len = self.get_wrk_cpu_set().len() as u64; _ = yaml::set_rss(interface, wrk_cpu_set_len, ethtool, nic_file); } fn module_set_hard_irq(&self, answers: &Vec>, nic_file: &mut File) { let interface = self.get_interface_stat(answers); - let wrk_cpu_set = self.get_wrk_cpu_set(answers); + let wrk_cpu_set = self.get_wrk_cpu_set(); yaml::set_hard_irq(interface, &wrk_cpu_set, nic_file); } diff --git a/src/flow.rs b/src/flow.rs index c946b68..e865f9a 100644 --- a/src/flow.rs +++ b/src/flow.rs @@ -12,7 +12,7 @@ use crate::module::Module; use std::collections::HashMap; use serde_json::{Value}; use std::collections::BTreeMap; -use crate::{FLOW_WINDOW, MAX_AVG_RATIO, LOAD_FACTOR, MIN_AVG_RATIO, FLOW_OBJECT, SYNC_AVG, FLOW_BUCKET, FLOW_LOCAL_THREAD_MAX, MULTIPLIER}; +use crate::{FLOW_WINDOW, MAX_AVG_RATIO, LOAD_FACTOR, MIN_AVG_RATIO, FLOW_OBJECT, FLOW_BUCKET, FLOW_LOCAL_THREAD_MAX, MULTIPLIER}; #[derive(Debug)] pub struct FlowModule { @@ -30,7 +30,7 @@ pub struct Counter { } impl Module for FlowModule { - fn new(analysis: &Analysis, debug: bool) -> Self { + fn new(_analysis: &Analysis, debug: bool) -> Self { let keys = [ Keys::max_memory_usage, Keys::threads_stat, @@ -211,7 +211,7 @@ impl FlowModule { for flow in &time.1.hashes { let v = current_queues.entry(*flow.0).or_insert(0); *v += *flow.1; - if (*v > counter.current_max) { + if *v > counter.current_max { counter.current_max = *v; } } diff --git a/src/flow_threads.rs b/src/flow_threads.rs index d857e11..aa5be82 100644 --- a/src/flow_threads.rs +++ b/src/flow_threads.rs @@ -6,7 +6,7 @@ SPDX-License-Identifier: BSD-3-Clause This file represents a Flow threads module. */ -use crate::structures::{Keys, ModuleResult, RobRegression, Analysis, Thread, Answer, Change, Reason}; +use crate::structures::{Keys, ModuleResult, RobRegression, Analysis, Thread, Answer, Change}; use crate::module::Module; use std::collections::{HashMap}; use serde_json::{Value}; @@ -27,7 +27,7 @@ pub struct RecyclerUp { } impl Module for FlowThreadsModule { - fn new(analysis: &Analysis, debug: bool) -> Self { + fn new(_analysis: &Analysis, debug: bool) -> Self { let keys = [ Keys::flow_recyclers, Keys::flow_managers, diff --git a/src/json.rs b/src/json.rs index 85e1c97..4a92223 100644 --- a/src/json.rs +++ b/src/json.rs @@ -54,7 +54,7 @@ pub fn check_emergency(stats: &PathBuf) -> bool { let last_stat = match get_the_last_one_stats(stats) { Ok(last_stat) => {last_stat}, - Err(e) => {return false;} + Err(_e) => {return false;} }; match find_emerg_mode_entered(&last_stat) { @@ -704,8 +704,8 @@ impl Preconfiguration { value.get("flow").and_then(|a| a.get("pkts_toclient")).and_then(|f| f.as_u64()).ok_or("Cannot find packets to client.")?; let bytes_to_client = flow.get("bytes_toclient").and_then(|f| f.as_u64()).ok_or("Cannot find bytes to client.")?; let bytes_to_server = flow.get("bytes_toserver").and_then(|f| f.as_u64()).ok_or("Cannot find packets bytes to server.")?; - let start = flow.get("start").ok_or("Cannot find start flow value").and_then(|s| self.find_flow_start_end_time(s, true))?; - let end = flow.get("end").ok_or("Cannot find end flow value").and_then(|e| self.find_flow_start_end_time(e, false))?; + let start = flow.get("start").ok_or("Cannot find start flow value").and_then(|s| self.find_flow_start_end_time(s))?; + let end = flow.get("end").ok_or("Cannot find end flow value").and_then(|e| self.find_flow_start_end_time(e))?; let state = flow.get("state").and_then(|f| f.as_str()).ok_or("Cannot find a flow state.")?.to_string(); let reason = flow.get("reason").and_then(|f| f.as_str()).ok_or("Cannot find a flow reason.")?.to_string(); let proto = value.get("proto").and_then(|f| f.as_str()).ok_or("Cannot find a flow protocol.")?.to_string(); @@ -736,7 +736,7 @@ impl Preconfiguration { Ok(()) } - pub fn find_flow_start_end_time(&self, time: &Value, start: bool) -> Result { + pub fn find_flow_start_end_time(&self, time: &Value) -> Result { let time_str = time.as_str().ok_or("Time is not a string.")?; let date_time: DateTime = time_str.parse().map_err(|_| "Invalid timestamp")?; diff --git a/src/memory_usage.rs b/src/memory_usage.rs index 3ed580a..8442bbf 100644 --- a/src/memory_usage.rs +++ b/src/memory_usage.rs @@ -22,7 +22,7 @@ pub struct MemoryModule { } impl Module for MemoryModule { - fn new(analysis: &Analysis, debug: bool) -> Self { + fn new(_analysis: &Analysis, debug: bool) -> Self { let keys = [ Keys::max_memory_usage, Keys::memcap_pressure, diff --git a/src/query.rs b/src/query.rs index 90449db..59fc168 100644 --- a/src/query.rs +++ b/src/query.rs @@ -45,19 +45,19 @@ impl Jsons { FileNames::suricata | FileNames::suriconf => { let yaml = match yaml::open_yaml(self.path(resources, &file_name)) { Ok(yaml) => {yaml}, - Err(e) => {return Err(Box::from("Unable to parse yaml file to value."))} + Err(e) => {return Err(Box::from(format!("Unable to parse yaml file to value. {e}")))} }; yaml::yaml_to_json(yaml) }, FileNames::preconf => { let json = match open_json(self.path(resources, &file_name)) { Ok(json) => {json}, - Err(e) => {return Err(Box::from("Unable to parse json file to value."))} + Err(e) => {return Err(Box::from(format!("Unable to parse json file to value. {e}")))} }; match json_to_value(json) { Ok(json) => {json}, - Err(e) => {return Err(Box::from("Unable to parse json file to value."))} + Err(e) => {return Err(Box::from(format!("Unable to parse json file to value. {e}")))} } } }; diff --git a/src/regression.rs b/src/regression.rs index 86fff4f..7322764 100644 --- a/src/regression.rs +++ b/src/regression.rs @@ -10,7 +10,7 @@ use scirs2_core::ndarray::{Array1, Array2}; use scirs2_stats::{huber_regression, theilslopes}; pub fn my_huber_regression(vector_values: Vec, uptime: u64, time_period: u64, num_elements: u64) -> Vec { - let loops: u64 = uptime/(time_period*num_elements); + let _loops: u64 = uptime/(time_period*num_elements); let loops = 3; let mut huber_reg_vec: Vec = Vec::new(); let vector_time: Vec = (0..uptime).step_by(time_period as usize).map(|v| v as f64).collect(); @@ -22,7 +22,7 @@ pub fn my_huber_regression(vector_values: Vec, uptime: u64, time_period: u let result = match huber_regression(&x.view(), &y.view(), None, None, None, None, None, None) { Ok(result) => {result}, - Err(e) => { + Err(_e) => { //println!("{e} {:?}", y); huber_reg_vec.push(0.0); continue} @@ -49,7 +49,7 @@ pub fn my_theil_sen_regression(vector_values: Vec, uptime: u64, time_period let result = match theilslopes(&x.view(), &y.view(), None, None){ Ok(result) => {result}, - Err(e) => { + Err(_e) => { //println!("{e} {:?}", y); theil_sen_vec.push(0.0); continue} diff --git a/src/structures.rs b/src/structures.rs index 0bbf98a..12cce3c 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -10,10 +10,7 @@ use std::path::PathBuf; extern crate chrono; use chrono::offset::Utc; use chrono::DateTime; -use std::time::{SystemTime}; use std::fs; -use std::thread; -use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{BTreeMap, HashMap}; @@ -32,7 +29,7 @@ pub enum Reason { shutdown, forced, emergency, - tcp_reuse // TODO, in future think about tcp_reuse + tcp_reuse } impl Reason { diff --git a/src/suricata.rs b/src/suricata.rs index 1ce224a..a18235d 100644 --- a/src/suricata.rs +++ b/src/suricata.rs @@ -6,11 +6,10 @@ SPDX-License-Identifier: BSD-3-Clause This file executes Suricata. */ -use std::path::PathBuf; use std::process::{Child, Command}; use crate::yaml::{emergency_check_memcap, Suriconf}; -use crate::{json, FLOW_WINDOW, WINDOWS, MIN_RUN}; -use crate::json::{check_emergency, CpuThread, Preconfiguration}; +use crate::{FLOW_WINDOW, MIN_RUN}; +use crate::json::{check_emergency, CpuThread}; use crate::structures::{Thread, SystemVar, CreatedLogs, CaptureMode, SuricataAgain, Modules}; use is_executable::IsExecutable; use std::time::Duration; @@ -18,12 +17,10 @@ use crossbeam_channel::{bounded, select, tick, Receiver}; use std::process::Stdio; use std::io::{BufRead, BufReader}; use std::thread; -use sysinfo::System; use procfs::process::{all_processes, Process}; -use scirs2_core::convenience::boolean; use signal_hook::consts::SIGINT; use signal_hook::iterator::Signals; -use std::sync::{atomic, Arc, Mutex}; +use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicBool, Ordering}; pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options: &Vec) -> Option<(SystemVar, SuricataAgain)> { diff --git a/src/yaml.rs b/src/yaml.rs index 9922d52..d39d6d7 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -27,7 +27,7 @@ pub fn open_yaml(file: &PathBuf) -> Result> { Ok(text) } -pub fn open_yaml_with_comments(file: &PathBuf) { +pub fn open_yaml_with_comments(_file: &PathBuf) { todo!() } @@ -327,7 +327,7 @@ pub fn check_for_interface_specific_workers_set_or_take_default(suricata_string: Ok(()) } -pub fn check_for_default(suricata_string: &mut Value, suriconf: &Suriconf) { +pub fn check_for_default(_suricata_string: &mut Value, _suriconf: &Suriconf) { // default // let defalut_workers =suricata_string.get_mut("threading").ok_or("Unable to get threading section.")? // .get_mut("cpu-affinity").ok_or("Unable to get cpu-affinity section.")? From 7fed582f9b3702f13fd20426a13f38d81f762b61 Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Sat, 25 Jul 2026 17:08:04 +0200 Subject: [PATCH 4/6] style: apply cargo fmt to format code --- src/lib.rs | 29 +++++++++++++------ src/main.rs | 68 ++++++++++++++++++++++++--------------------- tests/json_tests.rs | 6 ++-- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fa8d87e..ca1824b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,13 +15,24 @@ macro_rules! mods { }; } -mods!(argument, structures, yaml, suricata, json, - memory_usage, query, flow, flow_threads, regression, - module, cpu_affinity); +mods!( + argument, + structures, + yaml, + suricata, + json, + memory_usage, + query, + flow, + flow_threads, + regression, + module, + cpu_affinity +); static FLOW_WINDOW: u64 = 5; // in seconds static MIN_RUN: u64 = 360; -static WINDOWS:u64 = 3; +static WINDOWS: u64 = 3; static ACTIVE_LIMIT: u64 = 512; static FRAGMENTS: f64 = 4.0; static MTU: u64 = 1500; @@ -32,7 +43,7 @@ static FLOW_OBJECT: f64 = 312.0; // 296 B for flow object // reality 272 B static PACKET: f64 = 464.0; static HOST_HASHROW: f64 = 64.0; static IPPAIR_HASHROW: f64 = 64.0; -static DEFRAG_TRACKER_HASHROW: f64 = 48.0; +static DEFRAG_TRACKER_HASHROW: f64 = 48.0; static HOST_OBJECT: f64 = 120.0; // prealloc static IPPAIR_OBJECT: f64 = 136.0; static DEFRAG_TRACKER: f64 = 144.0; @@ -45,7 +56,7 @@ static LOAD_FACTOR: f64 = 2f64; static MAX_AVG_RATIO: i64 = 3; static MIN_AVG_RATIO: f64 = 0.5; static RECYCLER_START: u8 = 1; -static MANAGER_START: u8 = 1; +static MANAGER_START: u8 = 1; static SYNC_AVG: u64 = 100; static MULTIPLIER: f64 = 1.2; @@ -58,7 +69,7 @@ static DEFAULT_PACKET_SIZE: DefaultPacketSize = DefaultPacketSize::Average; static CPU_USAGE_MAX: f32 = 95.0; -static CPU_USAGE: f64 = 50.0; +static CPU_USAGE: f64 = 50.0; static HUBER_THRESHOLD: f64 = 0.0; -static MANAGER_SLOPE: f64 = 0.1; // once every 10 s -static PANIC_THRESHOLD: u64 = 100; \ No newline at end of file +static MANAGER_SLOPE: f64 = 0.1; // once every 10 s +static PANIC_THRESHOLD: u64 = 100; diff --git a/src/main.rs b/src/main.rs index 8884cbc..aa876fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,23 +7,22 @@ This file is a startpoint for Suriconf. */ use chrono::{DateTime, Utc}; -use clap::{Parser}; +use clap::Parser; use std::time::SystemTime; use suriconf::argument::Args; -use suriconf::yaml; -use suriconf::yaml::{Suriconf}; use suriconf::json::Preconfiguration; +use suriconf::query::Resources; use suriconf::structures::{CreatedLogs, JsonVar, SuricataAgain}; use suriconf::suricata; -use suriconf::query::Resources; +use suriconf::yaml; +use suriconf::yaml::Suriconf; #[allow(unused_variables)] fn main() { - let args = Args::parse(); let suriconf_string = match yaml::open_yaml(&args.suriconf_config) { - Ok(suriconf_string) => {suriconf_string}, + Ok(suriconf_string) => suriconf_string, Err(e) => { panic!("{e}"); } @@ -33,7 +32,7 @@ fn main() { suriconf.create_suriconf_structure(&args, &suriconf_string); let mut suricata_string = match yaml::open_yaml(&suriconf.suri_configuration) { - Ok(suricata_string) => {suricata_string}, + Ok(suricata_string) => suricata_string, Err(e) => { panic!("{e}"); } @@ -42,7 +41,7 @@ fn main() { match yaml::check_enable_stats_log(&mut suricata_string) { Err(e) => { panic!("{e}"); - }, + } Ok(()) => {} } let mut json_var: JsonVar = Default::default(); @@ -52,42 +51,42 @@ fn main() { match yaml::check_set_cpu_affinity(&mut suricata_string, &suriconf, &mut json_var, &datetime) { Err(e) => { panic!("{e}") - }, + } Ok(()) => {} }; match suriconf.find_suricata_executable_file() { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } - + match suriconf.find_ethtool_executable_file() { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } match suriconf.find_ifconfig_executable_file() { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } - + match suriconf.find_ip_executable_file() { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } match suriconf.check_read_write_for_log_dir() { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } @@ -95,21 +94,22 @@ fn main() { yaml::close_yaml(&suricata_string, &logs.suri_configuration).unwrap(); suricata::check_min_suricata_runtime_for_modules(&suriconf); - + let sys = loop { - let (sys, suricata_again) = match suricata::execute_suricata(&suriconf, &mut logs, &args.options) { - Some((sys, suricata_again)) => { - if sys.threads.is_empty() { - panic!("Unable to get data from Suricata."); + let (sys, suricata_again) = + match suricata::execute_suricata(&suriconf, &mut logs, &args.options) { + Some((sys, suricata_again)) => { + if sys.threads.is_empty() { + panic!("Unable to get data from Suricata."); + } + (sys, suricata_again) + } + _ => { + return; } - (sys, suricata_again) - } - _ => { - return; - } - }; + }; if suricata_again == SuricataAgain::Done { - break sys + break sys; } }; @@ -118,12 +118,18 @@ fn main() { match preconfiguration.create_preconfiguration_structure_and_save(&logs) { Err(e) => { panic!("{}", e); - }, + } Ok(()) => {} } // QUERY - let mut resources = Resources::new(logs.suri_configuration, suriconf, args.suriconf_config, logs.preconfiguration, json_var, args.verbose); + let mut resources = Resources::new( + logs.suri_configuration, + suriconf, + args.suriconf_config, + logs.preconfiguration, + json_var, + args.verbose, + ); resources.main_query(); } - diff --git a/tests/json_tests.rs b/tests/json_tests.rs index 673a8c5..dd93d82 100644 --- a/tests/json_tests.rs +++ b/tests/json_tests.rs @@ -5,7 +5,7 @@ SPDX-License-Identifier: BSD-3-Clause This file contains implementation tests. */ -use::suriconf::json; +use ::suriconf::json; use std::path::PathBuf; #[cfg(test)] @@ -15,7 +15,7 @@ mod tests { #[test] fn json_open() { let json = PathBuf::from("./tests/flows.json"); - let result= json::open_json(&json); + let result = json::open_json(&json); assert!(result.is_ok(), "File cannot be opened."); } -} \ No newline at end of file +} From da85d72d319a3e6505422ae1ded08be78b8ca45d Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Wed, 29 Jul 2026 13:07:06 +0200 Subject: [PATCH 5/6] ci: add Rust format check to pipeline --- .github/workflows/{build_test.yml => fmt_build_test.yml} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename .github/workflows/{build_test.yml => fmt_build_test.yml} (76%) diff --git a/.github/workflows/build_test.yml b/.github/workflows/fmt_build_test.yml similarity index 76% rename from .github/workflows/build_test.yml rename to .github/workflows/fmt_build_test.yml index 1a5c7f9..dfcbc01 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/fmt_build_test.yml @@ -1,4 +1,4 @@ -name: Rust build and test +name: Rust format, build and test on: push: @@ -16,6 +16,8 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Check formatting + run: cargo fmt --check - name: Build run: cargo build --verbose - name: Run tests From cd48b6be97b68e5a14ea1b82e74ade386c53230c Mon Sep 17 00:00:00 2001 From: Eliska Cervinkova Date: Wed, 29 Jul 2026 13:08:57 +0200 Subject: [PATCH 6/6] docs: update README --- README.md | 149 +++++++++++++++++++++++++----------------------------- 1 file changed, 69 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 4f22eba..59cd1f7 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,94 @@ -# Suricata-autoconfigurer +# Suriconf: configuration assistant for Suricata -An information manual for setting up Suriconf, a configuration assistant for Suricata. +[![License](https://img.shields.io/badge/license-BSD-blue.svg)](LICENSE) +[![Rust](https://img.shields.io/badge/rust-1.88+-orange.svg)](https://rustup.rs/) +[![Bachelor's Thesis](https://img.shields.io/badge/thesis-completed-success)](https://www.vut.cz/studenti/zav-prace/detail/170986) ---- +Suriconf is an automated configuration assistant for [Suricata](https://github.com/OISF/suricata). It analyzes network traffic and system resources to optimize Suricata's configuration through a modular approach. Each module uses mathematical methods and performance metrics to configure specific Suricata components. Testing showed Suriconf v1.0-dev successfully configured Suricata in 80.8% of test cases with [rules](https://community.emergingthreats.net/). -- [Suricata-autoconfigurer](#suricata-autoconfigurer) - - [1. Dependencies](#1-dependencies) - - [2. Suriconf Configuration](#2-suriconf-configuration) - - [2.1 Configuration Overview](#21-configuration-overview) - - [2.2 Modules](#22-modules) - - [2.3 Variables](#23-variables) - - [3. How to Run](#3-how-to-run) - - [4. Expected Output](#4-expected-output) +## Contents --- - -## 1. Dependencies - -It is necessary to install **rustup**. Installation instructions are available at: -https://rustup.rs/ -> [!WARNING] -The Rust compiler version must be higher than 1.88. - -The remaining dependencies required by Suriconf are defined in its configuration. These binaries must be installed and their paths provided in the configuration file. The required tools include: - -- Suricata (version 9.0.0-dev (d030a9c4e 2026-04-01)) -- ethtool (version 5.13) -- ifconfig (net-tools 2.10-alpha) -- ip (ip utility, iproute2-6.8.0, libbpf 0.5.0) - + - [Prerequisites](#prerequisites) + - [Rust toolchain](#rust-toolchain) + - [Configuration](#configuration) + - [Configuration overview](#configuration-overview) + - [Modules](#modules) + - [Variables](#variables) + - [Usage](#usage) + - [Output](#output) --- -## 2. Suriconf configuration file +## Prerequisites -### 2. 1. Configuration Overview +### Rust toolchain +1. Install Rustup from [rustup.rs](https://rustup.rs/). +2. Verify your Rust version: `rustc --version`. -- The entire configuration is defined in a YAML file, typically named `suriconf.yaml`. +> [!WARNING] +> Minimum required version of rustc is 1.88 or higher. -- The default Suricata configuration file is specified by the **`suri-configuration`** parameter. +### Required binaries -- The **`log-dir`** parameter defines the directory where Suricata logs are stored. - This directory must have read and write permissions. +The following tools must be installed, and their paths must be accessible and specified in the configuration file. -- The **`preconf-time`** parameter defines the duration of the Suricata preconfiguration run. - > [!WARNING] - The flow threads module requires a minimum of 6 minutes to configure properly. +| Tool | Version | +|------|---------| +| Suricata | 9.0.0-dev (d030a9c4e 2026-04-01) | +| ethtool | 5.13 | +| ifconfig | net-tools 2.10-alpha | +| ip | iproute2-6.8.0, libbpf 0.5.0 | -- The **`analysis`** parameter determines whether Suricata performs multiple runs with different configuration options (dynamic) or whether all decisions are derived from a single preconfiguration run (static). - > [!WARNING] - In version 1.0-dev, only static analysis is supported. +## Configuration -- The **mode** defines whether Suriconf writes changes directly into the configuration file or only provides configuration suggestions: - - suggestion mode: only recommendations are provided - - modify mode: configuration is modified automatically +### Configuration overview -- The **modify mode** includes two submodes: - - ask mode (user confirms changes) - - force mode (all detected changes are applied automatically) +The entire configuration is defined in a YAML file, typically named `suriconf.yaml`. - > [!WARNING] - In version 1.0-dev, only **modify mode with `yaml_change: force`** is supported. +| Parameter | Description | +|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `suri-configuration` | Path to the default Suricata configuration file. | +| `log-dir` | Directory for Suricata logs (requires read/write permissions). | +| `preconf-time` | Duration of the Suricata preconfiguration run. | +| `analysis` | Analysis type: `dynamic` (multiple Suricata runs) or `static` (single Suricata run). | +| `mode` | Output mode: `suggestion` (recommendations only) or `modify` (writes changes to Suricata configuration file).
Modify mode with `yaml_change`: `ask` (user confirms each change) or `force` (all detected changes arre applied automatically). | ---- +> [!WARNING] +> - Flow threads module requires minimum 6 minutes (`preconf-time`). +> - Version 1.0-dev supports only `static` analysis. +> - Version 1.0-dev supports only `modify` mode with `yaml_change: force`. -### 2. 2. Modules -- The `modules` section defines all available modules. -- Each module can be enabled or disabled using the `enabled` parameter (`true` / `false`). -- If a module is disabled, Suricata uses its default configuration from `suricata.yaml`. +### Modules -> [!WARNING] -When disabling the `cpu_affinity` module, an interface-specific CPU affinity configuration must be defined in `suricata.yaml`. +The `modules` section defines all available modules. Each module can be enabled or disabled using the `enabled` parameter (`true` / `false`). ---- +> [!NOTE] +> For disabled modules, Suriconf uses the default configuration from the Suricata configuration file. -### 2. 3. Variables +> [!WARNING] +> When disabling `cpu_affinity` module, you must define interface-specific CPU affinity section in default Suricata configuration file. -- The `variables` section defines runtime and hardware-related settings: - - network interface used by Suricata - - packet capture mode (only AF_PACKET) - - maximum memory usage - - CPU core vector used by Suriconf +### Variables -- The CPU vector defines logical cores intended for Suriconf configuration and later selection for Suricata execution. +The `variables` section defines runtime and hardware settings. -- If `flow_threads` is enabled: - - management threads are taken from the `max_cpu_usage` vector +| Setting | Description | +|----------------------|--------------------------------------------------------| +| `interface` | Network interface used by Suricata. | +| `capture_mode` | Only AF_PACKET (`af_packet`) supported. | +| `max_memory_usage` | Maximum memory usage limit for Suricata configuration. | +| `max_cpu_usage_vec` | Logical cores for Suricata configuration. | -- When `cpu_affinity` is enabled at startup: - - remaining cores from the `max_cpu_usage` vector are assigned to worker threads - - the number of allocated CPU cores corresponds to the number of RX RSS queues of the network interface +**CPU allocation** +- **Management threads** (when `flow_threads` module enabled): taken from `max_cpu_usage_vec` vector. +- **Worker threads** (when `cpu_affinity` module enabled): assigned from remaining cores, limited by RX RSS queue count (fewer queues = fewer cores used). ---- +## Usage -## 3. How to run +Isolate CPU cores specified in the `max_cpu_usage_vec` vector to prevent interference from other processes. -One of the highly recommended practices, or even a requirement when running Suriconf, is isolating CPU cores specified in the `max_cpu_usage` vector. -The purpose of this is to prevent Suriconf from producing inaccurate estimates due to interference from other processes consuming CPU resources. -One possible approach is to use the `grubby` kernel parameter. Below is an example of isolating CPU cores 2 to 4, followed by a system reboot: +Isolate cores 2-4 using `grubby`: ```bash sudo grubby --update-kernel=ALL --args="isolcpus=2-4" && sudo reboot @@ -109,19 +100,17 @@ Use the Cargo package manager to run the project in `src` directory: cargo run ``` -To display available options, pass the -h flag after -- (all Suriconf parameters has to be behind --): +To display available options, pass the `-h` flag after `--`: ```bash cargo run -- -h ``` ---- - -## 4. Expected output -The expected output should be a successful configuration process that creates `suricata_result.yaml` and `nic_setup.sh` (used for configuring the NIC and **must be executed before running Suricata**), both with the same timestamp. - ---- +> [!NOTE] +> All Suriconf parameters must be passed after `--`. +## 4. Output +The expected output should be a successful configuration process that creates `suricata_result.yaml` and `nic_setup.sh`, both with the same timestamp. -## 5. Bachelor's thesis testing -All testing results are saved in directory `bt_tests_results` and resources used during testing are in directory `bt_tests_resources`. The Suriconf binary is stored in `src`. +> [!IMPORTANT] +> Execute `nic_setup.sh` before running Suricata to apply network interface settings.