use std::env;
use flowscope::extract::FiveTuple;
use flowscope::pcap::PcapFlowSource;
use flowscope::{CicFlowFeatures, FlowEvent, FlowRecord, FlowTracker};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = env::args()
.nth(1)
.ok_or("usage: ml_features_libsvm <trace.pcap>")?;
let mut tracker = FlowTracker::<FiveTuple>::new(FiveTuple::bidirectional());
let mut flows_emitted = 0u64;
for owned in PcapFlowSource::open(&path)?.views() {
let owned = owned?;
for ev in tracker.track(&owned) {
if let FlowEvent::Ended {
key, stats, reason, ..
} = ev
{
let rec = FlowRecord::from_parts(&stats, &key, Some(reason));
let feats = CicFlowFeatures::from_flow_record(&rec).with_iat(&stats);
emit_libsvm(&feats);
flows_emitted += 1;
}
}
}
eprintln!("{flows_emitted} flow(s) exported.");
Ok(())
}
fn emit_libsvm(f: &CicFlowFeatures) {
let pairs: [(u32, f64); 32] = [
(1, f.flow_duration_us as f64),
(2, f.total_fwd_packets as f64),
(3, f.total_bwd_packets as f64),
(4, f.total_fwd_bytes as f64),
(5, f.total_bwd_bytes as f64),
(6, f.fwd_packet_length_mean),
(7, f.bwd_packet_length_mean),
(8, f.mean_packet_length),
(9, f.flow_bytes_per_sec),
(10, f.flow_packets_per_sec),
(11, f.down_up_ratio),
(12, f.flow_iat_mean_us),
(13, f.flow_iat_std_us),
(14, f.flow_iat_min_us),
(15, f.flow_iat_max_us),
(16, f.fwd_iat_mean_us),
(17, f.fwd_iat_std_us),
(18, f.fwd_iat_min_us),
(19, f.fwd_iat_max_us),
(20, f.bwd_iat_mean_us),
(21, f.bwd_iat_std_us),
(22, f.bwd_iat_min_us),
(23, f.bwd_iat_max_us),
(24, f.active_mean_us),
(25, f.active_std_us),
(26, f.active_min_us),
(27, f.active_max_us),
(28, f.idle_mean_us),
(29, f.idle_std_us),
(30, f.idle_min_us),
(31, f.idle_max_us),
(
32,
(f.tcp_flag_counts.fwd_fin + f.tcp_flag_counts.bwd_fin) as f64,
),
];
let mut line = String::from("0");
for (idx, v) in pairs {
line.push(' ');
line.push_str(&idx.to_string());
line.push(':');
line.push_str(&format_value(v));
}
println!("{line}");
}
fn format_value(v: f64) -> String {
if v == 0.0 || v.is_nan() {
"0".to_string()
} else if v.fract() == 0.0 && v.abs() < 1e15 {
format!("{}", v as i64)
} else {
format!("{v:.6}")
}
}