1use serde::Serialize;
2use std::{
3 fmt::{Display, Formatter, Result},
4 net::IpAddr,
5};
6
7use crate::parse::data_link::vlan_tag::VlanTag;
8use crate::{IpType, PacketFlow};
9
10#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq)]
11pub struct PacketFlowOwned {
12 #[serde(flatten)]
13 pub data_link: DataLinkOwned,
14 #[serde(flatten)]
15 pub internet: Option<InternetOwned>,
16 #[serde(flatten)]
17 pub transport: Option<TransportOwned>,
18 #[serde(flatten)]
19 pub application: Option<ApplicationOwned>,
20}
21
22#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq)]
23pub struct DataLinkOwned {
24 pub destination_mac: String,
25 pub source_mac: String,
27 pub ethertype: String,
29 pub vlan: Option<VlanTag>,
30}
31
32impl Display for DataLinkOwned {
33 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
34 write!(
35 f,
36 "\n Destination MAC: {},\n Source MAC: {},\n Ethertype: {},\n VLAN: ",
37 self.destination_mac, self.source_mac, self.ethertype,
38 )?;
39
40 match &self.vlan {
41 Some(vlan) => write!(f, "{vlan}")?,
42 None => write!(f, "None")?,
43 }
44
45 writeln!(f)
46 }
47}
48
49impl Display for InternetOwned {
50 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
51 write!(f, "\n Source IP: ")?;
53 match &self.source_ip {
54 Some(ip) => write!(f, "{ip}")?,
55 None => write!(f, "None")?,
56 }
57
58 write!(f, ",\n Destination IP: ")?;
59 match &self.destination_ip {
60 Some(ip) => write!(f, "{ip}")?,
61 None => write!(f, "None")?,
62 }
63
64 write!(f, ",\n Protocol: {}\n", self.protocol)
65 }
66}
67
68impl Display for TransportOwned {
69 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
70 write!(f, "\n Source Port: ")?;
71 match self.source_port {
72 Some(p) => write!(f, "{p}")?,
73 None => write!(f, "None")?,
74 }
75
76 write!(f, ",\n Destination Port: ")?;
77 match self.destination_port {
78 Some(p) => write!(f, "{p}")?,
79 None => write!(f, "None")?,
80 }
81
82 write!(f, ",\n Protocol: {}\n", self.protocol)
83 }
84}
85
86impl Display for ApplicationOwned {
87 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
88 write!(f, "\n Protocol: {}\n", self.protocol)
89 }
90}
91
92#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq)]
93pub struct InternetOwned {
94 pub source_ip: Option<IpAddr>,
95 pub ip_source_type: Option<IpType>,
96 pub destination_ip: Option<IpAddr>,
97 pub ip_destination_type: Option<IpType>,
98 pub protocol: String,
99}
100
101#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq)]
102pub struct TransportOwned {
103 pub source_port: Option<u16>,
104 pub destination_port: Option<u16>,
105 pub protocol: String,
106}
107
108#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq)]
109pub struct ApplicationOwned {
110 pub protocol: String,
111}
112
113impl<'a> From<PacketFlow<'a>> for PacketFlowOwned {
114 fn from(flow: PacketFlow<'a>) -> Self {
115 Self {
116 data_link: DataLinkOwned {
117 destination_mac: flow.data_link.destination_mac,
118 source_mac: flow.data_link.source_mac,
119 ethertype: flow.data_link.ethertype,
120 vlan: flow.data_link.vlan,
121 },
122 internet: flow.internet.map(|internet| InternetOwned {
123 source_ip: internet.source,
124 ip_source_type: internet.source_type,
125 destination_ip: internet.destination,
126 ip_destination_type: internet.destination_type,
127 protocol: internet.protocol_name,
128 }),
129 transport: flow.transport.map(|transport| TransportOwned {
130 source_port: transport.source_port,
131 destination_port: transport.destination_port,
132 protocol: transport.protocol.to_string(),
133 }),
134 application: flow.application.map(|application| ApplicationOwned {
135 protocol: application.application_protocol.to_string(),
136 }),
137 }
138 }
139}
140
141impl Display for PacketFlowOwned {
142 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
143 writeln!(f, "Packet Flow:")?;
144 writeln!(f, " Data Link: {}", self.data_link)?;
145
146 if let Some(internet) = &self.internet {
147 writeln!(f, " Internet: {internet}")?;
148 }
149 if let Some(transport) = &self.transport {
150 writeln!(f, " Transport: {transport}")?;
151 }
152 if let Some(application) = &self.application {
153 writeln!(f, " Application: {application}")?;
154 }
155
156 Ok(())
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use serde_json;
164 use std::collections::hash_map::DefaultHasher;
165 use std::hash::{Hash, Hasher};
166 use std::net::{IpAddr, Ipv4Addr};
167
168 fn sample_data_link_without_vlan() -> DataLinkOwned {
169 DataLinkOwned {
170 destination_mac: "AA:BB:CC:DD:EE:FF".to_string(),
171 source_mac: "11:22:33:44:55:66".to_string(),
172 ethertype: "IPv4".to_string(),
173 vlan: None,
174 }
175 }
176
177 fn sample_internet() -> InternetOwned {
178 InternetOwned {
179 source_ip: Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10))),
180 ip_source_type: None,
181 destination_ip: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))),
182 ip_destination_type: None,
183 protocol: "TCP".to_string(),
184 }
185 }
186
187 fn sample_transport() -> TransportOwned {
188 TransportOwned {
189 source_port: Some(12345),
190 destination_port: Some(80),
191 protocol: "TCP".to_string(),
192 }
193 }
194
195 fn sample_application() -> ApplicationOwned {
196 ApplicationOwned {
197 protocol: "HTTP".to_string(),
198 }
199 }
200
201 fn sample_packet_flow_owned() -> PacketFlowOwned {
202 PacketFlowOwned {
203 data_link: sample_data_link_without_vlan(),
204 internet: Some(sample_internet()),
205 transport: Some(sample_transport()),
206 application: Some(sample_application()),
207 }
208 }
209
210 fn hash_of<T: Hash>(value: &T) -> u64 {
211 let mut hasher = DefaultHasher::new();
212 value.hash(&mut hasher);
213 hasher.finish()
214 }
215
216 #[test]
217 fn test_data_link_owned_display_without_vlan() {
218 let data_link = sample_data_link_without_vlan();
219
220 let expected = "\n Destination MAC: AA:BB:CC:DD:EE:FF,\n Source MAC: 11:22:33:44:55:66,\n Ethertype: IPv4,\n VLAN: None\n";
221 assert_eq!(data_link.to_string(), expected);
222 }
223
224 #[test]
225 fn test_internet_owned_display_with_ips() {
226 let internet = sample_internet();
227
228 let expected =
229 "\n Source IP: 192.168.1.10,\n Destination IP: 8.8.8.8,\n Protocol: TCP\n";
230 assert_eq!(internet.to_string(), expected);
231 }
232
233 #[test]
234 fn test_internet_owned_display_with_none_ips() {
235 let internet = InternetOwned {
236 source_ip: None,
237 ip_source_type: None,
238 destination_ip: None,
239 ip_destination_type: None,
240 protocol: "UDP".to_string(),
241 };
242
243 let expected = "\n Source IP: None,\n Destination IP: None,\n Protocol: UDP\n";
244 assert_eq!(internet.to_string(), expected);
245 }
246
247 #[test]
248 fn test_transport_owned_display_with_ports() {
249 let transport = sample_transport();
250
251 let expected = "\n Source Port: 12345,\n Destination Port: 80,\n Protocol: TCP\n";
252 assert_eq!(transport.to_string(), expected);
253 }
254
255 #[test]
256 fn test_transport_owned_display_with_none_ports() {
257 let transport = TransportOwned {
258 source_port: None,
259 destination_port: None,
260 protocol: "ICMP".to_string(),
261 };
262
263 let expected =
264 "\n Source Port: None,\n Destination Port: None,\n Protocol: ICMP\n";
265 assert_eq!(transport.to_string(), expected);
266 }
267
268 #[test]
269 fn test_application_owned_display() {
270 let application = sample_application();
271
272 let expected = "\n Protocol: HTTP\n";
273 assert_eq!(application.to_string(), expected);
274 }
275
276 #[test]
277 fn test_packet_flow_owned_display_full() {
278 let flow = sample_packet_flow_owned();
279
280 let expected = concat!(
281 "Packet Flow:\n",
282 " Data Link: \n",
283 " Destination MAC: AA:BB:CC:DD:EE:FF,\n",
284 " Source MAC: 11:22:33:44:55:66,\n",
285 " Ethertype: IPv4,\n",
286 " VLAN: None\n",
287 "\n",
288 " Internet: \n",
289 " Source IP: 192.168.1.10,\n",
290 " Destination IP: 8.8.8.8,\n",
291 " Protocol: TCP\n",
292 "\n",
293 " Transport: \n",
294 " Source Port: 12345,\n",
295 " Destination Port: 80,\n",
296 " Protocol: TCP\n",
297 "\n",
298 " Application: \n",
299 " Protocol: HTTP\n",
300 "\n"
301 );
302
303 assert_eq!(flow.to_string(), expected);
304 }
305
306 #[test]
307 fn test_packet_flow_owned_display_only_data_link() {
308 let flow = PacketFlowOwned {
309 data_link: sample_data_link_without_vlan(),
310 internet: None,
311 transport: None,
312 application: None,
313 };
314
315 let expected = concat!(
316 "Packet Flow:\n",
317 " Data Link: \n",
318 " Destination MAC: AA:BB:CC:DD:EE:FF,\n",
319 " Source MAC: 11:22:33:44:55:66,\n",
320 " Ethertype: IPv4,\n",
321 " VLAN: None\n",
322 "\n"
323 );
324
325 assert_eq!(flow.to_string(), expected);
326 }
327
328 #[test]
329 fn test_packet_flow_owned_clone_and_eq() {
330 let flow = sample_packet_flow_owned();
331 let cloned = flow.clone();
332
333 assert_eq!(flow, cloned);
334 }
335
336 #[test]
337 fn test_packet_flow_owned_hash_stable_for_equal_values() {
338 let flow1 = sample_packet_flow_owned();
339 let flow2 = sample_packet_flow_owned();
340
341 assert_eq!(flow1, flow2);
342 assert_eq!(hash_of(&flow1), hash_of(&flow2));
343 }
344
345 #[test]
346 fn test_data_link_owned_hash_stable_for_equal_values() {
347 let dl1 = sample_data_link_without_vlan();
348 let dl2 = sample_data_link_without_vlan();
349
350 assert_eq!(dl1, dl2);
351 assert_eq!(hash_of(&dl1), hash_of(&dl2));
352 }
353
354 #[test]
355 fn test_packet_flow_owned_serialize() {
356 let flow = sample_packet_flow_owned();
357 let json = serde_json::to_string(&flow).unwrap();
358
359 assert!(json.contains("\"destination_mac\":\"AA:BB:CC:DD:EE:FF\""));
360 assert!(json.contains("\"source_mac\":\"11:22:33:44:55:66\""));
361 assert!(json.contains("\"ethertype\":\"IPv4\""));
362 assert!(json.contains("\"source_ip\":\"192.168.1.10\""));
363 assert!(json.contains("\"destination_ip\":\"8.8.8.8\""));
364 assert!(json.contains("\"source_port\":12345"));
365 assert!(json.contains("\"destination_port\":80"));
366 assert!(json.contains("\"protocol\":\"HTTP\"") || json.contains("\"protocol\":\"TCP\""));
367 }
368
369 #[test]
370 fn test_internet_owned_serialize_none_fields() {
371 let internet = InternetOwned {
372 source_ip: None,
373 ip_source_type: None,
374 destination_ip: None,
375 ip_destination_type: None,
376 protocol: "UDP".to_string(),
377 };
378
379 let json = serde_json::to_string(&internet).unwrap();
380
381 assert!(json.contains("\"source_ip\":null"));
382 assert!(json.contains("\"destination_ip\":null"));
383 assert!(json.contains("\"protocol\":\"UDP\""));
384 }
385}