pcapforge_core/
feature_store.rs1use anyhow::Result;
2use chrono::{DateTime, Utc};
3use polars::prelude::*;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::Path;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct PacketFeatures {
10 pub timestamp: DateTime<Utc>,
11 pub src_ip: String,
12 pub dst_ip: String,
13 pub src_port: Option<u16>,
14 pub dst_port: Option<u16>,
15 pub protocol: String,
16 pub packet_size: u32,
17 pub flags: HashMap<String, bool>,
18 pub payload_size: u32,
19 pub ttl: Option<u8>,
20 pub window_size: Option<u16>,
21 pub custom_features: HashMap<String, serde_json::Value>,
22}
23
24pub struct FeatureStore {
25 features: Vec<PacketFeatures>,
26 dataframe: Option<DataFrame>,
27}
28
29impl FeatureStore {
30 pub fn new() -> Self {
31 Self {
32 features: Vec::new(),
33 dataframe: None,
34 }
35 }
36
37 pub fn add_features(&mut self, features: PacketFeatures) {
38 self.features.push(features);
39 }
40
41 pub fn build_dataframe(&mut self) -> Result<()> {
42 if self.features.is_empty() {
43 return Ok(());
44 }
45
46 let mut timestamps = Vec::new();
47 let mut src_ips = Vec::new();
48 let mut dst_ips = Vec::new();
49 let mut src_ports = Vec::new();
50 let mut dst_ports = Vec::new();
51 let mut protocols = Vec::new();
52 let mut packet_sizes = Vec::new();
53 let mut payload_sizes = Vec::new();
54
55 for feature in &self.features {
56 timestamps.push(feature.timestamp.timestamp_millis());
57 src_ips.push(feature.src_ip.clone());
58 dst_ips.push(feature.dst_ip.clone());
59 src_ports.push(feature.src_port);
60 dst_ports.push(feature.dst_port);
61 protocols.push(feature.protocol.clone());
62 packet_sizes.push(feature.packet_size);
63 payload_sizes.push(feature.payload_size);
64 }
65
66 let df = DataFrame::new(vec![
67 Column::new("timestamp".into(), timestamps),
68 Column::new("src_ip".into(), src_ips),
69 Column::new("dst_ip".into(), dst_ips),
70 Column::new("src_port".into(), src_ports.into_iter().map(|p| p.unwrap_or(0) as u32).collect::<Vec<_>>()),
71 Column::new("dst_port".into(), dst_ports.into_iter().map(|p| p.unwrap_or(0) as u32).collect::<Vec<_>>()),
72 Column::new("protocol".into(), protocols),
73 Column::new("packet_size".into(), packet_sizes),
74 Column::new("payload_size".into(), payload_sizes),
75 ])?;
76
77 self.dataframe = Some(df);
78 Ok(())
79 }
80
81 pub fn save_parquet<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
82 if self.dataframe.is_none() {
83 self.build_dataframe()?;
84 }
85
86 if let Some(df) = &mut self.dataframe {
87 let file = std::fs::File::create(path)?;
88 ParquetWriter::new(file).finish(df)?;
89 }
90
91 Ok(())
92 }
93
94 pub fn save_json<P: AsRef<Path>>(&self, path: P) -> Result<()> {
95 let file = std::fs::File::create(path)?;
96 serde_json::to_writer_pretty(file, &self.features)?;
97 Ok(())
98 }
99
100 pub fn save_csv<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
101 if self.dataframe.is_none() {
102 self.build_dataframe()?;
103 }
104
105 if let Some(df) = &mut self.dataframe {
106 let mut file = std::fs::File::create(path)?;
107 CsvWriter::new(&mut file).finish(df)?;
108 }
109
110 Ok(())
111 }
112
113 pub fn get_statistics(&self) -> FeatureStatistics {
114 let total_packets = self.features.len();
115 let mut protocol_counts: HashMap<String, usize> = HashMap::new();
116 let mut total_bytes = 0u64;
117
118 for feature in &self.features {
119 *protocol_counts.entry(feature.protocol.clone()).or_insert(0) += 1;
120 total_bytes += feature.packet_size as u64;
121 }
122
123 FeatureStatistics {
124 total_packets,
125 total_bytes,
126 protocol_distribution: protocol_counts,
127 unique_src_ips: self.features.iter()
128 .map(|f| f.src_ip.clone())
129 .collect::<std::collections::HashSet<_>>()
130 .len(),
131 unique_dst_ips: self.features.iter()
132 .map(|f| f.dst_ip.clone())
133 .collect::<std::collections::HashSet<_>>()
134 .len(),
135 }
136 }
137}
138
139#[derive(Debug, Serialize)]
140pub struct FeatureStatistics {
141 pub total_packets: usize,
142 pub total_bytes: u64,
143 pub protocol_distribution: HashMap<String, usize>,
144 pub unique_src_ips: usize,
145 pub unique_dst_ips: usize,
146}