1use crate::structs::{FlowId, L4Proto, PacketDirection};
2use indicatif::ProgressBar;
3use indicatif::ProgressStyle;
4use pcap_file::pcap;
5use pnet_packet::Packet;
6use pnet_packet::ip::IpNextHeaderProtocols;
7use pnet_packet::{ethernet, ipv4, tcp, udp};
8use rand::Rng;
9use std::collections::HashMap;
10use std::fs::File;
11use std::fs::OpenOptions;
12use std::io::BufReader;
13use std::io::BufWriter;
14use std::io::Write;
15use std::net::Ipv4Addr;
16use std::path::Path;
17use std::time::Duration;
18
19const DURATION_THRESHOLD: Duration = Duration::from_secs(600);
20
21#[derive(Debug)]
24pub struct FlowStats {
26 pub timestamp: Duration,
27 pub duration: Duration,
28 pub protocol: L4Proto,
29 pub src_ip: Ipv4Addr,
30 pub dst_ip: Ipv4Addr,
31 pub src_port: u16,
32 pub dst_port: u16,
33 pub ttl_client: u8,
34 pub ttl_server: u8,
35 pub fwd_packets_count: usize,
36 pub bwd_packets_count: usize,
37 pub fwd_bytes: usize,
38 pub bwd_bytes: usize,
39 pub payloads: Vec<Vec<u8>>,
40 pub directions: Vec<PacketDirection>,
41 pub flags: Vec<u8>, pub iat: Vec<Duration>,
43}
44
45#[allow(clippy::upper_case_acronyms)]
46enum PacketInfo {
47 TCP(TCPPacketInfo),
48 UDP(UDPPacketInfo),
49 ICMP(ICMPPacketInfo),
50}
51
52trait PacketInfoTrait {
53 fn ts(&self) -> Duration;
54 fn ttl(&self) -> u8;
55 fn src_ip(&self) -> Ipv4Addr;
56 fn payload(self) -> Vec<u8>;
57 fn payload_size(&self) -> usize;
58}
59
60#[derive(Debug)]
61struct TCPPacketInfo {
62 payload: Vec<u8>,
63 ts: Duration,
64 flags: u8,
65 src_ip: Ipv4Addr,
66 ttl: u8,
67}
68
69impl PacketInfoTrait for TCPPacketInfo {
70 fn ts(&self) -> Duration {
71 self.ts
72 }
73 fn ttl(&self) -> u8 {
74 self.ttl
75 }
76 fn src_ip(&self) -> Ipv4Addr {
77 self.src_ip
78 }
79 fn payload(self) -> Vec<u8> {
80 self.payload
81 }
82 fn payload_size(&self) -> usize {
83 self.payload.len()
84 }
85}
86
87#[derive(Debug)]
88struct ICMPPacketInfo {
89 ts: Duration,
92 src_ip: Ipv4Addr,
93 ttl: u8,
94}
95
96impl PacketInfoTrait for ICMPPacketInfo {
97 fn ts(&self) -> Duration {
98 self.ts
99 }
100 fn ttl(&self) -> u8 {
101 self.ttl
102 }
103 fn src_ip(&self) -> Ipv4Addr {
104 self.src_ip
105 }
106 fn payload(self) -> Vec<u8> {
107 vec![]
108 }
109 fn payload_size(&self) -> usize {
110 0
111 }
112}
113
114#[derive(Debug)]
115struct UDPPacketInfo {
116 payload: Vec<u8>,
117 ts: Duration,
118 src_ip: Ipv4Addr,
119 ttl: u8,
120}
121
122impl PacketInfoTrait for UDPPacketInfo {
123 fn ts(&self) -> Duration {
124 self.ts
125 }
126 fn ttl(&self) -> u8 {
127 self.ttl
128 }
129 fn src_ip(&self) -> Ipv4Addr {
130 self.src_ip
131 }
132 fn payload(self) -> Vec<u8> {
133 self.payload
134 }
135 fn payload_size(&self) -> usize {
136 self.payload.len()
137 }
138}
139
140impl From<pcap::PcapPacket<'_>> for PacketInfo {
141 fn from(p: pcap::PcapPacket<'_>) -> PacketInfo {
142 let eth_packet = ethernet::EthernetPacket::new(&p.data).unwrap();
143 let ip_packet = ipv4::Ipv4Packet::new(eth_packet.payload()).unwrap();
144 let ttl = ip_packet.get_ttl();
145
146 match ip_packet.get_next_level_protocol() {
147 IpNextHeaderProtocols::Tcp => {
148 let tcp_packet = tcp::TcpPacket::new(ip_packet.payload()).unwrap();
149 PacketInfo::TCP(TCPPacketInfo {
150 payload: tcp_packet.payload().to_vec(),
151 ts: p.timestamp,
152 flags: tcp_packet.get_flags(),
153 src_ip: ip_packet.get_source(),
154 ttl,
155 })
156 }
157 IpNextHeaderProtocols::Udp => {
158 let udp_packet = udp::UdpPacket::new(ip_packet.payload()).unwrap();
159 PacketInfo::UDP(UDPPacketInfo {
160 payload: udp_packet.payload().to_vec(),
161 ts: p.timestamp,
162 src_ip: ip_packet.get_source(),
163 ttl,
164 })
165 }
166 IpNextHeaderProtocols::Icmp => PacketInfo::ICMP(ICMPPacketInfo {
167 ts: p.timestamp,
168 src_ip: ip_packet.get_source(),
169 ttl,
170 }),
171
172 _ => {
173 panic!("Unsupported protocol")
175 }
176 }
177 }
178}
179
180impl FlowStats {
181 fn process_packets<T: PacketInfoTrait>(flow_id: FlowId, packets: Vec<T>) -> FlowStats {
183 let first_packet = packets.first().unwrap(); let timestamp = first_packet.ts();
185 let duration = packets
186 .last()
187 .unwrap()
188 .ts()
189 .checked_sub(first_packet.ts())
190 .unwrap();
191
192 let iat: Vec<Duration> = packets
193 .windows(2)
194 .map(|packets| packets[1].ts().checked_sub(packets[0].ts()).unwrap())
195 .collect();
196
197 let fwd_bytes: usize = packets
198 .iter()
199 .filter_map(|p| {
200 if p.src_ip() == flow_id.src_ip {
201 Some(p.payload_size())
202 } else {
203 None
204 }
205 })
206 .sum();
207
208 let bwd_bytes: usize = packets
209 .iter()
210 .filter_map(|p| {
211 if p.src_ip() == flow_id.src_ip {
212 None
213 } else {
214 Some(p.payload_size())
215 }
216 })
217 .sum();
218
219 let ttl_client = packets
222 .iter()
223 .find(|p| p.src_ip() == flow_id.src_ip)
224 .map_or(0, PacketInfoTrait::ttl);
225
226 let ttl_server = packets
227 .iter()
228 .find(|p| p.src_ip() != flow_id.src_ip)
229 .map_or(0, PacketInfoTrait::ttl);
230
231 let directions: Vec<PacketDirection> = packets
232 .iter()
233 .map(|p| {
234 if p.src_ip() == flow_id.src_ip {
235 PacketDirection::Forward
236 } else {
237 PacketDirection::Backward
238 }
239 })
240 .collect();
241
242 let fwd_packets_count = directions
243 .iter()
244 .filter(|&d| *d == PacketDirection::Forward)
245 .count();
246
247 let bwd_packets_count = directions
248 .iter()
249 .filter(|&d| *d == PacketDirection::Backward)
250 .count();
251
252 let payloads: Vec<Vec<u8>> = packets.into_iter().map(PacketInfoTrait::payload).collect();
253
254 FlowStats {
255 timestamp,
256 duration,
257 protocol: flow_id.protocol,
258 src_ip: flow_id.src_ip,
259 dst_ip: flow_id.dst_ip,
260 src_port: flow_id.src_port,
261 dst_port: flow_id.dst_port,
262 ttl_client,
263 ttl_server,
264 fwd_packets_count,
265 bwd_packets_count,
266 fwd_bytes,
267 bwd_bytes,
268 payloads,
269 directions,
270 flags: vec![],
271 iat,
272 }
273 }
274
275 fn new_from_tcp(flow_id: FlowId, packets: Vec<TCPPacketInfo>) -> Self {
276 let flags = packets.iter().map(|p| p.flags).collect();
278 let mut stats = Self::process_packets(flow_id, packets);
279 stats.flags = flags;
280 stats
281 }
282
283 fn new_from_udp(flow_id: FlowId, packets: Vec<UDPPacketInfo>) -> Self {
284 Self::process_packets(flow_id, packets)
285 }
286
287 fn new_from_icmp(flow_id: FlowId, packets: Vec<ICMPPacketInfo>) -> Self {
288 Self::process_packets(flow_id, packets)
289 }
290}
291
292fn flow_id_from_packet(data: &[u8]) -> Option<FlowId> {
293 let eth_packet = ethernet::EthernetPacket::new(data).unwrap();
294 let ip_packet = ipv4::Ipv4Packet::new(eth_packet.payload()).unwrap();
295
296 let (protocol, src_port, dst_port) = match ip_packet.get_next_level_protocol() {
297 IpNextHeaderProtocols::Tcp => {
298 let tcp_packet = tcp::TcpPacket::new(ip_packet.payload())?;
299 (
300 L4Proto::TCP,
301 tcp_packet.get_source(),
302 tcp_packet.get_destination(),
303 )
304 }
305 IpNextHeaderProtocols::Udp => {
306 let udp_packet = udp::UdpPacket::new(ip_packet.payload()).unwrap();
307 (
308 L4Proto::UDP,
309 udp_packet.get_source(),
310 udp_packet.get_destination(),
311 )
312 }
313 IpNextHeaderProtocols::Icmp => (L4Proto::ICMP, 0, 0),
314
315 _ => {
316 return None;
318 }
319 };
320
321 Some(FlowId {
322 protocol,
323 src_ip: ip_packet.get_source(),
324 dst_ip: ip_packet.get_destination(),
325 src_port,
326 dst_port,
327 })
328}
329
330pub fn export_stats(file: &str, stats: Vec<FlowStats>, include_payloads: bool) {
332 let file = File::create(file).expect("Cannot open file");
333 let mut output = BufWriter::new(file);
334 let header = if include_payloads {
335 "timestamp,duration,protocol,src_ip,dst_ip,src_port,dst_port,ttl_client,ttl_server,fwd_packets_count,bwd_packets_count,fwd_bytes,bwd_bytes,payloads,directions,flags,iat"
336 } else {
337 "timestamp,duration,protocol,src_ip,dst_ip,src_port,dst_port,ttl_client,ttl_server,fwd_packets_count,bwd_packets_count,fwd_bytes,bwd_bytes,directions,flags,iat"
338 };
339 writeln!(output, "{header}").expect("Error during CSV writing");
340 for f in stats {
341 let iat: Vec<u128> = f.iat.iter().map(Duration::as_millis).collect();
342 if include_payloads {
343 writeln!(
344 output,
345 "{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {:?}, {:?}, {:?}, {:?}",
346 f.timestamp.as_millis(),
347 f.duration.as_millis(),
348 f.protocol,
349 f.src_ip,
350 f.dst_ip,
351 f.src_port,
352 f.dst_port,
353 f.ttl_client,
354 f.ttl_server,
355 f.fwd_packets_count,
356 f.bwd_packets_count,
357 f.fwd_bytes,
358 f.bwd_bytes,
359 f.payloads,
360 f.directions,
361 f.flags,
362 iat
363 )
364 .expect("Error during CSV writing");
365 } else {
366 writeln!(
367 output,
368 "{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {:?}, {:?}, {:?}",
369 f.timestamp.as_millis(),
370 f.duration.as_millis(),
371 f.protocol,
372 f.src_ip,
373 f.dst_ip,
374 f.src_port,
375 f.dst_port,
376 f.ttl_client,
377 f.ttl_server,
378 f.fwd_packets_count,
379 f.bwd_packets_count,
380 f.fwd_bytes,
381 f.bwd_bytes,
382 f.directions,
383 f.flags,
384 iat
385 )
386 .expect("Error during CSV writing");
387 }
388 }
389}
390
391pub fn process_file(file: &str) -> Vec<FlowStats> {
393 let file_in = BufReader::new(File::open(file).expect("Error opening file"));
394 let mut pcap_reader = pcap::PcapReader::new(file_in).unwrap();
395 let mut tcp_ongoing_flows: HashMap<FlowId, Vec<TCPPacketInfo>> = HashMap::new();
396 let mut udp_ongoing_flows: HashMap<FlowId, Vec<UDPPacketInfo>> = HashMap::new();
397 let mut icmp_ongoing_flows: HashMap<FlowId, Vec<ICMPPacketInfo>> = HashMap::new();
398 let mut finished_flows: Vec<FlowStats> = vec![];
399
400 while let Some(packet) = pcap_reader.next_packet() {
401 if let Ok(packet) = packet
402 && let Some(mut flow_id) = flow_id_from_packet(&packet.data)
403 {
404 flow_id.normalize();
405
406 let packet_info: PacketInfo = packet.into();
407
408 match packet_info {
409 PacketInfo::TCP(packet) => {
410 let mut flow = tcp_ongoing_flows.entry(flow_id).or_default();
411 if let Some(last_packet) = flow.last() {
412 if last_packet.ts + DURATION_THRESHOLD < packet.ts {
414 finished_flows.push(FlowStats::new_from_tcp(
416 flow_id,
417 tcp_ongoing_flows.remove(&flow_id).unwrap(),
418 ));
419 flow = tcp_ongoing_flows.entry(flow_id).or_default();
420 }
421 }
422 flow.push(packet); }
424 PacketInfo::UDP(packet) => {
425 let mut flow = udp_ongoing_flows.entry(flow_id).or_default();
426 if let Some(last_packet) = flow.last() {
427 if last_packet.ts + DURATION_THRESHOLD < packet.ts {
429 finished_flows.push(FlowStats::new_from_udp(
430 flow_id,
431 udp_ongoing_flows.remove(&flow_id).unwrap(),
432 ));
433 flow = udp_ongoing_flows.entry(flow_id).or_default();
434 }
435 }
436 flow.push(packet);
437 }
438 PacketInfo::ICMP(packet) => {
439 let mut flow = icmp_ongoing_flows.entry(flow_id).or_default();
440 if let Some(last_packet) = flow.last() {
441 if last_packet.ts + DURATION_THRESHOLD < packet.ts {
443 finished_flows.push(FlowStats::new_from_icmp(
444 flow_id,
445 icmp_ongoing_flows.remove(&flow_id).unwrap(),
446 ));
447 flow = icmp_ongoing_flows.entry(flow_id).or_default();
448 }
449 }
450 flow.push(packet);
451 }
452 }
453 }
454 }
455 for (k, v) in tcp_ongoing_flows.drain() {
457 finished_flows.push(FlowStats::new_from_tcp(k, v));
458 }
459 for (k, v) in udp_ongoing_flows.drain() {
460 finished_flows.push(FlowStats::new_from_udp(k, v));
461 }
462 for (k, v) in icmp_ongoing_flows.drain() {
463 finished_flows.push(FlowStats::new_from_icmp(k, v));
464 }
465
466 finished_flows
467}
468
469pub fn split_untaint(input: &str) {
471 let path = Path::new(input);
472 let mut s_original = String::from(path.file_stem().unwrap().to_str().unwrap());
473 let mut s_fosr = s_original.clone();
474 s_original.push_str("-original.pcap");
475 s_fosr.push_str("-fosr.pcap");
476
477 let file_out_original = OpenOptions::new()
478 .write(true)
479 .create(true)
480 .truncate(true)
481 .open(path.with_file_name(s_original))
482 .expect("Error opening or creating file");
483 let file_out_fosr = OpenOptions::new()
484 .write(true)
485 .create(true)
486 .truncate(true)
487 .open(path.with_file_name(s_fosr))
488 .expect("Error opening or creating file");
489
490 let mut pcap_writer_original =
491 pcap::PcapWriter::new(BufWriter::new(file_out_original)).expect("Error writing file");
492 let mut pcap_writer_fosr =
493 pcap::PcapWriter::new(BufWriter::new(file_out_fosr)).expect("Error writing file");
494
495 let mut count = 0;
496 {
497 let file_in = BufReader::new(File::open(input).expect("Error opening file"));
499 let mut pcap_reader = pcap::PcapReader::new(file_in).unwrap();
500 while pcap_reader.next_packet().is_some() {
501 count += 1;
502 }
503 }
504 let file_in = BufReader::new(File::open(input).expect("Error opening file"));
505 let mut pcap_reader = pcap::PcapReader::new(file_in).unwrap();
506
507 let pb = ProgressBar::new(count);
509 pb.set_style(
510 ProgressStyle::with_template("{spinner:.green} Untainting [{wide_bar}] ({eta})").unwrap(),
511 );
512
513 while let Some(packet) = pcap_reader.next_packet() {
514 let mut packet = packet.expect("Error during packet parsing");
515 let data = packet.data.to_mut();
517 let ip_start = ethernet::MutableEthernetPacket::minimum_packet_size();
519 let mut ipv4_packet = ipv4::MutableIpv4Packet::new(&mut data[ip_start..]).unwrap();
520 let ip_flags = ipv4_packet.get_flags();
521 if ipv4_packet.get_flags() & 0b100 > 0 {
522 ipv4_packet.set_flags(ip_flags & 0b011);
523 ipv4_packet.set_checksum(ipv4::checksum(&ipv4_packet.to_immutable()));
524 pcap_writer_fosr.write_packet(&packet).unwrap();
525 } else {
526 pcap_writer_original.write_packet(&packet).unwrap();
527 }
528 pb.inc(1);
529 }
530 pb.finish();
531}
532
533pub fn untaint_file(input: &str, output: &str) {
535 let file_out = OpenOptions::new()
536 .write(true)
537 .create(true)
538 .truncate(true)
539 .open(output)
540 .expect("Error opening or creating file");
541 let mut pcap_writer =
542 pcap::PcapWriter::new(BufWriter::new(file_out)).expect("Error writing file");
543
544 let mut count = 0;
545 {
546 let file_in = BufReader::new(File::open(input).expect("Error opening file"));
548 let mut pcap_reader = pcap::PcapReader::new(file_in).unwrap();
549 while pcap_reader.next_packet().is_some() {
550 count += 1;
551 }
552 }
553 let file_in = BufReader::new(File::open(input).expect("Error opening file"));
554 let mut pcap_reader = pcap::PcapReader::new(file_in).unwrap();
555
556 let pb = ProgressBar::new(count);
558 pb.set_style(
559 ProgressStyle::with_template("{spinner:.green} Untainting [{wide_bar}] ({eta})").unwrap(),
560 );
561
562 while let Some(packet) = pcap_reader.next_packet() {
563 let mut packet = packet.expect("Error during packet parsing");
564 let data = packet.data.to_mut();
566 let ip_start = ethernet::MutableEthernetPacket::minimum_packet_size();
568 let mut ipv4_packet = ipv4::MutableIpv4Packet::new(&mut data[ip_start..]).unwrap();
569 let ip_flags = ipv4_packet.get_flags();
570 ipv4_packet.set_flags(ip_flags & 0b011);
571 ipv4_packet.set_checksum(ipv4::checksum(&ipv4_packet.to_immutable()));
572 pb.inc(1);
573 pcap_writer.write_packet(&packet).unwrap();
574 }
575 pb.finish();
576}
577
578pub fn is_global(addr: &Ipv4Addr) -> bool {
580 addr.octets()[0] != 0
581 && !addr.is_multicast()
582 && !addr.is_broadcast()
583 && !addr.is_documentation()
584 && !addr.is_link_local()
585 && !addr.is_loopback()
586 && !addr.is_private()
587}
588
589pub fn sample_random_global_ip(rng: &mut impl Rng) -> Ipv4Addr {
591 let mut addr = Ipv4Addr::from_bits(rng.next_u32());
592 while !is_global(&addr) {
594 addr = Ipv4Addr::from_bits(rng.next_u32());
596 }
597 addr
598}