1use crate::error::VCLError;
30use etherparse::{
31 Ipv4HeaderSlice, Ipv6HeaderSlice,
32 TcpHeaderSlice, UdpHeaderSlice,
33};
34use tracing::debug;
35
36#[derive(Debug, Clone, PartialEq)]
38pub enum IpVersion {
39 V4,
40 V6,
41}
42
43#[derive(Debug, Clone, PartialEq)]
45pub enum TransportProtocol {
46 Tcp {
48 src_port: u16,
49 dst_port: u16,
50 syn: bool,
51 ack: bool,
52 fin: bool,
53 rst: bool,
54 payload_offset: usize,
55 },
56 Udp {
58 src_port: u16,
59 dst_port: u16,
60 payload_offset: usize,
61 },
62 Icmp {
64 icmp_type: u8,
65 code: u8,
66 },
67 Icmpv6 {
69 icmp_type: u8,
70 code: u8,
71 },
72 Other {
74 protocol_number: u8,
75 },
76}
77
78impl TransportProtocol {
79 pub fn src_port(&self) -> Option<u16> {
81 match self {
82 TransportProtocol::Tcp { src_port, .. } => Some(*src_port),
83 TransportProtocol::Udp { src_port, .. } => Some(*src_port),
84 _ => None,
85 }
86 }
87
88 pub fn dst_port(&self) -> Option<u16> {
90 match self {
91 TransportProtocol::Tcp { dst_port, .. } => Some(*dst_port),
92 TransportProtocol::Udp { dst_port, .. } => Some(*dst_port),
93 _ => None,
94 }
95 }
96
97 pub fn is_syn(&self) -> bool {
99 matches!(self, TransportProtocol::Tcp { syn: true, ack: false, .. })
100 }
101
102 pub fn is_fin(&self) -> bool {
104 matches!(self, TransportProtocol::Tcp { fin: true, .. })
105 }
106
107 pub fn is_rst(&self) -> bool {
109 matches!(self, TransportProtocol::Tcp { rst: true, .. })
110 }
111
112 pub fn protocol_number(&self) -> u8 {
114 match self {
115 TransportProtocol::Tcp { .. } => 6,
116 TransportProtocol::Udp { .. } => 17,
117 TransportProtocol::Icmp { .. } => 1,
118 TransportProtocol::Icmpv6 { .. } => 58,
119 TransportProtocol::Other { protocol_number } => *protocol_number,
120 }
121 }
122}
123
124#[derive(Debug, Clone)]
126pub struct ParsedPacket {
127 pub raw: Vec<u8>,
129 pub ip_version: IpVersion,
131 pub src_ip: String,
133 pub dst_ip: String,
135 pub ttl: u8,
137 pub total_len: usize,
139 pub ip_payload_offset: usize,
141 pub transport: TransportProtocol,
143}
144
145impl ParsedPacket {
146 pub fn parse(raw: Vec<u8>) -> Result<Self, VCLError> {
153 if raw.is_empty() {
154 return Err(VCLError::InvalidPacket("Empty packet".to_string()));
155 }
156 match raw[0] >> 4 {
157 4 => Self::parse_ipv4(raw),
158 6 => Self::parse_ipv6(raw),
159 v => Err(VCLError::InvalidPacket(format!("Unknown IP version: {}", v))),
160 }
161 }
162
163 fn parse_ipv4(raw: Vec<u8>) -> Result<Self, VCLError> {
164 let header = Ipv4HeaderSlice::from_slice(&raw)
165 .map_err(|e| VCLError::InvalidPacket(format!("IPv4 header error: {}", e)))?;
166
167 let src_ip = format!(
168 "{}.{}.{}.{}",
169 header.source()[0], header.source()[1],
170 header.source()[2], header.source()[3]
171 );
172 let dst_ip = format!(
173 "{}.{}.{}.{}",
174 header.destination()[0], header.destination()[1],
175 header.destination()[2], header.destination()[3]
176 );
177 let ttl = header.ttl();
178 let protocol = header.protocol().0;
179 let ip_payload_offset = (header.ihl() as usize) * 4;
180 let total_len = raw.len();
181
182 let transport = parse_transport(protocol, &raw, ip_payload_offset)?;
183
184 debug!(
185 src = %src_ip, dst = %dst_ip,
186 protocol, ttl, total_len,
187 "IPv4 packet parsed"
188 );
189
190 Ok(ParsedPacket {
191 raw,
192 ip_version: IpVersion::V4,
193 src_ip,
194 dst_ip,
195 ttl,
196 total_len,
197 ip_payload_offset,
198 transport,
199 })
200 }
201
202 fn parse_ipv6(raw: Vec<u8>) -> Result<Self, VCLError> {
203 let header = Ipv6HeaderSlice::from_slice(&raw)
204 .map_err(|e| VCLError::InvalidPacket(format!("IPv6 header error: {}", e)))?;
205
206 let src_ip = format!("{}", header.source_addr());
207 let dst_ip = format!("{}", header.destination_addr());
208 let ttl = header.hop_limit();
209 let protocol = header.next_header().0;
210 let ip_payload_offset = 40; let total_len = raw.len();
212
213 let transport = parse_transport(protocol, &raw, ip_payload_offset)?;
214
215 debug!(
216 src = %src_ip, dst = %dst_ip,
217 protocol, ttl, total_len,
218 "IPv6 packet parsed"
219 );
220
221 Ok(ParsedPacket {
222 raw,
223 ip_version: IpVersion::V6,
224 src_ip,
225 dst_ip,
226 ttl,
227 total_len,
228 ip_payload_offset,
229 transport,
230 })
231 }
232
233 pub fn is_ipv4(&self) -> bool {
235 self.ip_version == IpVersion::V4
236 }
237
238 pub fn is_ipv6(&self) -> bool {
240 self.ip_version == IpVersion::V6
241 }
242
243 pub fn is_destined_for(&self, ip: &str) -> bool {
245 self.dst_ip == ip
246 }
247
248 pub fn is_from(&self, ip: &str) -> bool {
250 self.src_ip == ip
251 }
252
253 pub fn ip_payload(&self) -> &[u8] {
255 if self.ip_payload_offset < self.raw.len() {
256 &self.raw[self.ip_payload_offset..]
257 } else {
258 &[]
259 }
260 }
261
262 pub fn is_dns(&self) -> bool {
264 matches!(&self.transport, TransportProtocol::Udp { dst_port: 53, .. })
265 }
266
267 pub fn is_ping(&self) -> bool {
269 matches!(&self.transport,
270 TransportProtocol::Icmp { icmp_type: 8, .. } |
271 TransportProtocol::Icmpv6 { icmp_type: 128, .. }
272 )
273 }
274
275 pub fn summary(&self) -> String {
277 match &self.transport {
278 TransportProtocol::Tcp { src_port, dst_port, syn, fin, rst, .. } => {
279 let flags = if *syn { " SYN" } else if *fin { " FIN" } else if *rst { " RST" } else { "" };
280 format!("TCP {}:{} → {}:{}{} ({} bytes)",
281 self.src_ip, src_port, self.dst_ip, dst_port, flags, self.total_len)
282 }
283 TransportProtocol::Udp { src_port, dst_port, .. } => {
284 format!("UDP {}:{} → {}:{} ({} bytes)",
285 self.src_ip, src_port, self.dst_ip, dst_port, self.total_len)
286 }
287 TransportProtocol::Icmp { icmp_type, code } => {
288 format!("ICMP {} → {} type={} code={} ({} bytes)",
289 self.src_ip, self.dst_ip, icmp_type, code, self.total_len)
290 }
291 TransportProtocol::Icmpv6 { icmp_type, code } => {
292 format!("ICMPv6 {} → {} type={} code={} ({} bytes)",
293 self.src_ip, self.dst_ip, icmp_type, code, self.total_len)
294 }
295 TransportProtocol::Other { protocol_number } => {
296 format!("Proto#{} {} → {} ({} bytes)",
297 protocol_number, self.src_ip, self.dst_ip, self.total_len)
298 }
299 }
300 }
301}
302
303fn parse_transport(
304 protocol: u8,
305 raw: &[u8],
306 offset: usize,
307) -> Result<TransportProtocol, VCLError> {
308 match protocol {
309 6 => parse_tcp(raw, offset),
310 17 => parse_udp(raw, offset),
311 1 => parse_icmp(raw, offset),
312 58 => parse_icmpv6(raw, offset),
313 p => Ok(TransportProtocol::Other { protocol_number: p }),
314 }
315}
316
317fn parse_tcp(raw: &[u8], offset: usize) -> Result<TransportProtocol, VCLError> {
318 if offset >= raw.len() {
319 return Ok(TransportProtocol::Other { protocol_number: 6 });
320 }
321 let tcp = TcpHeaderSlice::from_slice(&raw[offset..])
322 .map_err(|e| VCLError::InvalidPacket(format!("TCP header error: {}", e)))?;
323 let payload_offset = offset + (tcp.data_offset() as usize) * 4;
324 Ok(TransportProtocol::Tcp {
325 src_port: tcp.source_port(),
326 dst_port: tcp.destination_port(),
327 syn: tcp.syn(),
328 ack: tcp.ack(),
329 fin: tcp.fin(),
330 rst: tcp.rst(),
331 payload_offset,
332 })
333}
334
335fn parse_udp(raw: &[u8], offset: usize) -> Result<TransportProtocol, VCLError> {
336 if offset >= raw.len() {
337 return Ok(TransportProtocol::Other { protocol_number: 17 });
338 }
339 let udp = UdpHeaderSlice::from_slice(&raw[offset..])
340 .map_err(|e| VCLError::InvalidPacket(format!("UDP header error: {}", e)))?;
341 let payload_offset = offset + 8; Ok(TransportProtocol::Udp {
343 src_port: udp.source_port(),
344 dst_port: udp.destination_port(),
345 payload_offset,
346 })
347}
348
349fn parse_icmp(raw: &[u8], offset: usize) -> Result<TransportProtocol, VCLError> {
350 if offset + 2 > raw.len() {
351 return Ok(TransportProtocol::Other { protocol_number: 1 });
352 }
353 Ok(TransportProtocol::Icmp {
354 icmp_type: raw[offset],
355 code: raw[offset + 1],
356 })
357}
358
359fn parse_icmpv6(raw: &[u8], offset: usize) -> Result<TransportProtocol, VCLError> {
360 if offset + 2 > raw.len() {
361 return Ok(TransportProtocol::Other { protocol_number: 58 });
362 }
363 Ok(TransportProtocol::Icmpv6 {
364 icmp_type: raw[offset],
365 code: raw[offset + 1],
366 })
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 fn ipv4_tcp_packet() -> Vec<u8> {
374 vec![
375 0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 192, 168, 1, 1, 10, 0, 0, 1, 0x00, 0x50, 0x1F, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x50, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, ]
390 }
391
392 fn ipv4_udp_packet() -> Vec<u8> {
393 vec![
394 0x45, 0x00, 0x00, 0x1C,
396 0x00, 0x01, 0x00, 0x00,
397 0x40, 0x11, 0x00, 0x00, 10, 0, 0, 1,
399 8, 8, 8, 8, 0x04, 0x00, 0x00, 0x35, 0x00, 0x08, 0x00, 0x00, ]
406 }
407
408 fn ipv4_icmp_packet() -> Vec<u8> {
409 vec![
410 0x45, 0x00, 0x00, 0x1C,
412 0x00, 0x01, 0x00, 0x00,
413 0x40, 0x01, 0x00, 0x00, 10, 0, 0, 1,
415 10, 0, 0, 2,
416 0x08, 0x00, 0x00, 0x00, ]
420 }
421
422 #[test]
423 fn test_parse_tcp() {
424 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
425 assert!(pkt.is_ipv4());
426 assert_eq!(pkt.src_ip, "192.168.1.1");
427 assert_eq!(pkt.dst_ip, "10.0.0.1");
428 assert_eq!(pkt.ttl, 64);
429 assert!(matches!(pkt.transport, TransportProtocol::Tcp {
430 src_port: 80, dst_port: 8080, syn: true, ..
431 }));
432 assert!(pkt.transport.is_syn());
433 assert!(!pkt.transport.is_fin());
434 assert_eq!(pkt.transport.src_port(), Some(80));
435 assert_eq!(pkt.transport.dst_port(), Some(8080));
436 assert_eq!(pkt.transport.protocol_number(), 6);
437 }
438
439 #[test]
440 fn test_parse_udp_dns() {
441 let pkt = ParsedPacket::parse(ipv4_udp_packet()).unwrap();
442 assert!(pkt.is_ipv4());
443 assert_eq!(pkt.dst_ip, "8.8.8.8");
444 assert!(matches!(pkt.transport, TransportProtocol::Udp {
445 src_port: 1024, dst_port: 53, ..
446 }));
447 assert!(pkt.is_dns());
448 assert_eq!(pkt.transport.protocol_number(), 17);
449 }
450
451 #[test]
452 fn test_parse_icmp_ping() {
453 let pkt = ParsedPacket::parse(ipv4_icmp_packet()).unwrap();
454 assert!(pkt.is_ping());
455 assert!(matches!(pkt.transport, TransportProtocol::Icmp {
456 icmp_type: 8, code: 0
457 }));
458 assert_eq!(pkt.transport.protocol_number(), 1);
459 }
460
461 #[test]
462 fn test_parse_empty() {
463 assert!(ParsedPacket::parse(vec![]).is_err());
464 }
465
466 #[test]
467 fn test_parse_unknown_version() {
468 let raw = vec![0x30u8; 20];
469 assert!(ParsedPacket::parse(raw).is_err());
470 }
471
472 #[test]
473 fn test_is_destined_for() {
474 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
475 assert!(pkt.is_destined_for("10.0.0.1"));
476 assert!(!pkt.is_destined_for("1.2.3.4"));
477 }
478
479 #[test]
480 fn test_is_from() {
481 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
482 assert!(pkt.is_from("192.168.1.1"));
483 assert!(!pkt.is_from("1.2.3.4"));
484 }
485
486 #[test]
487 fn test_ip_payload() {
488 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
489 assert_eq!(pkt.ip_payload_offset, 20);
491 assert!(!pkt.ip_payload().is_empty());
492 }
493
494 #[test]
495 fn test_summary_tcp() {
496 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
497 let s = pkt.summary();
498 assert!(s.contains("TCP"));
499 assert!(s.contains("192.168.1.1"));
500 assert!(s.contains("10.0.0.1"));
501 assert!(s.contains("80"));
502 assert!(s.contains("8080"));
503 assert!(s.contains("SYN"));
504 }
505
506 #[test]
507 fn test_summary_udp() {
508 let pkt = ParsedPacket::parse(ipv4_udp_packet()).unwrap();
509 let s = pkt.summary();
510 assert!(s.contains("UDP"));
511 assert!(s.contains("53"));
512 }
513
514 #[test]
515 fn test_tcp_flags() {
516 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
517 assert!(pkt.transport.is_syn());
518 assert!(!pkt.transport.is_fin());
519 assert!(!pkt.transport.is_rst());
520 }
521
522 #[test]
523 fn test_other_protocol() {
524 let mut raw = vec![0u8; 24];
525 raw[0] = 0x45; raw[9] = 0x2F; raw[12..16].copy_from_slice(&[10, 0, 0, 1]);
528 raw[16..20].copy_from_slice(&[10, 0, 0, 2]);
529 let pkt = ParsedPacket::parse(raw).unwrap();
530 assert!(matches!(pkt.transport, TransportProtocol::Other { protocol_number: 47 }));
531 assert_eq!(pkt.transport.protocol_number(), 47);
532 }
533
534 #[test]
535 fn test_is_not_dns_for_tcp() {
536 let pkt = ParsedPacket::parse(ipv4_tcp_packet()).unwrap();
537 assert!(!pkt.is_dns());
538 }
539
540 #[test]
541 fn test_is_not_ping_for_udp() {
542 let pkt = ParsedPacket::parse(ipv4_udp_packet()).unwrap();
543 assert!(!pkt.is_ping());
544 }
545}