1use cfg_if::cfg_if;
2use colored::*;
3use pcap::Packet;
4use std::collections::HashMap;
5use std::default::Default;
6use std::fmt::Write;
7use std::net::IpAddr;
8use std::result::Result;
9
10use crate::proto::*;
11
12#[cfg(feature = "resolve")]
13use dns_lookup::lookup_addr;
14
15pub type Resolver = HashMap<IpAddr, String>;
16
17#[derive(Eq, PartialEq, Default)]
18pub struct PacketSummary<'a> {
19 pub l2_src: Option<u128>,
20 pub l2_dst: Option<u128>,
21 pub ethertype: Option<Ethertype>,
22 pub vlan_id: Option<u16>,
23 pub l3_src: Option<u128>,
24 pub l3_dst: Option<u128>,
25 pub next_proto: Option<NextProto>,
26 pub l4_sport: Option<u16>,
27 pub l4_dport: Option<u16>,
28 pub resolver: Option<&'a mut HashMap<IpAddr, String>>,
29}
30
31#[allow(clippy::upper_case_acronyms)]
32#[derive(Eq, PartialEq, Debug)]
33pub enum ProtoHandler {
34 COMPLETE, UNKNOWN, ETH,
37 VLAN,
38 IPV4,
39 IPV6,
40 UDP,
41 TCP,
42 DCCP,
43 SCTP,
44}
45
46pub fn handle_protocol(
47 pkt: &Packet,
48 offset: usize,
49 pktsum: &mut PacketSummary,
50 proto: &ProtoHandler,
51) -> Result<(usize, ProtoHandler), String> {
52 let handler = match proto {
53 ProtoHandler::ETH => handle_eth,
54 ProtoHandler::VLAN => handle_vlan,
55 ProtoHandler::IPV4 => handle_ipv4,
56 ProtoHandler::IPV6 => handle_ipv6,
57 ProtoHandler::UDP => handle_ports,
58 ProtoHandler::TCP => handle_ports,
59 ProtoHandler::SCTP => handle_ports,
60 ProtoHandler::DCCP => handle_ports,
61 _ => handle_unknown,
62 };
63
64 handler(pkt, offset, pktsum)
65}
66
67pub fn get_field(data: &[u8], offset: usize, bytelen: usize) -> Result<u128, &str> {
68 assert!(bytelen <= 16, "Length must be less than 16 bytes");
69 if (data.len() - offset) < bytelen {
70 return Err("Data after offset is shorter than bytelen");
71 }
72 let mut addr: u128 = 0;
73 for i in 0..(bytelen) {
74 addr |= (data[offset + i] as u128) << (((bytelen - 1) * 8) - (i * 8))
75 }
76 Ok(addr)
77}
78
79pub fn int_to_mac_str(addr: &u64, formatted: &mut String) {
80 let bytes = addr.to_be_bytes();
81 write!(formatted, "{:02x}", bytes[2]).unwrap();
82 for byte in bytes[3..].iter() {
83 write!(formatted, ":{:02x}", byte).unwrap();
84 }
85}
86
87pub fn int_to_ipv6_str(addr: &u128, formatted: &mut String) {
88 let bytes = addr.to_be_bytes();
89 write!(formatted, "{:02x}{:02x}", bytes[0], bytes[1]).unwrap();
90 for pair in bytes
91 .iter()
92 .skip(2)
93 .step_by(2)
94 .zip(bytes.iter().skip(3).step_by(2))
95 {
96 write!(formatted, ":{:02x}{:02x}", pair.0, pair.1).unwrap();
97 }
98}
99
100pub fn int_to_ipv4_str(addr: &u32, formatted: &mut String) {
101 let bytes = addr.to_be_bytes();
102 write!(formatted, "{}", bytes[0]).unwrap();
103 for byte in bytes.iter().skip(1) {
104 write!(formatted, ".{}", byte).unwrap();
105 }
106}
107
108pub fn get_ethertype_handler(ethertype: &Option<Ethertype>) -> ProtoHandler {
109 match ethertype {
110 Some(VLAN) => ProtoHandler::VLAN,
111 Some(IPV4) => ProtoHandler::IPV4,
112 Some(IPV6) => ProtoHandler::IPV6,
113 _ => ProtoHandler::UNKNOWN,
114 }
115}
116
117pub fn get_nextproto_handler(next_proto: &Option<NextProto>) -> ProtoHandler {
118 match next_proto {
119 Some(TCP) => ProtoHandler::TCP,
120 Some(UDP) => ProtoHandler::UDP,
121 Some(DCCP) => ProtoHandler::DCCP,
122 Some(SCTP) => ProtoHandler::SCTP,
123 _ => ProtoHandler::UNKNOWN,
124 }
125}
126
127pub fn handle_eth(
128 pkt: &Packet,
129 offset: usize,
130 pktsum: &mut PacketSummary,
131) -> Result<(usize, ProtoHandler), String> {
132 pktsum.l2_dst = get_field(pkt, offset, 6).ok();
133 pktsum.l2_src = get_field(pkt, offset + 6, 6).ok();
134 pktsum.ethertype = get_field(pkt.data, offset + 12, 2).map(|x| x as u16).ok();
135
136 let next_proto_hdl = get_ethertype_handler(&pktsum.ethertype);
137
138 Ok((offset + 14, next_proto_hdl))
139}
140
141pub fn handle_vlan(
142 pkt: &Packet,
143 offset: usize,
144 pktsum: &mut PacketSummary,
145) -> Result<(usize, ProtoHandler), String> {
146 pktsum.vlan_id = get_field(pkt.data, offset, 2)
147 .map(|x| x as u16 & 0xfff)
148 .ok();
149 pktsum.ethertype = get_field(pkt.data, offset + 2, 2).map(|x| x as u16).ok();
150
151 let next_proto_hdl = get_ethertype_handler(&pktsum.ethertype);
152
153 Ok((offset + 4, next_proto_hdl))
154}
155
156pub fn handle_ipv4(
157 pkt: &Packet,
158 offset: usize,
159 pktsum: &mut PacketSummary,
160) -> Result<(usize, ProtoHandler), String> {
161 let ihl = ((pkt.data[offset] & 0xf) * 4) as usize;
162
163 pktsum.next_proto = Some(pkt.data[offset + 9]);
164 pktsum.l3_src = get_field(pkt, offset + 12, 4).ok();
165 pktsum.l3_dst = get_field(pkt, offset + 16, 4).ok();
166
167 let next_proto_hdl = get_nextproto_handler(&pktsum.next_proto);
168
169 Ok((offset + ihl, next_proto_hdl))
170}
171
172pub fn handle_ipv6(
173 pkt: &Packet,
174 offset: usize,
175 pktsum: &mut PacketSummary,
176) -> Result<(usize, ProtoHandler), String> {
177 let mut next_offset = offset + 40;
178 let mut next_proto = pkt.data[offset + 6];
179
180 pktsum.l3_src = get_field(pkt, offset + 8, 16).ok();
181 pktsum.l3_dst = get_field(pkt, offset + 24, 16).ok();
182
183 let mut bos = false;
185 while !bos {
186 match next_proto {
187 HOPOPT | IPV6_ROUTE | IPV6_OPTS => {
188 next_proto = pkt[next_offset];
189 next_offset += 8 + (pkt[next_offset + 1] * 8) as usize;
190 }
191 IPV6_FRAG => {
192 let frag = get_field(pkt, next_offset + 2, 2)
193 .map(|x| x as u16 & 0xff8)
194 .unwrap();
195 next_proto = pkt[next_offset];
196 next_offset += 8;
197
198 if frag != 0 {
202 pktsum.next_proto = Some(next_proto);
204 return Ok((next_offset, ProtoHandler::COMPLETE));
205 }
206 bos = true; }
208 AH => {
209 pktsum.next_proto = Some(next_proto);
211 return Ok((next_offset, ProtoHandler::COMPLETE));
212 }
213 IPV6_NONXT => {
214 pktsum.next_proto = Some(next_proto);
216 return Ok((next_offset, ProtoHandler::COMPLETE));
217 }
218 _ => bos = true,
219 };
220 }
221
222 pktsum.next_proto = Some(next_proto);
223
224 let next_proto_hdl = get_nextproto_handler(&pktsum.next_proto);
225
226 Ok((next_offset, next_proto_hdl))
227}
228
229pub fn handle_ports(
230 pkt: &Packet,
231 offset: usize,
232 pktsum: &mut PacketSummary,
233) -> Result<(usize, ProtoHandler), String> {
234 let sport_offset = offset;
235 let dport_offset = offset + 2;
236 pktsum.l4_sport = get_field(pkt.data, sport_offset, 2).ok().map(|x| x as u16);
237 pktsum.l4_dport = get_field(pkt.data, dport_offset, 2).ok().map(|x| x as u16);
238
239 Ok((offset + 4, ProtoHandler::COMPLETE))
240}
241
242pub fn handle_unknown(
243 _pkt: &Packet,
244 _offset: usize,
245 _pktsum: &mut PacketSummary,
246) -> Result<(usize, ProtoHandler), String> {
247 Err("Unknown protocol".to_string())
248}
249
250impl<'a> PacketSummary<'a> {
251 pub fn new() -> Self {
252 Default::default()
253 }
254
255 pub fn from_packet(pkt: &Packet, resolver: Option<&'a mut HashMap<IpAddr, String>>) -> Self {
256 let mut pktsum = Self::new();
257 pktsum.resolver = resolver;
258
259 let mut offset = 0;
261 let mut proto_handler = ProtoHandler::ETH;
262
263 loop {
266 break match proto_handler {
267 ProtoHandler::UNKNOWN => {} ProtoHandler::COMPLETE => {} _ => {
270 match handle_protocol(pkt, offset, &mut pktsum, &proto_handler) {
271 Ok((o, h)) => {
272 offset = o;
274 proto_handler = h;
275 }
276 Err(_) => return pktsum, }
278 continue; }
280 };
281 }
282 pktsum
283 }
284
285 pub fn formatted(&mut self) -> String {
286 let mut out = String::with_capacity(128);
290
291 out.push('[');
292
293 let mut l3_src = String::with_capacity(39);
295 let mut l3_dst = String::with_capacity(39);
296
297 if let Some(vlan_id) = self.vlan_id {
298 let tag = vlan_id.to_string();
299 out.push_str(format!("Dot1Q: {} | ", tag.yellow()).as_str());
300 }
301
302 let l4_sport = match self.l4_sport {
303 Some(p) => format!(":{}", p),
304 None => String::new(),
305 };
306
307 let l4_dport = match self.l4_dport {
308 Some(p) => format!(":{}", p),
309 None => String::new(),
310 };
311
312 let next_proto = match self.next_proto {
313 Some(np) => np.resolve(),
314 None => "-".to_string(),
315 };
316
317 let is_ip = match self.ethertype {
318 Some(IPV6) => {
319 int_to_ipv6_str(&self.l3_src.unwrap(), &mut l3_src);
320 int_to_ipv6_str(&self.l3_dst.unwrap(), &mut l3_dst);
321 true
322 }
323 Some(IPV4) => {
324 int_to_ipv4_str(&(self.l3_src.unwrap() as u32), &mut l3_src);
325 int_to_ipv4_str(&(self.l3_dst.unwrap() as u32), &mut l3_dst);
326 true
327 }
328 _ => false,
329 };
330
331 if is_ip {
332 cfg_if! {
333 if #[cfg(feature = "resolve")] {
334 if let Some(resolver) = &mut self.resolver {
335 let srcip: IpAddr = l3_src.parse().unwrap();
336 let dstip: IpAddr = l3_dst.parse().unwrap();
337
338 let l3_src_resolved = resolver.entry(srcip).or_insert_with(|| {
339 match lookup_addr(&srcip) {
340 Ok(addr) => addr,
341 Err(_) => l3_src.to_string(),
342 }
343 });
344 out.push_str(
345 format!(
346 "{}{} → ",
347 l3_src_resolved.magenta(),
348 l4_sport.cyan(),
349 ).as_str()
350 );
351
352 let l3_dst_resolved = resolver.entry(dstip).or_insert_with(|| {
353 match lookup_addr(&dstip) {
354 Ok(addr) => addr,
355 Err(_) => l3_dst.to_string(),
356 }
357 });
358 out.push_str(
359 format!(
360 "{}{} ",
361 l3_dst_resolved.magenta(),
362 l4_dport.cyan(),
363 ).as_str()
364 );
365 } else {
366 out.push_str(
367 format!(
368 "{}{} → {}{} ",
369 l3_src.magenta(),
370 l4_sport.cyan(),
371 l3_dst.magenta(),
372 l4_dport.cyan(),
373 ).as_str()
374 );
375 }
376 } else {
377 out.push_str(
378 format!(
379 "{}{} → {}{} ",
380 l3_src.magenta(),
381 l4_sport.cyan(),
382 l3_dst.magenta(),
383 l4_dport.cyan(),
384 ).as_str()
385 );
386 }
387 }
388 out.push_str(format!("({})", next_proto.green()).as_str());
389 } else {
390 let mut l2_src = String::with_capacity(17);
392 let mut l2_dst = String::with_capacity(17);
393
394 int_to_mac_str(&(self.l2_src.unwrap_or(0) as u64), &mut l2_src);
395 int_to_mac_str(&(self.l2_dst.unwrap_or(0) as u64), &mut l2_dst);
396
397 let mut ethertype = "----".to_string();
398 if let Some(et) = self.ethertype {
399 ethertype = et.resolve();
400 }
401
402 out.push_str(
403 format!(
404 "{} → {} ({})",
405 l2_src.magenta(),
406 l2_dst.magenta(),
407 ethertype.green(),
408 )
409 .as_str(),
410 );
411 }
412 out.push(']');
413 out
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use libc::timeval;
420 use pcap::{Packet, PacketHeader};
421
422 use super::*;
423
424 const REF_V4_PACKET: Packet = Packet {
425 header: &PacketHeader {
426 ts: timeval {
427 tv_sec: 0,
428 tv_usec: 0,
429 },
430 caplen: 77,
431 len: 77,
432 },
433 data: &[
434 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x0, 0x5f, 0xff, 0x8, 0x0, 0x45, 0x0, 0x0, 0x3b, 0x0, 0x1, 0x0, 0x0, 0x40, 0x6, 0x3a, 0x12, 0x7f, 0x0, 0x0, 0x1, 0xc0, 0xa8, 0x1, 0x1, 0x0, 0x50, 0x14, 0xeb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0x2, 0x20, 0x0, 0x76, 0x46, 0x0, 0x0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, ],
452 };
453
454 const REF_V6_PACKET: Packet = Packet {
455 header: &PacketHeader {
456 ts: timeval {
457 tv_sec: 0,
458 tv_usec: 0,
459 },
460 caplen: 97,
461 len: 97,
462 },
463 data: &[
464 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x0, 0x5f, 0xff, 0x86, 0xdd, 0x60, 0x0, 0x0, 0x0, 0x0, 0x27, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
473 0x1, 0x73, 0x57, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
475 0x23, 0x3c, 0x01, b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A', b'A',
480 0x2b, 0x01, b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B', b'B',
484 0x2c, 0x01, 0x04, 0x02, b'C', b'C', b'C', b'C', b'C', b'C', b'C', b'C', b'C', b'C', b'C', b'C',
490 0x6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xd2, 0x0, 0x50, 0x14, 0xeb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0x2, 0x20, 0x0, 0x42, 0x76, 0x0, 0x0, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, ],
505 };
506
507 #[test]
508 fn test_get_field() {
509 let data = &[0x01, 0x23, 0x34, 0x0f, 0xff, 0x56];
510 let expected = 4095;
511
512 let result = get_field(data, 3, 2);
513 assert!(result.is_ok());
514 assert_eq!(result.unwrap(), expected);
515 }
516
517 #[test]
518 fn test_int_to_mac_str() {
519 let expected = "00:00:00:00:00:01";
520
521 let mut result = String::new();
522 int_to_mac_str(&1u64, &mut result);
523 assert_eq!(result, expected);
524 }
525
526 #[test]
527 fn test_int_to_ipv6_str() {
528 let expected = "0000:0000:0000:0000:0000:0000:0000:0001";
529
530 let mut result = String::new();
531 int_to_ipv6_str(&1u128, &mut result);
532 assert_eq!(result, expected);
533 }
534
535 #[test]
536 fn test_int_to_ipv4_str() {
537 let expected = "0.0.0.1";
538
539 let mut result = String::new();
540 int_to_ipv4_str(&1u32, &mut result);
541 assert_eq!(result, expected);
542 }
543
544 #[test]
545 fn test_from_packet_v4() {
546 let pktsum = PacketSummary::from_packet(&REF_V4_PACKET, None);
547
548 assert_eq!(pktsum.l2_dst.unwrap(), 1, "l2_dst");
549 assert_eq!(pktsum.l2_src.unwrap(), 281474976710655, "l2_src");
550 assert_eq!(pktsum.ethertype.unwrap(), 2048, "ethertype");
551 assert_eq!(pktsum.vlan_id.unwrap(), 4095, "vlan_id");
552 assert_eq!(pktsum.l3_src.unwrap(), 2130706433, "l3_src");
553 assert_eq!(pktsum.l3_dst.unwrap(), 3232235777, "l3_dst");
554 assert_eq!(pktsum.next_proto.unwrap(), 6, "next_proto");
555 assert_eq!(pktsum.l4_sport.unwrap(), 80, "l4_sport");
556 assert_eq!(pktsum.l4_dport.unwrap(), 5355, "l4_dport");
557 }
558
559 #[test]
560 fn test_from_packet_v6() {
561 let pktsum = PacketSummary::from_packet(&REF_V6_PACKET, None);
562
563 assert_eq!(pktsum.l2_dst.unwrap(), 1, "l2_dst");
564 assert_eq!(pktsum.l2_src.unwrap(), 281474976710655, "l2_src");
565 assert_eq!(pktsum.ethertype.unwrap(), 34525, "ethertype");
566 assert_eq!(pktsum.vlan_id.unwrap(), 4095, "vlan_id");
567 assert_eq!(pktsum.l3_src.unwrap(), 1, "l3_src");
568 assert_eq!(
569 pktsum.l3_dst.unwrap(),
570 153312949341957855387619965112881774883,
571 "l3_dst"
572 );
573 assert_eq!(pktsum.next_proto.unwrap(), 6, "next_proto");
574 assert_eq!(pktsum.l4_sport.unwrap(), 80, "l4_sport");
575 assert_eq!(pktsum.l4_dport.unwrap(), 5355, "l4_dport");
576 }
577
578 #[test]
579 fn test_handle_eth() {
580 let mut pktsum = PacketSummary::new();
581 let expected = (14, ProtoHandler::VLAN);
582
583 let result = handle_eth(&REF_V4_PACKET, 0, &mut pktsum);
584 assert_eq!(result.unwrap(), expected, "offset");
585 assert_eq!(pktsum.l2_dst.unwrap(), 1, "l2_dst");
586 assert_eq!(pktsum.l2_src.unwrap(), 281474976710655, "l2_src");
587 }
588
589 #[test]
590 fn test_handle_vlan() {
591 let mut pktsum = PacketSummary::new();
592 let expected = (18, ProtoHandler::IPV4);
593
594 let result = handle_vlan(&REF_V4_PACKET, 14, &mut pktsum);
595 assert_eq!(result.unwrap(), expected, "offset");
596 assert_eq!(pktsum.ethertype.unwrap(), 2048, "ethertype");
597 assert_eq!(pktsum.vlan_id.unwrap(), 4095, "vlan_id");
598 }
599
600 #[test]
601 fn test_handle_ipv4() {
602 let mut pktsum = PacketSummary::new();
603 let expected = (38, ProtoHandler::TCP);
604
605 let result = handle_ipv4(&REF_V4_PACKET, 18, &mut pktsum);
606 assert_eq!(result.unwrap(), expected, "offset");
607 assert_eq!(pktsum.l3_src.unwrap(), 2130706433, "l3_src");
608 assert_eq!(pktsum.l3_dst.unwrap(), 3232235777, "l3_dst");
609 assert_eq!(pktsum.next_proto.unwrap(), 6, "next_proto");
610 }
611
612 #[test]
613 fn test_handle_ipv6() {
614 let mut pktsum = PacketSummary::new();
615 let expected = (114, ProtoHandler::TCP);
616
617 let result = handle_ipv6(&REF_V6_PACKET, 18, &mut pktsum);
618 assert_eq!(result.unwrap(), expected, "offset");
619 assert_eq!(pktsum.l3_src.unwrap(), 1, "l3_src");
620 assert_eq!(
621 pktsum.l3_dst.unwrap(),
622 153312949341957855387619965112881774883,
623 "l3_dst"
624 );
625 assert_eq!(pktsum.next_proto.unwrap(), 6, "next_proto");
626 }
627
628 #[test]
629 fn test_handle_unknown() {
630 let mut pktsum = PacketSummary::new();
631
632 let result = handle_unknown(&REF_V4_PACKET, 0, &mut pktsum);
633 assert!(result.is_err());
634 }
635}