improved reports update strategy and console prints

This commit is contained in:
Giuliano Bellini s294739 2022-09-10 19:27:02 +02:00
parent f29e98aea9
commit d8ca648baa
3 changed files with 66 additions and 62 deletions

View file

@ -4,7 +4,7 @@
[![Version](https://img.shields.io/crates/v/sniffnet.svg)](https://crates.io/crates/sniffnet)
![License](https://img.shields.io/crates/l/sniffnet.svg)
Application to analyze and filter network traffic.
Multithreaded application to analyze and filter network traffic.
Several command line options are available to select the network adapter to inspect, to set a desired reports update frequency and to specify filters on the observed traffic.

View file

@ -60,7 +60,7 @@ fn main() {
let lowest_port = args.lowest_port;
let highest_port = args.highest_port;
let min_packets = args.minimum_packets;
let interval = args.interval;
let interval: u64 = args.interval;
let network_layer: String = args.network_layer_filter.to_ascii_lowercase();
let network_layer_2: String = network_layer.clone();
let transport_layer: String = args.transport_layer_filter.to_ascii_lowercase();
@ -124,7 +124,11 @@ fn main() {
println!("{}{}{}", "\tThe folder '".cyan().italic(), output_folder.cyan().italic(),
"' will be periodically updated\r".cyan().italic());
println!("{}{}{}{}", "\r\n\tPress the key\r".cyan().bold(), "\r\n\t\t- 'p' to pause\r".yellow().bold(),
"\r\n\t\t- 's' to stop\r".red().bold(), "\r\n\tthe application\n\n\r".cyan().bold());
"\r\n\t\t- 's' to stop\r".red().bold(), "\r\n\tthe application\n\r".cyan().bold());
print!("\t{}{}{}\r", "Updating reports every ".cyan().blink().italic(),
interval.to_string().cyan().blink().italic(),
" seconds...".cyan().blink().italic());
io::stdout().flush().unwrap();
// to kill the main thread even if a secondary thread panics
let orig_hook = panic::take_hook();
@ -151,7 +155,7 @@ fn main() {
});
// Main thread: updates application status
set_status_by_key(status_pair2);
set_status_by_key(status_pair2, interval);
// Wait for the final report update, to not kill the application while the report is being written
thread_write_report.join().expect("Thread in charge of writing report panicked!\r\n");
@ -268,7 +272,7 @@ fn is_valid_transport_layer(transport_layer: String) -> bool {
/// # Arguments
///
/// * `status_pair` - Shared variable to change the application current status.
fn set_status_by_key(status_pair: Arc<(Mutex<Status>, Condvar)>) {
fn set_status_by_key(status_pair: Arc<(Mutex<Status>, Condvar)>, interval: u64) {
let _raw = RawScreen::into_raw_mode();
let mut reader = input().read_sync();
@ -279,6 +283,8 @@ fn set_status_by_key(status_pair: Arc<(Mutex<Status>, Condvar)>) {
match event {
InputEvent::Keyboard(KeyEvent::Char('p')) => {
if *status == Status::Running {
print!(" \r");
io::stdout().flush().unwrap();
println!("\t{}", "Sniffnet paused...\r".yellow().bold());
print!("\t{}", "Press 'r' to resume\r".yellow().blink().bold());
io::stdout().flush().unwrap();
@ -287,17 +293,21 @@ fn set_status_by_key(status_pair: Arc<(Mutex<Status>, Condvar)>) {
}
InputEvent::Keyboard(KeyEvent::Char('r')) => {
if *status == Status::Pause {
print!(" \r");
print!(" \r");
io::stdout().flush().unwrap();
println!("\t{}", "Sniffnet resumed\r".green().bold());
print!("\t{}{}{}\r", "Updating reports every ".cyan().blink().italic(),
interval.to_string().cyan().blink().italic(),
" seconds...".cyan().blink().italic());
io::stdout().flush().unwrap();
*status = Status::Running;
cvar.notify_all();
}
}
InputEvent::Keyboard(KeyEvent::Char('s')) => {
print!(" \r");
print!(" \r");
io::stdout().flush().unwrap();
println!("\r\t{}", "Sniffnet stopped... waiting for the last report update\n\n\r".red().bold());
println!("\r\t{}", "Sniffnet stopped... waiting for the last reports update\n\n\r".red().bold());
*status = Status::Stop;
cvar.notify_all();
return;

View file

@ -22,9 +22,6 @@ use crate::{address_port_pair::AddressPortPair, AppProtocol, info_address_port_p
use std::time::{Instant};
use plotters::style::full_palette::{GREEN_600, GREY};
#[cfg(feature = "draw_graph")]
use charts::{Chart, ScaleLinear, MarkerType, LineSeriesView, AreaSeriesView, AxisPosition, Color};
/// The calling thread enters in a loop in which it waits for ```interval``` seconds and then re-write
/// from scratch the output report file, with updated values.
@ -73,8 +70,7 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
let path_graph = &*format!("{}/bandwidth.svg", output_folder);
let mut times_report_updated: u128 = 0;
let mut last_report_updated_console: u128 = 0;
let mut tot_intervals: u128 = 0;
let time_origin = Local::now();
let first_timestamp = time_origin.format("%d/%m/%Y %H:%M:%S").to_string();
#[cfg(feature = "elapsed_time")]
@ -97,10 +93,10 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
let mut max_received_packets_second: i128 = 0;
loop {
// sleep interval seconds
thread::sleep(Duration::from_secs(interval));
times_report_updated += 1;
let mut output = BufWriter::new(File::create(format!("{}/report.txt", output_folder.clone())).expect("Error creating output file\n\r"));
tot_intervals += 1;
let info_traffic = info_traffic_mutex.lock().expect("Error acquiring mutex\n\r");
@ -113,33 +109,46 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
#[cfg(feature = "elapsed_time")]
let start = Instant::now();
write_report_file_header(output.get_mut().try_clone().expect("Error cloning file handler\n\r"),
device_name.clone(), first_timestamp.clone(),
times_report_updated, lowest_port, highest_port, min_packets,
network_layer.clone(), transport_layer.clone(), app_layer.clone(),
info_traffic.map.len(), all_packets,
tot_received_packets+tot_sent_packets, info_traffic.app_protocols.clone());
if *status_pair.0.lock().expect("Error acquiring mutex\n\r") != Status::Pause { // write textual report
#[cfg(feature = "elapsed_time")]
let time_header = start.elapsed().as_millis();
let mut output = BufWriter::new(File::create(format!("{}/report.txt", output_folder.clone())).expect("Error creating output file\n\r"));
let mut sorted_vec: Vec<(&AddressPortPair, &InfoAddressPortPair)> = info_traffic.map.iter().collect();
sorted_vec.sort_by(|&(_, a), &(_, b)|
b.transmitted_packets.cmp(&a.transmitted_packets));
write_report_file_header(output.get_mut().try_clone().expect("Error cloning file handler\n\r"),
device_name.clone(), first_timestamp.clone(),
lowest_port, highest_port, min_packets,
network_layer.clone(), transport_layer.clone(), app_layer.clone(),
info_traffic.map.len(), all_packets,
tot_received_packets+tot_sent_packets, info_traffic.app_protocols.clone());
#[cfg(feature = "elapsed_time")]
let time_header_sort = start.elapsed().as_millis();
#[cfg(feature = "elapsed_time")]
let time_header = start.elapsed().as_millis();
for (key, val) in sorted_vec.iter() {
if val.transmitted_packets >= min_packets {
write!(output, "{}\n{}\n\n", key, val).expect("Error writing output file\n\r");
let mut sorted_vec: Vec<(&AddressPortPair, &InfoAddressPortPair)> = info_traffic.map.iter().collect();
sorted_vec.sort_by(|&(_, a), &(_, b)|
b.transmitted_packets.cmp(&a.transmitted_packets));
#[cfg(feature = "elapsed_time")]
let time_header_sort = start.elapsed().as_millis();
for (key, val) in sorted_vec.iter() {
if val.transmitted_packets >= min_packets {
write!(output, "{}\n{}\n\n", key, val).expect("Error writing output file\n\r");
}
}
output.flush().expect("Error writing output file\n\r");
#[cfg(feature = "elapsed_time")]
let time_header_sort_print = start.elapsed().as_millis();
}
drop(info_traffic);
// graphs
#[cfg(feature = "elapsed_time")]
let start_drawing = Instant::now();
let root_area = SVGBackend::new(path_graph, (1250, 700)).into_drawing_area();
root_area.fill(&GREY).expect("Error drawing graph");
@ -153,12 +162,12 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
// bits graph
sent_bits_graph.push((interval as u128 * times_report_updated,(-1*(tot_sent_bytes*8) as i128 + tot_sent_bits_prev)/interval as i128 ));
sent_bits_graph.push((interval as u128 * tot_intervals,(-1*(tot_sent_bytes*8) as i128 + tot_sent_bits_prev)/interval as i128 ));
if -1*(tot_sent_bytes*8) as i128 + tot_sent_bits_prev < min_sent_bits_second {
min_sent_bits_second = -1*(tot_sent_bytes*8) as i128 + tot_sent_bits_prev;
}
tot_sent_bits_prev = (tot_sent_bytes * 8) as i128;
received_bits_graph.push((interval as u128 * times_report_updated, (tot_received_bytes as i128 * 8 - tot_received_bits_prev)/interval as i128 ));
received_bits_graph.push((interval as u128 * tot_intervals, (tot_received_bytes as i128 * 8 - tot_received_bits_prev)/interval as i128 ));
if tot_received_bytes as i128 * 8 - tot_received_bits_prev > max_received_bits_second {
max_received_bits_second = tot_received_bytes as i128 * 8 - tot_received_bits_prev ;
}
@ -167,7 +176,7 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
.set_label_area_size(LabelAreaPosition::Left, 60)
.set_label_area_size(LabelAreaPosition::Bottom, 60)
.caption("Bit traffic per second", ("sans-serif", 30))
.build_cartesian_2d(0..interval as u128 * times_report_updated, min_sent_bits_second/interval as i128..max_received_bits_second/interval as i128)
.build_cartesian_2d(0..interval as u128 * tot_intervals, min_sent_bits_second/interval as i128..max_received_bits_second/interval as i128)
.expect("Error drawing graph");
chart_bits.configure_mesh()
.y_desc("bit/s")
@ -205,12 +214,12 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
// packets graph
sent_packets_graph.push((interval as u128 * times_report_updated, (-1*(tot_sent_packets as i128) + tot_sent_packets_prev)/interval as i128 ));
sent_packets_graph.push((interval as u128 * tot_intervals, (-1*(tot_sent_packets as i128) + tot_sent_packets_prev)/interval as i128 ));
if -1*(tot_sent_packets as i128) + tot_sent_packets_prev < min_sent_packets_second {
min_sent_packets_second = -1*(tot_sent_packets as i128) + tot_sent_packets_prev;
}
tot_sent_packets_prev = tot_sent_packets as i128;
received_packets_graph.push((interval as u128 * times_report_updated, (tot_received_packets as i128 - tot_received_packets_prev)/interval as i128 ));
received_packets_graph.push((interval as u128 * tot_intervals, (tot_received_packets as i128 - tot_received_packets_prev)/interval as i128 ));
if tot_received_packets as i128 - tot_received_packets_prev > max_received_packets_second {
max_received_packets_second = tot_received_packets as i128 - tot_received_packets_prev;
}
@ -219,7 +228,7 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
.set_label_area_size(LabelAreaPosition::Left, 60)
.set_label_area_size(LabelAreaPosition::Bottom, 60)
.caption("Packet traffic per second", ("sans-serif", 30))
.build_cartesian_2d(0..interval as u128*times_report_updated, min_sent_packets_second/interval as i128..max_received_packets_second/interval as i128)
.build_cartesian_2d(0..interval as u128*tot_intervals, min_sent_packets_second/interval as i128..max_received_packets_second/interval as i128)
.expect("Error drawing graph");
chart_packets.configure_mesh()
.y_desc("packet/s")
@ -249,6 +258,11 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
root_area.present().expect("Error drawing graph");
#[cfg(feature = "elapsed_time")]
let time_drawing = start_drawing.elapsed().as_millis();
#[cfg(feature = "elapsed_time")]
println!("Drawing time: {} ms", time_drawing);
#[cfg(feature = "elapsed_time")]
{
let time_header_sort_print = start.elapsed().as_millis();
@ -264,33 +278,17 @@ pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval
time_header_sort_print-time_header_sort,
time_header_sort_print).expect("Error writing output file\n\r");
if times_report_updated >= 10 {
if tot_intervals >= 10 {
write!(output, "\t\tTimings (average on the last 10 report writes):\n\
\t\t\tTot time mutex held: {}ms\n",
last_10_write_times.iter().sum::<u128>()/10).expect("Error writing output file\n\r");
last_10_write_times.remove(0);
}
output.flush().expect("Error writing output file\n\r");
}
output.flush().expect("Error writing output file\n\r");
let status = status_pair.0.lock().expect("Error acquiring mutex\n\r");
if *status == Status::Running {
if times_report_updated - last_report_updated_console != 1 {
println!("{}{}{}{}\r", "\tReport updated (".cyan().italic(),
times_report_updated.to_string().cyan().italic(), ")".cyan().italic(),
" - reports have also been updated during pause".cyan().italic());
}
else {
println!("{}{}{}\r", "\tReports updated (".cyan().italic(),
times_report_updated.to_string().cyan().italic(), ")".cyan().italic());
}
last_report_updated_console = times_report_updated;
}
else if *status == Status::Stop {
println!("{}{}{}\r", "\tThe final report is available in the file '".cyan().italic(),
if *status_pair.0.lock().expect("Error acquiring mutex\n\r") == Status::Stop {
println!("{}{}{}\r", "\tThe final reports are available in the folder '".cyan().italic(),
output_folder.clone().cyan().bold(), "'\n\n\r".cyan().italic());
return;
}
@ -500,8 +498,6 @@ fn get_app_count_string(app_count: HashMap<AppProtocol, u128>, tot_packets: u128
///
/// * `first_timestamp` - A not formatted String representing the initial timestamp of the sniffing process.
///
/// * `times_report_updated` - An integer representing the amount of times the report has been updated.
///
/// * `lowest_port` - The lowest port number to be considered in the report. Specified by the user
/// through the ```-l``` option.
///
@ -547,7 +543,7 @@ fn get_app_count_string(app_count: HashMap<AppProtocol, u128>, tot_packets: u128
/// <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
/// ```
fn write_report_file_header(mut output: File, device_name: String, first_timestamp: String,
times_report_updated: u128, lowest_port: u16, highest_port: u16,
lowest_port: u16, highest_port: u16,
min_packets: u128, network_layer: String, transport_layer: String, app_layer: AppProtocol,
num_pairs: usize, num_sniffed_packets: u128, num_filtered_packets: u128,
app_count: HashMap<AppProtocol, u128>) {
@ -556,7 +552,6 @@ fn write_report_file_header(mut output: File, device_name: String, first_timesta
let adapter_string = format!("<><>\t\tPackets are sniffed from adapter '{}'\n", device_name);
let first_timestamp_string = format!("<><>\t\t\tReport start time: {}\n", first_timestamp);
let last_timestamp_string = format!("<><>\t\t\tReport last update: {}\n", Local::now().format("%d/%m/%Y %H:%M:%S").to_string());
let number_updates_string = format!("<><>\t\t\tNumber of times report was updated: {}\n", times_report_updated.separate_with_underscores());
let ports_string = get_ports_string(lowest_port,highest_port);
let network_layer_string = get_network_layer_string(network_layer);
let transport_layer_string = get_transport_layer_string(transport_layer);
@ -572,7 +567,6 @@ fn write_report_file_header(mut output: File, device_name: String, first_timesta
write!(output, "<><>\t\tReport updates info\n").expect("Error writing output file\n");
write!(output, "{}", first_timestamp_string).expect("Error writing output file\n");
write!(output, "{}", last_timestamp_string).expect("Error writing output file\n");
write!(output, "{}", number_updates_string).expect("Error writing output file\n");
write!(output, "<><>\n").expect("Error writing output file\n");
write!(output, "<><>\t\tFilters\n").expect("Error writing output file\n");