net_parser_rs/flow/
mod.rs

1pub mod device;
2pub mod errors;
3pub mod info;
4pub mod layer2;
5pub mod layer3;
6pub mod layer4;
7
8use crate::common::Vlan;
9use crate::layer2::ethernet::Ethernet;
10use crate::PcapRecord;
11
12use device::Device;
13use errors::Error;
14use layer2::{FlowExtraction as Layer2Extraction};
15use log::*;
16
17///
18/// Trait that provides necessary information to indicate a flow
19///
20pub trait FlowExtraction {
21    fn payload(&self) -> &[u8];
22
23    fn extract_flow(&self) -> Result<Flow, Error> {
24        trace!("Creating stream from payload of {}B", self.payload().len());
25
26        let payload_ref = self.payload();
27
28        Ethernet::parse(payload_ref)
29            .map_err(|e| {
30                error!("Error parsing ethernet {:?}", e);
31                Error::NetParser(e)
32            })
33            .and_then(|r| {
34                let (rem, l2) = r;
35                if rem.is_empty() {
36                    l2.extract_flow()
37                } else {
38                    Err(Error::Incomplete { size: rem.len() })
39                }
40            })
41    }
42}
43
44impl<'a> FlowExtraction for PcapRecord<'a> {
45    fn payload(&self) -> &[u8] {
46        self.payload
47    }
48}
49
50///
51/// Flow that was built from a record moved
52///
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
54pub struct Flow {
55    pub source: Device,
56    pub destination: Device,
57    pub layer2: info::layer2::Id,
58    pub layer3: info::layer3::Id,
59    pub layer4: info::layer4::Id,
60    pub vlan: Vlan,
61}
62
63impl Flow {
64    pub fn new(
65        l2: info::layer2::Info,
66        l3: info::layer3::Info,
67        l4: info::layer4::Info
68    ) -> Flow {
69        Flow {
70            source: Device {
71                mac: l2.src_mac,
72                ip: l3.src_ip,
73                port: l4.src_port
74            },
75            destination: Device {
76                mac: l2.dst_mac,
77                ip: l3.dst_ip,
78                port: l4.dst_port
79            },
80            layer2: l2.id,
81            layer3: l3.id,
82            layer4: l4.id,
83            vlan: l2.vlan
84        }
85    }
86}
87
88impl std::fmt::Display for Flow {
89    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90        write!(
91            f,
92            "Source=[{}]   Destination=[{}]   Vlan={}",
93            self.source, self.destination, self.vlan
94        )
95    }
96}
97
98///
99/// Utility function to convert a vector of records to flows, unless an error is encountered in stream conversion
100///
101pub fn convert_records<'b>(
102    records: Vec<PcapRecord<'b>>,
103) -> Vec<(PcapRecord<'b>, Flow)> {
104    let mut records = records;
105    let mut results = vec![];
106
107    loop {
108        if let Some(r) = records.pop() {
109            match r.extract_flow() {
110                Ok(f) => {
111                    results.push( (r, f) );
112                }
113                Err(e) => {
114                    debug!("Failed to extract stream: {}", e);
115                }
116            }
117        } else {
118            break;
119        }
120    }
121
122    results
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::common::MacAddress;
128    use crate::flow::info::layer2::{Id as L2Id};
129    use crate::flow::info::layer3::{Id as L3Id};
130    use crate::flow::info::layer4::{Id as L4Id};
131    use super::*;
132
133    use std::io::Read;
134    use std::path::PathBuf;
135
136    #[test]
137    fn format_flow() {
138        let flow = Flow {
139            layer2: L2Id::Ethernet,
140            layer3: L3Id::IPv4,
141            layer4: L4Id::Tcp,
142            source: Device {
143                ip: std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 1, 2, 3)),
144                mac: MacAddress([0u8, 1u8, 2u8, 3u8, 4u8, 5u8]),
145                port: 80,
146            },
147            destination: Device {
148                ip: std::net::IpAddr::V4(std::net::Ipv4Addr::new(100, 99, 98, 97)),
149                mac: MacAddress([11u8, 10u8, 9u8, 8u8, 7u8, 6u8]),
150                port: 52436,
151            },
152            vlan: 0,
153        };
154
155        assert_eq!(format!("{}", flow), "Source=[Mac=00:01:02:03:04:05   Ip=0.1.2.3   Port=80]   Destination=[Mac=0b:0a:09:08:07:06   Ip=100.99.98.97   Port=52436]   Vlan=0")
156    }
157
158    #[test]
159    fn file_convert() {
160        let _ = env_logger::try_init();
161
162        let pcap_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
163            .join("resources")
164            .join("4SICS-GeekLounge-151020.pcap");
165
166        let pcap_reader = std::fs::File::open(pcap_path.clone())
167            .expect(&format!("Failed to open pcap path {:?}", pcap_path));
168
169        let bytes = pcap_reader
170            .bytes()
171            .map(|b| b.unwrap())
172            .collect::<std::vec::Vec<u8>>();
173
174        let (_, f) =
175            crate::CaptureFile::parse(&bytes).expect("Failed to parse");
176
177        assert_eq!(f.global_header.endianness, nom::Endianness::Little);
178        assert_eq!(f.records.len(), 246137);
179
180        let converted_records = convert_records(f.records.into_inner());
181
182        assert_eq!(converted_records.len(), 236527);
183    }
184}