mirror of
https://github.com/GyulyVGC/sniffnet.git
synced 2026-10-24 08:02:12 +00:00
removed -m and -v options. optimized and modularized textual report write
This commit is contained in:
parent
ef84d6c943
commit
758c051666
7 changed files with 33 additions and 223 deletions
|
|
@ -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 = []
|
||||
11
README.md
11
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"```.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/args.rs
12
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue