removed -m and -v options. optimized and modularized textual report write

This commit is contained in:
Giuliano Bellini s294739 2022-09-29 22:33:56 +02:00
parent ef84d6c943
commit 758c051666
7 changed files with 33 additions and 223 deletions

View file

@ -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 = []
[features]
default = ["elapsed_time"]
elapsed_time = []

View file

@ -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"```.

View file

@ -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)
}
}

View file

@ -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.

View file

@ -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
)
}
}

View file

@ -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);

View file

@ -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<Mutex<InfoTraffic>>,
status_pair: Arc<(Mutex<Status>, 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> = 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> = 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<AppProtocol, u128>, 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<AppProtocol, u128>, 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<AppProtocol, u128>) {
@ -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");
}