Skip to main content

stackforge_core/
packet.rs

1//! Zero-copy packet representation and manipulation.
2//!
3//! This module implements the core `Packet` struct using a "Lazy Zero-Copy View"
4//! architecture. Packets are represented as:
5//!
6//! 1. A contiguous buffer of raw bytes (`Bytes`)
7//! 2. A lightweight index of layer boundaries (`SmallVec<LayerIndex>`)
8//!
9//! Field access is lazy - values are read directly from the buffer only when
10//! requested, avoiding the allocation overhead of eager parsing.
11
12use bytes::{Bytes, BytesMut};
13use smallvec::SmallVec;
14
15use crate::error::{PacketError, Result};
16use crate::layer::{
17    DnsLayer, HttpLayer, IcmpLayer, Icmpv6Layer, Ipv6Layer, LayerEnum, LayerIndex, LayerKind,
18    RawLayer, SshLayer, TcpLayer, TlsLayer, UdpLayer,
19    arp::ArpLayer,
20    ethernet::{Dot3Layer, ETHERNET_HEADER_LEN, EthernetLayer},
21    ethertype, http,
22    http2::{Http2Layer, is_http2_payload},
23    icmp, ip_protocol,
24    ipv4::Ipv4Layer,
25    ssh::{SSH_PORT, is_ssh_payload},
26    tls::is_tls_payload,
27};
28
29/// Maximum number of layers to store inline before heap allocation.
30const INLINE_LAYER_CAPACITY: usize = 4;
31
32/// A network packet with zero-copy buffer storage.
33///
34/// # Architecture
35///
36/// The `Packet` struct implements a "Lazy Zero-Copy View" model:
37///
38/// - **Zero-Copy**: The `data` field uses `Bytes`, a reference-counted buffer.
39/// - **Lazy**: Fields are not parsed until accessed.
40/// - **Copy-on-Write**: Mutation triggers cloning only when buffer is shared.
41#[derive(Debug, Clone)]
42pub struct Packet {
43    data: Bytes,
44    layers: SmallVec<[LayerIndex; INLINE_LAYER_CAPACITY]>,
45    is_dirty: bool,
46}
47
48impl Packet {
49    // ========================================================================
50    // Constructors
51    // ========================================================================
52
53    /// Creates an empty packet with no data.
54    #[inline]
55    pub fn empty() -> Self {
56        Self {
57            data: Bytes::new(),
58            layers: SmallVec::new(),
59            is_dirty: false,
60        }
61    }
62
63    /// Creates a packet from raw bytes without parsing.
64    #[inline]
65    pub fn from_bytes(data: impl Into<Bytes>) -> Self {
66        Self {
67            data: data.into(),
68            layers: SmallVec::new(),
69            is_dirty: false,
70        }
71    }
72
73    /// Creates a packet from a byte slice by copying the data.
74    #[inline]
75    pub fn from_slice(data: &[u8]) -> Self {
76        Self {
77            data: Bytes::copy_from_slice(data),
78            layers: SmallVec::new(),
79            is_dirty: false,
80        }
81    }
82
83    /// Creates a new packet with pre-allocated capacity.
84    #[inline]
85    pub fn with_capacity(capacity: usize) -> Self {
86        Self {
87            data: Bytes::from(BytesMut::with_capacity(capacity)),
88            layers: SmallVec::new(),
89            is_dirty: false,
90        }
91    }
92
93    // ========================================================================
94    // Basic Properties
95    // ========================================================================
96
97    #[inline]
98    pub fn len(&self) -> usize {
99        self.data.len()
100    }
101    #[inline]
102    pub fn is_empty(&self) -> bool {
103        self.data.is_empty()
104    }
105    #[inline]
106    pub fn is_dirty(&self) -> bool {
107        self.is_dirty
108    }
109    #[inline]
110    pub fn layer_count(&self) -> usize {
111        self.layers.len()
112    }
113    #[inline]
114    pub fn is_parsed(&self) -> bool {
115        !self.layers.is_empty()
116    }
117
118    // ========================================================================
119    // Raw Data Access
120    // ========================================================================
121
122    #[inline]
123    pub fn as_bytes(&self) -> &[u8] {
124        &self.data
125    }
126    #[inline]
127    pub fn bytes(&self) -> Bytes {
128        self.data.clone()
129    }
130    #[inline]
131    pub fn into_bytes(self) -> Bytes {
132        self.data
133    }
134
135    // ========================================================================
136    // Layer Access
137    // ========================================================================
138
139    #[inline]
140    pub fn layers(&self) -> &[LayerIndex] {
141        &self.layers
142    }
143
144    #[inline]
145    pub fn get_layer(&self, kind: LayerKind) -> Option<&LayerIndex> {
146        self.layers.iter().find(|l| l.kind == kind)
147    }
148
149    /// Returns the bytes for a specific layer.
150    pub fn layer_bytes(&self, kind: LayerKind) -> Result<&[u8]> {
151        self.get_layer(kind)
152            .map(|idx| &self.data[idx.range()])
153            .ok_or(PacketError::LayerNotFound(kind))
154    }
155
156    /// Returns the payload (data after all parsed headers).
157    #[inline]
158    pub fn payload(&self) -> &[u8] {
159        self.layers
160            .last()
161            .map(|l| &self.data[l.end..])
162            .unwrap_or(&self.data)
163    }
164
165    // ========================================================================
166    // Typed Layer Access
167    // ========================================================================
168
169    /// Get the Ethernet layer view if present.
170    pub fn ethernet(&self) -> Option<EthernetLayer> {
171        self.get_layer(LayerKind::Ethernet)
172            .map(|idx| EthernetLayer::new(idx.start, idx.end))
173    }
174
175    /// Get the IPv4 layer view if present.
176    pub fn ipv4(&self) -> Option<Ipv4Layer> {
177        self.get_layer(LayerKind::Ipv4)
178            .map(|idx| Ipv4Layer::new(idx.start, idx.end))
179    }
180
181    /// Get the ARP layer view if present.
182    pub fn arp(&self) -> Option<ArpLayer> {
183        self.get_layer(LayerKind::Arp)
184            .map(|idx| ArpLayer::new(idx.start, idx.end))
185    }
186
187    /// Get the ICMP layer view if present.
188    pub fn icmp(&self) -> Option<IcmpLayer> {
189        self.get_layer(LayerKind::Icmp)
190            .map(|idx| IcmpLayer { index: *idx })
191    }
192
193    /// Get the TCP layer view if present.
194    pub fn tcp(&self) -> Option<TcpLayer> {
195        self.get_layer(LayerKind::Tcp)
196            .map(|idx| TcpLayer::new(idx.start, idx.end))
197    }
198
199    /// Get the UDP layer view if present.
200    pub fn udp(&self) -> Option<UdpLayer> {
201        self.get_layer(LayerKind::Udp)
202            .map(|idx| UdpLayer { index: *idx })
203    }
204
205    /// Get the DNS layer view if present.
206    pub fn dns(&self) -> Option<DnsLayer> {
207        self.get_layer(LayerKind::Dns)
208            .map(|idx| DnsLayer { index: *idx })
209    }
210
211    /// Get the TLS layer view if present.
212    pub fn tls(&self) -> Option<TlsLayer> {
213        self.get_layer(LayerKind::Tls)
214            .map(|idx| TlsLayer { index: *idx })
215    }
216
217    /// Get a LayerEnum for a given LayerIndex.
218    pub fn layer_enum(&self, idx: &LayerIndex) -> LayerEnum {
219        match idx.kind {
220            LayerKind::Ethernet => LayerEnum::Ethernet(EthernetLayer::new(idx.start, idx.end)),
221            LayerKind::Dot3 => LayerEnum::Dot3(Dot3Layer::new(idx.start, idx.end)),
222            LayerKind::Arp => LayerEnum::Arp(ArpLayer::new(idx.start, idx.end)),
223            LayerKind::Ipv4 => LayerEnum::Ipv4(Ipv4Layer::new(idx.start, idx.end)),
224            LayerKind::Ipv6 => LayerEnum::Ipv6(Ipv6Layer { index: *idx }),
225            LayerKind::Icmp => LayerEnum::Icmp(IcmpLayer { index: *idx }),
226            LayerKind::Icmpv6 => LayerEnum::Icmpv6(Icmpv6Layer { index: *idx }),
227            LayerKind::Tcp => LayerEnum::Tcp(TcpLayer::new(idx.start, idx.end)),
228            LayerKind::Udp => LayerEnum::Udp(UdpLayer { index: *idx }),
229            LayerKind::Dns => LayerEnum::Dns(DnsLayer { index: *idx }),
230            LayerKind::Ssh => LayerEnum::Ssh(SshLayer { index: *idx }),
231            LayerKind::Tls => LayerEnum::Tls(TlsLayer { index: *idx }),
232            LayerKind::Dot15d4 => {
233                LayerEnum::Dot15d4(crate::layer::dot15d4::Dot15d4Layer::new(idx.start, idx.end))
234            }
235            LayerKind::Dot15d4Fcs => LayerEnum::Dot15d4Fcs(
236                crate::layer::dot15d4::Dot15d4FcsLayer::new(idx.start, idx.end),
237            ),
238            LayerKind::Dot11 => {
239                LayerEnum::Dot11(crate::layer::dot11::Dot11Layer::new(idx.start, idx.end))
240            }
241            LayerKind::Http => LayerEnum::Http(HttpLayer { index: *idx }),
242            LayerKind::Http2 => LayerEnum::Http2(Http2Layer::new(idx.start, idx.end, false)),
243            LayerKind::Quic => LayerEnum::Quic(crate::layer::quic::QuicLayer::from_index(*idx)),
244            LayerKind::L2tp => LayerEnum::L2tp(crate::layer::l2tp::L2tpLayer::new(*idx)),
245            LayerKind::Raw
246            | LayerKind::Dot1Q
247            | LayerKind::Dot1AD
248            | LayerKind::Dot1AH
249            | LayerKind::LLC
250            | LayerKind::SNAP
251            | LayerKind::Generic => LayerEnum::Raw(RawLayer { index: *idx }),
252        }
253    }
254
255    /// Get all layers as LayerEnum objects.
256    pub fn layer_enums(&self) -> Vec<LayerEnum> {
257        self.layers.iter().map(|idx| self.layer_enum(idx)).collect()
258    }
259
260    // ========================================================================
261    // Parsing (Index-Only)
262    // ========================================================================
263
264    /// Parses the packet to identify layer boundaries.
265    pub fn parse(&mut self) -> Result<()> {
266        self.layers.clear();
267
268        if self.data.len() < ETHERNET_HEADER_LEN {
269            return Ok(());
270        }
271
272        // Parse Ethernet header
273        let eth_end = ETHERNET_HEADER_LEN;
274        self.layers
275            .push(LayerIndex::new(LayerKind::Ethernet, 0, eth_end));
276
277        let etype = u16::from_be_bytes([self.data[12], self.data[13]]);
278
279        match etype {
280            ethertype::IPV4 => self.parse_ipv4(eth_end)?,
281            ethertype::IPV6 => self.parse_ipv6(eth_end)?,
282            ethertype::ARP => self.parse_arp(eth_end)?,
283            _ => {
284                if eth_end < self.data.len() {
285                    self.layers
286                        .push(LayerIndex::new(LayerKind::Raw, eth_end, self.data.len()));
287                }
288            }
289        }
290
291        Ok(())
292    }
293
294    fn parse_ipv4(&mut self, offset: usize) -> Result<()> {
295        let min_size = 20;
296        if offset + min_size > self.data.len() {
297            return Err(PacketError::BufferTooShort {
298                expected: offset + min_size,
299                actual: self.data.len(),
300            });
301        }
302
303        let ihl = (self.data[offset] & 0x0F) as usize;
304        let header_len = ihl * 4;
305
306        if header_len < min_size {
307            return Err(PacketError::ParseError {
308                offset,
309                message: format!("invalid IHL: {}", ihl),
310            });
311        }
312
313        let ip_end = offset + header_len;
314        if ip_end > self.data.len() {
315            return Err(PacketError::BufferTooShort {
316                expected: ip_end,
317                actual: self.data.len(),
318            });
319        }
320
321        self.layers
322            .push(LayerIndex::new(LayerKind::Ipv4, offset, ip_end));
323
324        let protocol = self.data[offset + 9];
325        match protocol {
326            ip_protocol::TCP => self.parse_tcp(ip_end)?,
327            ip_protocol::UDP => self.parse_udp(ip_end)?,
328            ip_protocol::ICMP => self.parse_icmp(ip_end)?,
329            _ => {
330                if ip_end < self.data.len() {
331                    self.layers
332                        .push(LayerIndex::new(LayerKind::Raw, ip_end, self.data.len()));
333                }
334            }
335        }
336
337        Ok(())
338    }
339
340    fn parse_ipv6(&mut self, offset: usize) -> Result<()> {
341        let min_size = 40;
342        if offset + min_size > self.data.len() {
343            return Err(PacketError::BufferTooShort {
344                expected: offset + min_size,
345                actual: self.data.len(),
346            });
347        }
348
349        let ip_end = offset + min_size;
350        self.layers
351            .push(LayerIndex::new(LayerKind::Ipv6, offset, ip_end));
352
353        let next_header = self.data[offset + 6];
354        match next_header {
355            ip_protocol::TCP => self.parse_tcp(ip_end)?,
356            ip_protocol::UDP => self.parse_udp(ip_end)?,
357            ip_protocol::ICMPV6 => self.parse_icmpv6(ip_end)?,
358            _ => {
359                if ip_end < self.data.len() {
360                    self.layers
361                        .push(LayerIndex::new(LayerKind::Raw, ip_end, self.data.len()));
362                }
363            }
364        }
365
366        Ok(())
367    }
368
369    fn parse_arp(&mut self, offset: usize) -> Result<()> {
370        // First check if we have enough bytes for hwlen/plen fields
371        if offset + 5 > self.data.len() {
372            return Err(PacketError::BufferTooShort {
373                expected: offset + 5,
374                actual: self.data.len(),
375            });
376        }
377
378        let hwlen = self.data[offset + 4] as usize;
379        let plen = self.data[offset + 5] as usize;
380        let total_len = 8 + 2 * hwlen + 2 * plen;
381
382        if offset + total_len > self.data.len() {
383            return Err(PacketError::BufferTooShort {
384                expected: offset + total_len,
385                actual: self.data.len(),
386            });
387        }
388
389        let arp_end = offset + total_len;
390        self.layers
391            .push(LayerIndex::new(LayerKind::Arp, offset, arp_end));
392
393        if arp_end < self.data.len() {
394            self.layers
395                .push(LayerIndex::new(LayerKind::Raw, arp_end, self.data.len()));
396        }
397
398        Ok(())
399    }
400
401    fn parse_tcp(&mut self, offset: usize) -> Result<()> {
402        let min_size = 20;
403        if offset + min_size > self.data.len() {
404            return Err(PacketError::BufferTooShort {
405                expected: offset + min_size,
406                actual: self.data.len(),
407            });
408        }
409
410        let data_offset = ((self.data[offset + 12] >> 4) & 0x0F) as usize;
411        let header_len = data_offset * 4;
412
413        let tcp_end = (offset + header_len).min(self.data.len());
414        self.layers
415            .push(LayerIndex::new(LayerKind::Tcp, offset, tcp_end));
416
417        if tcp_end < self.data.len() {
418            // Check for SSH on port 22
419            let src_port = u16::from_be_bytes([self.data[offset], self.data[offset + 1]]);
420            let dst_port = u16::from_be_bytes([self.data[offset + 2], self.data[offset + 3]]);
421
422            let payload = &self.data[tcp_end..];
423            if (src_port == SSH_PORT || dst_port == SSH_PORT) && is_ssh_payload(payload) {
424                self.parse_ssh(tcp_end)?;
425            } else if is_tls_payload(payload) {
426                self.parse_tls(tcp_end)?;
427            } else if is_http2_payload(payload) {
428                self.layers
429                    .push(LayerIndex::new(LayerKind::Http2, tcp_end, self.data.len()));
430            } else if http::detection::is_http(payload) {
431                self.layers
432                    .push(LayerIndex::new(LayerKind::Http, tcp_end, self.data.len()));
433            } else {
434                self.layers
435                    .push(LayerIndex::new(LayerKind::Raw, tcp_end, self.data.len()));
436            }
437        }
438
439        Ok(())
440    }
441
442    fn parse_udp(&mut self, offset: usize) -> Result<()> {
443        let min_size = 8;
444        if offset + min_size > self.data.len() {
445            return Err(PacketError::BufferTooShort {
446                expected: offset + min_size,
447                actual: self.data.len(),
448            });
449        }
450
451        let udp_end = offset + min_size;
452        self.layers
453            .push(LayerIndex::new(LayerKind::Udp, offset, udp_end));
454
455        // Check for DNS
456        let dst_port = u16::from_be_bytes([self.data[offset + 2], self.data[offset + 3]]);
457        let src_port = u16::from_be_bytes([self.data[offset], self.data[offset + 1]]);
458
459        if (dst_port == 53 || src_port == 53 || dst_port == 5353 || src_port == 5353)
460            && udp_end + 12 <= self.data.len()
461        {
462            self.layers
463                .push(LayerIndex::new(LayerKind::Dns, udp_end, self.data.len()));
464        } else if (dst_port == 443 || src_port == 443 || dst_port == 4433 || src_port == 4433)
465            && udp_end < self.data.len()
466            && is_quic_payload(&self.data[udp_end..])
467        {
468            self.layers
469                .push(LayerIndex::new(LayerKind::Quic, udp_end, self.data.len()));
470        } else if (dst_port == 1701 || src_port == 1701)
471            && udp_end + 2 <= self.data.len()
472            && (self.data[udp_end + 1] & 0x0F) == 2
473        {
474            // L2TPv2: version nibble (low 4 bits of second byte) must be 2
475            self.layers
476                .push(LayerIndex::new(LayerKind::L2tp, udp_end, self.data.len()));
477        } else if udp_end < self.data.len() {
478            self.layers
479                .push(LayerIndex::new(LayerKind::Raw, udp_end, self.data.len()));
480        }
481
482        Ok(())
483    }
484
485    fn parse_icmp(&mut self, offset: usize) -> Result<()> {
486        if offset + 8 > self.data.len() {
487            return Err(PacketError::BufferTooShort {
488                expected: offset + 8,
489                actual: self.data.len(),
490            });
491        }
492
493        // Add ICMP layer
494        self.layers
495            .push(LayerIndex::new(LayerKind::Icmp, offset, self.data.len()));
496
497        // Check if this is an ICMP error message that contains an embedded packet
498        // Error types: 3 (dest unreach), 4 (source quench), 5 (redirect), 11 (time exceeded), 12 (param problem)
499        if offset < self.data.len() {
500            let icmp_type = self.data[offset];
501            if icmp::is_error_type(icmp_type) {
502                // ICMP error messages have 8-byte header, then embedded IP packet
503                let embedded_offset = offset + 8;
504                if embedded_offset < self.data.len() {
505                    // Try to parse the embedded IP packet
506                    // Silently ignore errors since the embedded packet might be truncated
507                    let _ = self.parse_ipv4(embedded_offset);
508                }
509            }
510        }
511
512        Ok(())
513    }
514
515    fn parse_icmpv6(&mut self, offset: usize) -> Result<()> {
516        if offset + 8 > self.data.len() {
517            return Err(PacketError::BufferTooShort {
518                expected: offset + 8,
519                actual: self.data.len(),
520            });
521        }
522        self.layers
523            .push(LayerIndex::new(LayerKind::Icmpv6, offset, self.data.len()));
524        Ok(())
525    }
526
527    fn parse_ssh(&mut self, offset: usize) -> Result<()> {
528        let remaining = &self.data[offset..];
529
530        if remaining.len() >= 4 && &remaining[..4] == b"SSH-" {
531            // SSH version exchange - spans until \r\n
532            let end = remaining
533                .windows(2)
534                .position(|w| w == b"\r\n")
535                .map(|p| offset + p + 2)
536                .unwrap_or(self.data.len());
537            self.layers
538                .push(LayerIndex::new(LayerKind::Ssh, offset, end));
539            if end < self.data.len() {
540                self.layers
541                    .push(LayerIndex::new(LayerKind::Raw, end, self.data.len()));
542            }
543        } else if remaining.len() >= 5 {
544            // SSH binary packet
545            let pkt_len =
546                u32::from_be_bytes([remaining[0], remaining[1], remaining[2], remaining[3]])
547                    as usize;
548            let end = (offset + 4 + pkt_len).min(self.data.len());
549            self.layers
550                .push(LayerIndex::new(LayerKind::Ssh, offset, end));
551            if end < self.data.len() {
552                self.layers
553                    .push(LayerIndex::new(LayerKind::Raw, end, self.data.len()));
554            }
555        } else {
556            self.layers
557                .push(LayerIndex::new(LayerKind::Raw, offset, self.data.len()));
558        }
559
560        Ok(())
561    }
562
563    fn parse_tls(&mut self, offset: usize) -> Result<()> {
564        use crate::layer::tls::TLS_RECORD_HEADER_LEN;
565
566        let remaining = self.data.len() - offset;
567        if remaining < TLS_RECORD_HEADER_LEN {
568            self.layers
569                .push(LayerIndex::new(LayerKind::Raw, offset, self.data.len()));
570            return Ok(());
571        }
572
573        // Read the TLS record header
574        let frag_len = u16::from_be_bytes([self.data[offset + 3], self.data[offset + 4]]) as usize;
575        let record_end = (offset + TLS_RECORD_HEADER_LEN + frag_len).min(self.data.len());
576
577        self.layers
578            .push(LayerIndex::new(LayerKind::Tls, offset, record_end));
579
580        // If there's more data after this record, treat as Raw
581        // (could be additional TLS records, but we parse one for now)
582        if record_end < self.data.len() {
583            self.layers
584                .push(LayerIndex::new(LayerKind::Raw, record_end, self.data.len()));
585        }
586
587        Ok(())
588    }
589
590    // ========================================================================
591    // Mutation Support (Copy-on-Write)
592    // ========================================================================
593
594    /// Applies a mutation function to the packet data.
595    pub fn with_data_mut<F, R>(&mut self, f: F) -> R
596    where
597        F: FnOnce(&mut [u8]) -> R,
598    {
599        let bytes = std::mem::take(&mut self.data);
600        let mut bytes_mut = bytes
601            .try_into_mut()
602            .unwrap_or_else(|b| BytesMut::from(b.as_ref()));
603        let result = f(&mut bytes_mut);
604        self.data = bytes_mut.freeze();
605        self.is_dirty = true;
606        result
607    }
608
609    pub fn set_byte(&mut self, offset: usize, value: u8) {
610        self.with_data_mut(|data| data[offset] = value);
611    }
612
613    pub fn set_bytes(&mut self, offset: usize, values: &[u8]) {
614        self.with_data_mut(|data| data[offset..offset + values.len()].copy_from_slice(values));
615    }
616
617    #[inline]
618    pub fn mark_dirty(&mut self) {
619        self.is_dirty = true;
620    }
621    #[inline]
622    pub fn mark_clean(&mut self) {
623        self.is_dirty = false;
624    }
625
626    pub fn add_layer(&mut self, index: LayerIndex) {
627        self.layers.push(index);
628    }
629
630    pub fn set_data(&mut self, data: BytesMut) {
631        self.data = data.freeze();
632        self.is_dirty = true;
633    }
634}
635
636impl Default for Packet {
637    fn default() -> Self {
638        Self::empty()
639    }
640}
641
642impl From<Vec<u8>> for Packet {
643    fn from(data: Vec<u8>) -> Self {
644        Self::from_bytes(data)
645    }
646}
647
648impl From<&[u8]> for Packet {
649    fn from(data: &[u8]) -> Self {
650        Self::from_slice(data)
651    }
652}
653
654impl AsRef<[u8]> for Packet {
655    fn as_ref(&self) -> &[u8] {
656        self.as_bytes()
657    }
658}
659
660/// Detect a QUIC payload by checking that the Fixed Bit (bit 6) is set.
661/// RFC 9000 ยง17: all QUIC packets have the Fixed Bit set to 1.
662fn is_quic_payload(buf: &[u8]) -> bool {
663    !buf.is_empty() && (buf[0] & 0x40) != 0
664}
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669    use crate::ARP_HEADER_LEN;
670    use crate::layer::field::MacAddress;
671
672    fn sample_arp_packet() -> Vec<u8> {
673        vec![
674            // Ethernet header (14 bytes)
675            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // dst: broadcast
676            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // src
677            0x08, 0x06, // type: ARP
678            // ARP header (28 bytes)
679            0x00, 0x01, // hwtype: Ethernet
680            0x08, 0x00, // ptype: IPv4
681            0x06, 0x04, // hwlen, plen
682            0x00, 0x01, // op: request
683            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // hwsrc
684            0xc0, 0xa8, 0x01, 0x01, // psrc: 192.168.1.1
685            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hwdst
686            0xc0, 0xa8, 0x01, 0x02, // pdst: 192.168.1.2
687        ]
688    }
689
690    #[test]
691    fn test_packet_parse_arp() {
692        let data = sample_arp_packet();
693        let mut packet = Packet::from_bytes(data);
694        packet.parse().unwrap();
695
696        assert_eq!(packet.layer_count(), 2); // Ethernet + ARP
697
698        let eth = packet.ethernet().unwrap();
699        assert!(eth.is_broadcast(packet.as_bytes()));
700        assert_eq!(eth.ethertype(packet.as_bytes()).unwrap(), ethertype::ARP);
701
702        let arp = packet.arp().unwrap();
703        assert!(arp.is_request(packet.as_bytes()));
704    }
705
706    #[test]
707    fn test_packet_layer_access() {
708        let data = sample_arp_packet();
709        let mut packet = Packet::from_bytes(data);
710        packet.parse().unwrap();
711
712        let eth_bytes = packet.layer_bytes(LayerKind::Ethernet).unwrap();
713        assert_eq!(eth_bytes.len(), ETHERNET_HEADER_LEN);
714
715        let arp_bytes = packet.layer_bytes(LayerKind::Arp).unwrap();
716        assert_eq!(arp_bytes.len(), ARP_HEADER_LEN);
717    }
718
719    #[test]
720    fn test_packet_modify_through_layer() {
721        let data = sample_arp_packet();
722        let mut packet = Packet::from_bytes(data);
723        packet.parse().unwrap();
724
725        // Modify source MAC through ethernet layer
726        let eth = packet.ethernet().unwrap();
727        packet.with_data_mut(|buf| {
728            eth.set_src(buf, MacAddress::new([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]))
729                .unwrap();
730        });
731
732        assert!(packet.is_dirty());
733        let eth = packet.ethernet().unwrap();
734        assert_eq!(
735            eth.src(packet.as_bytes()).unwrap(),
736            MacAddress::new([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
737        );
738    }
739
740    #[test]
741    fn test_icmp_error_packet_parsing() {
742        // ICMP Destination Unreachable with embedded UDP packet
743        let data = vec![
744            // Ethernet header (14 bytes)
745            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // dst
746            0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // src
747            0x08, 0x00, // ethertype: IPv4
748            // Outer IPv4 header (20 bytes) - ICMP error message from router
749            0x45, 0x00, 0x00, 0x38, // ver/ihl, tos, total len = 56
750            0x00, 0x01, 0x00, 0x00, // id, flags, frag offset
751            0x40, 0x01, 0x00, 0x00, // ttl=64, proto=ICMP, checksum
752            192, 168, 1, 1, // src: router
753            192, 168, 1, 100, // dst: original sender
754            // ICMP Destination Unreachable (8 bytes)
755            0x03, // type = 3 (dest unreachable)
756            0x03, // code = 3 (port unreachable)
757            0x00, 0x00, // checksum
758            0x00, 0x00, 0x00, 0x00, // unused
759            // Embedded original IPv4 header (20 bytes)
760            0x45, 0x00, 0x00, 0x1c, // ver/ihl, tos, total len = 28
761            0xab, 0xcd, 0x00, 0x00, // id, flags, frag offset
762            0x40, 0x11, 0x00, 0x00, // ttl=64, proto=UDP, checksum
763            192, 168, 1, 100, // src: original sender
764            8, 8, 8, 8, // dst: 8.8.8.8 (where packet was sent)
765            // Embedded UDP header (first 8 bytes)
766            0x04, 0xd2, // sport = 1234
767            0x00, 0x35, // dport = 53 (DNS)
768            0x00, 0x14, // length = 20
769            0x00, 0x00, // checksum
770        ];
771
772        let mut packet = Packet::from_bytes(data);
773        packet.parse().unwrap();
774
775        // Should have: Ethernet, IP (outer), ICMP, IP (embedded), UDP (embedded)
776        assert_eq!(packet.layer_count(), 5);
777
778        // Verify outer IP layer
779        assert!(packet.ethernet().is_some());
780        assert!(packet.ipv4().is_some());
781        assert!(packet.icmp().is_some());
782
783        // Verify ICMP error type
784        let icmp = packet.icmp().unwrap();
785        let icmp_type = icmp.icmp_type(packet.as_bytes()).unwrap();
786        assert_eq!(icmp_type, 3); // Destination unreachable
787
788        // Verify embedded packet layers exist
789        // The second IP layer is the embedded one
790        let layers = packet.layers();
791        let _embedded_ip_idx = layers
792            .iter()
793            .rposition(|idx| idx.kind == LayerKind::Ipv4)
794            .unwrap();
795
796        // Verify embedded UDP exists
797        assert!(packet.udp().is_some());
798    }
799
800    #[test]
801    fn test_icmp_time_exceeded_with_tcp() {
802        // ICMP Time Exceeded with embedded TCP packet
803        // Note: ICMP error messages typically only include first 8 bytes of transport header
804        let data = vec![
805            // Ethernet header
806            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
807            // Outer IPv4 header
808            0x45, 0x00, 0x00, 0x4c, // total len = 76 (longer to include full embedded packet)
809            0x00, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 10, 0, 0, 1, // src: router
810            192, 168, 1, 100, // dst: original sender
811            // ICMP Time Exceeded
812            0x0b, // type = 11 (time exceeded)
813            0x00, // code = 0 (TTL exceeded)
814            0x00, 0x00, // checksum
815            0x00, 0x00, 0x00, 0x00, // unused
816            // Embedded original IPv4 header (20 bytes)
817            0x45, 0x00, 0x00, 0x28, // total len = 40 (20 IP + 20 TCP)
818            0x12, 0x34, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, // ttl=1, proto=TCP
819            192, 168, 1, 100, // src
820            93, 184, 216, 34, // dst
821            // Embedded TCP header (full 20 bytes for proper parsing)
822            0x04, 0xd2, // sport = 1234
823            0x00, 0x50, // dport = 80 (HTTP)
824            0x00, 0x00, 0x00, 0x01, // seq
825            0x00, 0x00, 0x00, 0x00, // ack
826            0x50, 0x02, 0x20, 0x00, // data offset=5, flags=SYN, window
827            0x00, 0x00, // checksum
828            0x00, 0x00, // urgent pointer
829        ];
830
831        let mut packet = Packet::from_bytes(data);
832        packet.parse().unwrap();
833
834        // Should have: Ethernet, IP (outer), ICMP, IP (embedded), TCP (embedded)
835        assert_eq!(packet.layer_count(), 5);
836
837        // Verify layers exist
838        assert!(packet.icmp().is_some());
839        assert!(packet.tcp().is_some());
840
841        // Verify ICMP type
842        let icmp = packet.icmp().unwrap();
843        assert_eq!(icmp.icmp_type(packet.as_bytes()).unwrap(), 11);
844    }
845
846    #[test]
847    fn test_icmp_with_extensions() {
848        // ICMP Time Exceeded with RFC 4884 extensions (Interface Information)
849        let data = vec![
850            // Ethernet header
851            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // dst
852            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // src
853            0x08, 0x00, // ethertype = IPv4
854            // IPv4 header (outer)
855            0x45, 0x00, 0x00, 0xa0, // version, ihl, tos, total_length (160 bytes)
856            0x00, 0x01, 0x00, 0x00, // id, flags, fragment offset
857            0x40, 0x01, 0x00, 0x00, // ttl=64, proto=ICMP, checksum
858            0xc0, 0xa8, 0x01, 0x01, // src = 192.168.1.1
859            0xc0, 0xa8, 0x01, 0x64, // dst = 192.168.1.100
860            // ICMP header
861            0x0b, // type = Time Exceeded
862            0x00, // code = TTL exceeded
863            0x00, 0x00, // checksum (will be wrong, but OK for parsing test)
864            0x00, 0x00, 0x00, 0x00, // unused
865            // Embedded IP packet (padded to 128 bytes per RFC 4884)
866            0x45, 0x00, 0x00, 0x3c, // Embedded IPv4 header
867            0x12, 0x34, 0x40, 0x00, // id, flags
868            0x01, 0x11, 0x00, 0x00, // ttl=1, proto=UDP, checksum
869            0xc0, 0xa8, 0x01, 0x64, // src = 192.168.1.100
870            0x08, 0x08, 0x08, 0x08, // dst = 8.8.8.8
871            // Embedded UDP header (first 8 bytes)
872            0x04, 0xd2, 0x00, 0x35, // sport=1234, dport=53 (DNS)
873            0x00, 0x28, 0x00, 0x00, // length, checksum
874            // Padding to reach 128 bytes total (from start of embedded IP)
875            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
876            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
877            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
878            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
879            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
880            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
881            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
882            // ICMP Extension Header (4 bytes)
883            0x20, 0x00, // version=2, reserved=0
884            0x00, 0x00, // checksum (simplified, would need proper calculation)
885            // ICMP Extension Object - Interface Information (classnum=2)
886            0x00, 0x10, // length = 16 bytes
887            0x02, // classnum = Interface Information
888            0x19, // flags: has_ifindex=1, has_ipaddr=1, has_mtu=1
889            0x00, 0x00, 0x00, 0x02, // ifindex = 2
890            0x00, 0x01, 0x00, 0x00, // AFI = IPv4 (1), reserved
891            0xc0, 0xa8, 0x01, 0x01, // IP = 192.168.1.1
892        ];
893
894        let mut packet = Packet::from_bytes(data.clone());
895        packet.parse().unwrap();
896
897        // Verify ICMP layer exists
898        assert!(packet.icmp().is_some());
899        let icmp = packet.icmp().unwrap();
900
901        // Verify ICMP type
902        assert_eq!(
903            icmp.icmp_type(packet.as_bytes()).unwrap(),
904            crate::layer::icmp::types::types::TIME_EXCEEDED
905        );
906
907        // Test extension detection
908        use crate::layer::icmp;
909        let icmp_offset = packet.layers()[1].end; // After outer IP
910        let icmp_payload = &data[icmp_offset + 8..]; // After ICMP header
911
912        assert!(icmp::has_extensions(
913            crate::layer::icmp::types::types::TIME_EXCEEDED,
914            icmp_payload.len()
915        ));
916
917        // Parse extension header
918        let ext_offset = icmp_offset + 8 + 128; // ICMP header + padded payload
919        if let Some((version, _checksum)) = icmp::parse_extension_header(&data, ext_offset) {
920            assert_eq!(version, 2);
921
922            // Parse extension object
923            let obj_offset = ext_offset + 4;
924            if let Some((len, classnum, _classtype, data_offset)) =
925                icmp::parse_extension_object(&data, obj_offset)
926            {
927                assert_eq!(len, 16);
928                assert_eq!(
929                    classnum,
930                    icmp::extensions::class_nums::INTERFACE_INFORMATION
931                );
932
933                // Parse Interface Information
934                let data_len = (len as usize) - 4; // Object length minus header
935                if let Some(info) = icmp::parse_interface_information(&data, data_offset, data_len)
936                {
937                    assert!(info.flags.has_ifindex);
938                    assert!(info.flags.has_ipaddr);
939                    assert_eq!(info.ifindex, Some(2));
940                    if let Some(icmp::IpAddr::V4(ip)) = info.ip_addr {
941                        assert_eq!(ip.to_string(), "192.168.1.1");
942                    } else {
943                        panic!("Expected IPv4 address");
944                    }
945                }
946            }
947        }
948    }
949}