From 758c0516664395d00a2435414c2250e891710c5a Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Thu, 29 Sep 2022 22:33:56 +0200 Subject: [PATCH] removed -m and -v options. optimized and modularized textual report write --- Cargo.toml | 6 +- README.md | 11 -- src/address_port_pair.rs | 34 +----- src/args.rs | 12 --- src/info_address_port_pair.rs | 36 +++---- src/main.rs | 6 +- src/thread_write_report_functions.rs | 151 +++------------------------ 7 files changed, 33 insertions(+), 223 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c360f03..48a285fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,6 @@ indexmap = "1.9.1" #used for debug purposes to measure time needed to write output reports -#[features] -#default = ["elapsed_time"] -#elapsed_time = [] \ No newline at end of file +[features] +default = ["elapsed_time"] +elapsed_time = [] \ No newline at end of file diff --git a/README.md b/README.md index 74c095e9..2f002b45 100644 --- a/README.md +++ b/README.md @@ -85,10 +85,6 @@ sets the interval of time between report updates (value in seconds). specifies the minimum port value to be considered; if omitted there is no ports lower bound. - - **-m, --minimum-packets**: -sets the minimum value of exchanged packets between an [address:port] pair to be printed in the textual report. - - - **-n, --net**: filters packets on the basis of the provided IP address version (IPv4 or IPv6). @@ -100,9 +96,6 @@ specifies the name of the output folder to contain textual and graphical reports - **-t, --trans**: filters packets on the basis of the provided transport layer protocol (TCP or UDP). -- **-v, --verbose**: -generates a more self-explainable textual report. - ## User interactions during application execution @@ -284,10 +277,6 @@ if the provided lowest port number is not an integer in the range ```0..=65535`` If also the highest port number is specified and ```highest_port < lowest_port == true``` the program raises an error and terminates. -- **Invalid minimum packets value**: -if the provided minimum packets value is not an integer in the range ```0..=u128::MAX``` the program raises an error and terminates. - - - **Invalid network layer protocol filter**: if a string different from "IPv4", "IPv6" or "no filter" is provided (not case-sensitive), the application raises an error and terminates. Note that not including the ```-n``` option is equal to provide ```-n "no filter"```. diff --git a/src/address_port_pair.rs b/src/address_port_pair.rs index a22056cd..c9fd80c3 100644 --- a/src/address_port_pair.rs +++ b/src/address_port_pair.rs @@ -44,38 +44,8 @@ impl AddressPortPair { impl fmt::Display for AddressPortPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - - let addr_string_1 = if self.address1.contains(':') { // IPv6 address is enclosed in [brackets] - format!("| [{}]:{} |", self.address1, self.port1) - } - else { - format!("| {}:{} |", self.address1, self.port1) - }; - - let addr_string_2 = if self.address2.contains(':') { // IPv6 address is enclosed in [brackets] - format!("| [{}]:{} |", self.address2, self.port2) - } - else { - format!("| {}:{} |", self.address2, self.port2) - }; - - let addresses_string = format!("{} ---> {}", addr_string_1, addr_string_2); - - let spaces = " ".to_string().repeat(addr_string_1.len()-6); - let my_interface_string = match self.traffic_type { - TrafficType::Incoming => {format!(" Remote address{}Your interface", spaces)} - TrafficType::Outgoing => {format!(" Your interface{}Remote Address", spaces)} - TrafficType::Multicast => {format!(" Remote address{}Multicast address", spaces)} - TrafficType::Other => {format!(" Remote address{}Remote address", spaces)} - }; - - let cornice_up_string = format!(" /{}\\ /{}\\", "-".to_string().repeat(addr_string_1.len() - 4), - "-".to_string().repeat(addr_string_2.len() - 4)); - - let cornice_down_string = format!(" \\{}/ \\{}/", "-".to_string().repeat(addr_string_1.len() - 4), - "-".to_string().repeat(addr_string_2.len() - 4)); - - write!(f,"{}\n{}\n{}\n{}", my_interface_string, cornice_up_string, addresses_string, cornice_down_string) + write!(f,"|{:^25}|{:>8} |{:^25}|{:>8} |", + self.address1, self.port1, self.address2, self.port2) } } diff --git a/src/args.rs b/src/args.rs index 2dcb5122..0c421fb7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -18,12 +18,6 @@ pub struct Args { #[clap(long="app", value_parser, default_value = "no filter")] pub application_layer_filter: String, - /// Prints textual report with a verbose format. - /// - /// This option does not need to be followed by a value. - #[clap(short, long)] - pub verbose: bool, - /// Prints list of the available network interfaces. /// /// Immediately terminates the program. @@ -54,12 +48,6 @@ pub struct Args { #[clap(short, long, value_parser, default_value_t = u16::MIN)] pub lowest_port: u16, - /// Sets the minimum value of transited packets for an address:port to be printed in the report. - /// - /// This option must be followed by a positive integer value. - #[clap(short, long, value_parser, default_value_t = 0)] - pub minimum_packets: u128, - /// Filters packets on the basis of the IP version address (IPv4 or IPv6). /// /// This option must be followed by a textual value. diff --git a/src/info_address_port_pair.rs b/src/info_address_port_pair.rs index 67e0d49f..ca662d4b 100644 --- a/src/info_address_port_pair.rs +++ b/src/info_address_port_pair.rs @@ -2,7 +2,6 @@ //! to keep track of statistics about the sniffed traffic. use std::fmt; -use thousands::Separable; /// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic. /// @@ -29,32 +28,23 @@ impl fmt::Display for InfoAddressPortPair { match self.transmitted_bytes { 0..=999 => {}, - 1000..=999999 => {n /= 1000_f32; multiple_transmitted.push('k'); }, - 1000000..=999999999 => {n /= 1000000_f32; multiple_transmitted.push('M');}, - _ => {n /= 1000000000_f32; multiple_transmitted.push('G'); } + 1_000..=999_999 => {n /= 1000_f32; multiple_transmitted.push('k'); }, // kilo + 1_000_000..=999_999_999 => {n /= 1_000_000_f32; multiple_transmitted.push('M');}, // mega + 1_000_000_000..=999_999_999_999 => {n /= 1_000_000_000_f32; multiple_transmitted.push('G');}, // giga + _ => {n /= 1_000_000_000_000_f32; multiple_transmitted.push('T');} // tera } - let set_precision = |prefix: &String, &n| { - if !prefix.is_empty() // no multiple - { - if n < 10.0 {2} - else if n < 100.0 {1} - else {0} - } else {0} - }; + let bytes_string = if !multiple_transmitted.is_empty() { // with multiple + format!("{:.1} {}B", n, multiple_transmitted) + } else { // no multiple + format!("{} B", n) + }; - let precision: usize = set_precision(&multiple_transmitted, &n); + write!(f, " {} |{:^9}|{:>10} |{:>10} | {} | {} |", + self.trans_protocol, self.app_protocol.to_string(), + self.transmitted_packets, bytes_string, + self.initial_timestamp, self.final_timestamp) - write!(f, "\t\tExchanged packets: {}\n\ - \t\tExchanged Bytes: {:.*} {}B\n\ - \t\tInitial Timestamp: {}\n\ - \t\tFinal Timestamp: {}\n\ - \t\tTransport layer protocol: {}\n\ - \t\tApplication layer protocol: {}\n\n", - self.transmitted_packets.separate_with_underscores(), precision, n, multiple_transmitted, - self.initial_timestamp, self.final_timestamp, - self.trans_protocol, self.app_protocol - ) } } diff --git a/src/main.rs b/src/main.rs index 5ef5f21e..bfe11669 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -// #![feature(test)] - //! Multithreading application to intercept incoming and outgoing traffic through a user specified network interface of a computer. //! //! The application will periodically generate a human readable textual report, @@ -57,11 +55,9 @@ fn main() { // parse arguments let args = Args::parse(); let mut adapter: String = args.adapter; - let verbose: bool = args.verbose; let output_folder: String = args.output_folder; let lowest_port = args.lowest_port; let highest_port = args.highest_port; - let min_packets = args.minimum_packets; let interval: u64 = args.interval; let network_layer: String = args.network_layer_filter.to_ascii_lowercase(); let network_layer_2: String = network_layer.clone(); @@ -144,7 +140,7 @@ fn main() { // Thread 1: updates textual report let thread_write_report = thread::spawn(move || { - sleep_and_write_report_loop(verbose,lowest_port, highest_port, interval, min_packets, + sleep_and_write_report_loop(lowest_port, highest_port, interval, device_name, network_layer, transport_layer, app_layer.unwrap(), output_folder, mutex_map2, status_pair3); diff --git a/src/thread_write_report_functions.rs b/src/thread_write_report_functions.rs index f64bf4e6..6a4c7508 100644 --- a/src/thread_write_report_functions.rs +++ b/src/thread_write_report_functions.rs @@ -27,9 +27,6 @@ use plotters::style::full_palette::{GREEN_800, GREY}; /// /// # Arguments /// -/// * `verbose` - Flag: if true textual report is verbose. -/// Specified by the user through the ```-v``` option. -/// /// * `lowest_port` - The lowest port number to be considered in the report. Specified by the user /// through the ```-l``` option. /// @@ -39,9 +36,6 @@ use plotters::style::full_palette::{GREEN_800, GREY}; /// * `interval` - Frequency of report updates (value in seconds). Specified by the user through the /// ```-i``` option. /// -/// * `min_packets` - Minimum number of packets for an address:port pair to be considered in the report. -/// Specified by the user through the ```-m``` option. -/// /// * `device_name` - A String representing the name of th network adapter to be sniffed. Specified by the user through the /// ```-a``` option. /// @@ -60,8 +54,8 @@ use plotters::style::full_palette::{GREEN_800, GREY}; /// * `info_traffic_mutex` - Struct with all the relevant info on the network traffic analyzed. /// /// * `status_pair` - Shared variable to check the application current status. -pub fn sleep_and_write_report_loop(verbose: bool, lowest_port: u16, highest_port: u16, interval: u64, min_packets: u128, - device_name: String, network_layer: String, transport_layer: String, app_layer: AppProtocol, +pub fn sleep_and_write_report_loop(lowest_port: u16, highest_port: u16, interval: u64, device_name: String, + network_layer: String, transport_layer: String, app_layer: AppProtocol, output_folder: String, info_traffic_mutex: Arc>, status_pair: Arc<(Mutex, Condvar)>) { @@ -100,11 +94,9 @@ pub fn sleep_and_write_report_loop(verbose: bool, lowest_port: u16, highest_port let text_path = format!("{}/report.txt", output_folder.clone()); let header_path = format!("{}/statistics.txt", output_folder.clone()); let mut output = BufWriter::new(File::create(text_path.clone()).expect("Error creating output file\n\r")); - if !verbose { - writeln!(output, "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); - writeln!(output, "| Src IP address | Src port | Dst IP address | Dst port | Layer 4 | Layer 7 | Packets | Bytes | Initial timestamp | Final timestamp |").expect("Error writing output file\n\r"); - writeln!(output, "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); - } + writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); + writeln!(output, "| Src IP address | Src port | Dst IP address | Dst port | Layer 4 | Layer 7 | Packets | Bytes | Initial timestamp | Final timestamp |").expect("Error writing output file\n\r"); + writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); loop { // sleep interval seconds @@ -128,10 +120,11 @@ pub fn sleep_and_write_report_loop(verbose: bool, lowest_port: u16, highest_port write_report_file_header(output_header.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, + lowest_port, highest_port, network_layer.clone(), + transport_layer.clone(), app_layer, info_traffic.map.len(), all_packets, - tot_received_packets+tot_sent_packets, info_traffic.app_protocols.clone()); + tot_received_packets+tot_sent_packets, + info_traffic.app_protocols.clone()); output_header.flush().expect("Error writing output file\n\r"); _time_header = start.elapsed().as_millis(); @@ -139,21 +132,10 @@ pub fn sleep_and_write_report_loop(verbose: bool, lowest_port: u16, highest_port for key in info_traffic.addresses_last_interval.iter() { let val = info_traffic.map.get(key).unwrap(); - if val.transmitted_packets >= min_packets { - if !verbose { // concise - let index = info_traffic.map.get_index_of(key).unwrap(); - let seek_pos = 504 + 168*index as u64; - output.seek(SeekFrom::Start(seek_pos)).unwrap(); - writeln!(output, "|{:^25}|{:>8} |{:^25}|{:>8} | {} |{:^9}|{:>10} |{:>12} | {} | {} |", - key.address1, key.port1, key.address2, key.port2, - val.trans_protocol, val.app_protocol.to_string(), - val.transmitted_packets, val.transmitted_bytes, - val.initial_timestamp, val.final_timestamp).expect("Error writing output file\n\r"); - } - else { // verbose - write!(output, "{}\n{}\n\n", key, val).expect("Error writing output file\n\r"); - } - } + let index = info_traffic.map.get_index_of(key).unwrap(); + let seek_pos = 166*(3 + index as u64); + output.seek(SeekFrom::Start(seek_pos)).unwrap(); + writeln!(output, "{}{}", key, val).expect("Error writing output file\n\r"); } info_traffic.addresses_last_interval = HashSet::new(); // empty set @@ -310,92 +292,6 @@ pub fn sleep_and_write_report_loop(verbose: bool, lowest_port: u16, highest_port } -// #[cfg(test)] -// mod print_report_entry_benchmark { -// extern crate test; -// -// use std::fs::File; -// use std::io::BufWriter; -// use std::io::Write; -// use test::Bencher; -// use chrono::{DateTime, Local}; -// use crate::address_port_pair::{AddressPortPair, TrafficType}; -// use crate::info_address_port_pair::InfoAddressPortPair; -// use crate::{AppProtocol, TransProtocol}; -// -// #[bench] -// fn bench_print_verbose(b: &mut Bencher) { -// let mut bench_vec: Vec<(&AddressPortPair, &InfoAddressPortPair)> = vec![]; -// let key = AddressPortPair::new -// ("255.255.255.255".to_string(), -// 443, -// "245.78.32.123".to_string(), -// 40900, -// TransProtocol::TCP, -// TrafficType::Outgoing -// ); -// let now_ugly: DateTime = Local::now(); -// let now = now_ugly.format("%d/%m/%Y %H:%M:%S").to_string(); -// let val = InfoAddressPortPair { -// transmitted_bytes: 5002222896, -// transmitted_packets: 89394742, -// initial_timestamp: now.clone(), -// final_timestamp: now.clone(), -// trans_protocol: TransProtocol::TCP, -// app_protocol: AppProtocol::HTTPS -// }; -// for _ in 0..3500 { -// bench_vec.push((&key, &val)); -// } -// let mut bench_output = BufWriter::new(File::create("bench_verbose.txt").expect("Error creating output file\n\r")); -// b.iter(|| { -// for (key, val) in bench_vec.iter() { -// write!(bench_output, "{}\n{}\n\n", key, val).expect("Error writing output file\n\r") -// } -// bench_output.flush().unwrap(); -// }); -// } -// -// #[bench] -// fn bench_print_concise(b: &mut Bencher) { -// let mut bench_vec: Vec<(&AddressPortPair, &InfoAddressPortPair)> = vec![]; -// let key = AddressPortPair::new -// ("255.255.255.255".to_string(), -// 443, -// "245.78.32.123".to_string(), -// 40900, -// TransProtocol::TCP, -// TrafficType::Outgoing -// ); -// let now_ugly: DateTime = Local::now(); -// let now = now_ugly.format("%Y/%m/%d %H:%M:%S").to_string(); -// let val = InfoAddressPortPair { -// transmitted_bytes: 5002222896, -// transmitted_packets: 89394742, -// initial_timestamp: now.clone(), -// final_timestamp: now.clone(), -// trans_protocol: TransProtocol::TCP, -// app_protocol: AppProtocol::HTTPS -// }; -// for _ in 0..3500 { -// bench_vec.push((&key, &val)); -// } -// let mut bench_output = BufWriter::new(File::create("bench_concise.txt").expect("Error creating output file\n\r")); -// b.iter(|| { -// for (key, val) in bench_vec.iter() { -// writeln!(bench_output, "|{:^25}|{:>8} |{:^25}|{:>8} | {} |{:^9}|{:>10} |{:>12} | {} | {} |", -// key.address1, key.port1, key.address2, key.port2, -// val.trans_protocol, val.app_protocol.to_string(), -// val.transmitted_packets, val.transmitted_bytes, -// val.initial_timestamp, val.final_timestamp).expect("Error writing output file\n\r"); -// } -// bench_output.flush().unwrap(); -// }); -// } -// } - - - /// Given the lowest and highest port numbers, the function generates the corresponding String /// to be used in the output report file header. /// @@ -419,18 +315,6 @@ fn get_ports_string(lowest_port: u16, highest_port: u16) -> String { } -/// Given the minimum packets number, the function generates the corresponding String -/// to be used in the output report file header. -/// -/// # Arguments -/// -/// * `min_packets` - Minimum number of packets for an address:port pair to be considered in the report. -/// Specified by the user through the ```-m``` option. -fn get_min_packets_string(min_packets: u128) -> String { - format!("<><>\t\tShowing only [address:port] pairs featured by more than {} packets\n", min_packets) -} - - /// Given the network layer textual filter, the function generates the corresponding String /// to be used in the output report file header. /// @@ -604,9 +488,6 @@ fn get_app_count_string(app_count: HashMap, tot_packets: u128 /// * `highest_port` - The highest port number to be considered in the report. Specified by the user /// through the ```-h``` option. /// -/// * `min_packets` - Minimum number of packets for an address:port pair to be considered in the report. -/// Specified by the user through the ```-m``` option. -/// /// * `network_layer` - A String representing the IP version to be filtered. Specified by the user through the /// ```-n``` option. /// @@ -644,7 +525,7 @@ fn get_app_count_string(app_count: HashMap, tot_packets: u128 /// ``` fn write_report_file_header(mut output: File, device_name: String, first_timestamp: String, lowest_port: u16, highest_port: u16, - min_packets: u128, network_layer: String, transport_layer: String, app_layer: AppProtocol, + network_layer: String, transport_layer: String, app_layer: AppProtocol, num_pairs: usize, num_sniffed_packets: u128, num_filtered_packets: u128, app_count: HashMap) { @@ -683,10 +564,6 @@ fn write_report_file_header(mut output: File, device_name: String, first_timesta writeln!(output, "{}<><>", app_count_string).expect("Error writing output file\n"); } - if min_packets > 1 { - let min_packets_string = get_min_packets_string(min_packets); - writeln!(output, "{}<><>", min_packets_string).expect("Error writing output file\n"); - } write!(output,"{}", cornice_string).expect("Error writing output file\n"); write!(output,"{}\n\n\n", cornice_string).expect("Error writing output file\n"); } \ No newline at end of file