Skip to main content

stackforge_core/layer/ipv6/
mod.rs

1//! IPv6 layer implementation.
2//!
3//! This module provides types and functions for working with IPv6 packets,
4//! including parsing, field access, and next-header resolution.
5
6pub mod builder;
7pub mod ext_headers;
8
9pub use builder::Ipv6Builder;
10pub use ext_headers::{ExtHeader, ExtHeaderKind, parse_ext_headers};
11
12use crate::layer::field::{Field, FieldError, FieldValue};
13use crate::layer::{Layer, LayerIndex, LayerKind};
14use std::net::Ipv6Addr;
15
16/// IPv6 fixed header length (40 bytes).
17pub const IPV6_HEADER_LEN: usize = 40;
18
19/// Field offsets within the IPv6 header.
20pub mod offsets {
21    /// Version (4 bits) | Traffic Class (8 bits) | Flow Label (20 bits) - bytes 0-3
22    pub const VERSION_TC_FL: usize = 0;
23    /// Payload length (16 bits)
24    pub const PAYLOAD_LEN: usize = 4;
25    /// Next Header (8 bits)
26    pub const NEXT_HEADER: usize = 6;
27    /// Hop Limit (8 bits)
28    pub const HOP_LIMIT: usize = 7;
29    /// Source address (128 bits = 16 bytes)
30    pub const SRC: usize = 8;
31    /// Destination address (128 bits = 16 bytes)
32    pub const DST: usize = 24;
33}
34
35/// A view into an IPv6 packet header.
36///
37/// Uses the "Lazy Zero-Copy View" pattern: holds only layer boundaries
38/// and reads fields directly from the buffer on demand.
39#[derive(Debug, Clone)]
40pub struct Ipv6Layer {
41    pub index: LayerIndex,
42}
43
44impl Ipv6Layer {
45    /// Create a new IPv6 layer view with specified bounds.
46    #[inline]
47    #[must_use]
48    pub const fn new(start: usize, end: usize) -> Self {
49        Self {
50            index: LayerIndex::new(LayerKind::Ipv6, start, end),
51        }
52    }
53
54    /// Create a layer at offset 0 with IPv6 header length.
55    #[inline]
56    #[must_use]
57    pub const fn at_start() -> Self {
58        Self::new(0, IPV6_HEADER_LEN)
59    }
60
61    /// Create a layer at the specified offset.
62    #[inline]
63    #[must_use]
64    pub const fn at_offset(offset: usize) -> Self {
65        Self::new(offset, offset + IPV6_HEADER_LEN)
66    }
67
68    // ========== Field Readers ==========
69
70    /// Read the IP version (should be 6).
71    #[inline]
72    pub fn version(&self, buf: &[u8]) -> Result<u8, FieldError> {
73        let slice = self.index.slice(buf);
74        if slice.is_empty() {
75            return Err(FieldError::BufferTooShort {
76                offset: self.index.start + offsets::VERSION_TC_FL,
77                need: 1,
78                have: 0,
79            });
80        }
81        Ok((slice[0] >> 4) & 0x0F)
82    }
83
84    /// Read the Traffic Class field (8 bits).
85    #[inline]
86    pub fn traffic_class(&self, buf: &[u8]) -> Result<u8, FieldError> {
87        let slice = self.index.slice(buf);
88        if slice.len() < 2 {
89            return Err(FieldError::BufferTooShort {
90                offset: self.index.start + offsets::VERSION_TC_FL,
91                need: 2,
92                have: slice.len(),
93            });
94        }
95        Ok(((slice[0] & 0x0F) << 4) | ((slice[1] >> 4) & 0x0F))
96    }
97
98    /// Read the Flow Label field (20 bits).
99    #[inline]
100    pub fn flow_label(&self, buf: &[u8]) -> Result<u32, FieldError> {
101        let slice = self.index.slice(buf);
102        if slice.len() < 4 {
103            return Err(FieldError::BufferTooShort {
104                offset: self.index.start + offsets::VERSION_TC_FL,
105                need: 4,
106                have: slice.len(),
107            });
108        }
109        let fl =
110            ((u32::from(slice[1]) & 0x0F) << 16) | (u32::from(slice[2]) << 8) | u32::from(slice[3]);
111        Ok(fl)
112    }
113
114    /// Read the Payload Length field (16 bits).
115    #[inline]
116    pub fn payload_len(&self, buf: &[u8]) -> Result<u16, FieldError> {
117        let slice = self.index.slice(buf);
118        if slice.len() < offsets::PAYLOAD_LEN + 2 {
119            return Err(FieldError::BufferTooShort {
120                offset: self.index.start + offsets::PAYLOAD_LEN,
121                need: 2,
122                have: slice.len().saturating_sub(offsets::PAYLOAD_LEN),
123            });
124        }
125        Ok(u16::from_be_bytes([
126            slice[offsets::PAYLOAD_LEN],
127            slice[offsets::PAYLOAD_LEN + 1],
128        ]))
129    }
130
131    /// Read the Next Header field (8 bits).
132    #[inline]
133    pub fn next_header(&self, buf: &[u8]) -> Result<u8, FieldError> {
134        let slice = self.index.slice(buf);
135        if slice.len() <= offsets::NEXT_HEADER {
136            return Err(FieldError::BufferTooShort {
137                offset: self.index.start + offsets::NEXT_HEADER,
138                need: 1,
139                have: slice.len().saturating_sub(offsets::NEXT_HEADER),
140            });
141        }
142        Ok(slice[offsets::NEXT_HEADER])
143    }
144
145    /// Read the Hop Limit field (8 bits).
146    #[inline]
147    pub fn hop_limit(&self, buf: &[u8]) -> Result<u8, FieldError> {
148        let slice = self.index.slice(buf);
149        if slice.len() <= offsets::HOP_LIMIT {
150            return Err(FieldError::BufferTooShort {
151                offset: self.index.start + offsets::HOP_LIMIT,
152                need: 1,
153                have: slice.len().saturating_sub(offsets::HOP_LIMIT),
154            });
155        }
156        Ok(slice[offsets::HOP_LIMIT])
157    }
158
159    /// Read the Source IPv6 address.
160    #[inline]
161    pub fn src(&self, buf: &[u8]) -> Result<Ipv6Addr, FieldError> {
162        Ipv6Addr::read(buf, self.index.start + offsets::SRC)
163    }
164
165    /// Read the Destination IPv6 address.
166    #[inline]
167    pub fn dst(&self, buf: &[u8]) -> Result<Ipv6Addr, FieldError> {
168        Ipv6Addr::read(buf, self.index.start + offsets::DST)
169    }
170
171    // ========== Field Writers ==========
172
173    /// Set the version field (high 4 bits of byte 0).
174    #[inline]
175    pub fn set_version(&self, buf: &mut [u8], version: u8) -> Result<(), FieldError> {
176        let offset = self.index.start + offsets::VERSION_TC_FL;
177        if buf.len() <= offset {
178            return Err(FieldError::BufferTooShort {
179                offset,
180                need: 1,
181                have: buf.len().saturating_sub(offset),
182            });
183        }
184        buf[offset] = (buf[offset] & 0x0F) | ((version & 0x0F) << 4);
185        Ok(())
186    }
187
188    /// Set the Traffic Class field.
189    #[inline]
190    pub fn set_traffic_class(&self, buf: &mut [u8], tc: u8) -> Result<(), FieldError> {
191        let offset = self.index.start + offsets::VERSION_TC_FL;
192        if buf.len() < offset + 2 {
193            return Err(FieldError::BufferTooShort {
194                offset,
195                need: 2,
196                have: buf.len().saturating_sub(offset),
197            });
198        }
199        // High nibble of TC goes into low nibble of byte 0
200        buf[offset] = (buf[offset] & 0xF0) | ((tc >> 4) & 0x0F);
201        // Low nibble of TC goes into high nibble of byte 1
202        buf[offset + 1] = (buf[offset + 1] & 0x0F) | ((tc & 0x0F) << 4);
203        Ok(())
204    }
205
206    /// Set the Flow Label field (20 bits).
207    #[inline]
208    pub fn set_flow_label(&self, buf: &mut [u8], fl: u32) -> Result<(), FieldError> {
209        let offset = self.index.start + offsets::VERSION_TC_FL;
210        if buf.len() < offset + 4 {
211            return Err(FieldError::BufferTooShort {
212                offset,
213                need: 4,
214                have: buf.len().saturating_sub(offset),
215            });
216        }
217        // Byte 1: keep high nibble (TC low nibble), set low nibble (FL bits 19-16)
218        buf[offset + 1] = (buf[offset + 1] & 0xF0) | (((fl >> 16) & 0x0F) as u8);
219        // Bytes 2-3: FL bits 15-0
220        buf[offset + 2] = ((fl >> 8) & 0xFF) as u8;
221        buf[offset + 3] = (fl & 0xFF) as u8;
222        Ok(())
223    }
224
225    /// Set the Payload Length field.
226    #[inline]
227    pub fn set_payload_len(&self, buf: &mut [u8], len: u16) -> Result<(), FieldError> {
228        let offset = self.index.start + offsets::PAYLOAD_LEN;
229        if buf.len() < offset + 2 {
230            return Err(FieldError::BufferTooShort {
231                offset,
232                need: 2,
233                have: buf.len().saturating_sub(offset),
234            });
235        }
236        buf[offset..offset + 2].copy_from_slice(&len.to_be_bytes());
237        Ok(())
238    }
239
240    /// Set the Next Header field.
241    #[inline]
242    pub fn set_next_header(&self, buf: &mut [u8], nh: u8) -> Result<(), FieldError> {
243        let offset = self.index.start + offsets::NEXT_HEADER;
244        if buf.len() <= offset {
245            return Err(FieldError::BufferTooShort {
246                offset,
247                need: 1,
248                have: buf.len().saturating_sub(offset),
249            });
250        }
251        buf[offset] = nh;
252        Ok(())
253    }
254
255    /// Set the Hop Limit field.
256    #[inline]
257    pub fn set_hop_limit(&self, buf: &mut [u8], hlim: u8) -> Result<(), FieldError> {
258        let offset = self.index.start + offsets::HOP_LIMIT;
259        if buf.len() <= offset {
260            return Err(FieldError::BufferTooShort {
261                offset,
262                need: 1,
263                have: buf.len().saturating_sub(offset),
264            });
265        }
266        buf[offset] = hlim;
267        Ok(())
268    }
269
270    /// Set the source IPv6 address.
271    #[inline]
272    pub fn set_src(&self, buf: &mut [u8], src: Ipv6Addr) -> Result<(), FieldError> {
273        src.write(buf, self.index.start + offsets::SRC)
274    }
275
276    /// Set the destination IPv6 address.
277    #[inline]
278    pub fn set_dst(&self, buf: &mut [u8], dst: Ipv6Addr) -> Result<(), FieldError> {
279        dst.write(buf, self.index.start + offsets::DST)
280    }
281
282    // ========== Dynamic Field Access ==========
283
284    /// Get a field value by name.
285    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
286        match name {
287            "version" => Some(self.version(buf).map(FieldValue::U8)),
288            "tc" | "traffic_class" => Some(self.traffic_class(buf).map(FieldValue::U8)),
289            "fl" | "flow_label" => Some(self.flow_label(buf).map(FieldValue::U32)),
290            "plen" | "payload_len" => Some(self.payload_len(buf).map(FieldValue::U16)),
291            "nh" | "next_header" => Some(self.next_header(buf).map(FieldValue::U8)),
292            "hlim" | "hop_limit" => Some(self.hop_limit(buf).map(FieldValue::U8)),
293            "src" => Some(self.src(buf).map(FieldValue::Ipv6)),
294            "dst" => Some(self.dst(buf).map(FieldValue::Ipv6)),
295            _ => None,
296        }
297    }
298
299    /// Set a field value by name.
300    pub fn set_field(
301        &self,
302        buf: &mut [u8],
303        name: &str,
304        value: FieldValue,
305    ) -> Option<Result<(), FieldError>> {
306        match (name, value) {
307            ("version", FieldValue::U8(v)) => Some(self.set_version(buf, v)),
308            ("tc" | "traffic_class", FieldValue::U8(v)) => Some(self.set_traffic_class(buf, v)),
309            ("fl" | "flow_label", FieldValue::U32(v)) => Some(self.set_flow_label(buf, v)),
310            ("plen" | "payload_len", FieldValue::U16(v)) => Some(self.set_payload_len(buf, v)),
311            ("nh" | "next_header", FieldValue::U8(v)) => Some(self.set_next_header(buf, v)),
312            ("hlim" | "hop_limit", FieldValue::U8(v)) => Some(self.set_hop_limit(buf, v)),
313            ("src", FieldValue::Ipv6(v)) => Some(self.set_src(buf, v)),
314            ("dst", FieldValue::Ipv6(v)) => Some(self.set_dst(buf, v)),
315            _ => None,
316        }
317    }
318
319    /// Get the list of field names for this layer.
320    #[must_use]
321    pub fn field_names() -> &'static [&'static str] {
322        &["version", "tc", "fl", "plen", "nh", "hlim", "src", "dst"]
323    }
324
325    // ========== Utility Methods ==========
326
327    /// Generate a human-readable summary string.
328    #[must_use]
329    pub fn summary(&self, buf: &[u8]) -> String {
330        let src = self
331            .src(buf)
332            .map_or_else(|_| "?".to_string(), |a| a.to_string());
333        let dst = self
334            .dst(buf)
335            .map_or_else(|_| "?".to_string(), |a| a.to_string());
336        format!("IPv6 {src} > {dst}")
337    }
338
339    /// Get the header length (always 40 for IPv6 base header).
340    #[must_use]
341    pub fn header_len(&self, _buf: &[u8]) -> usize {
342        IPV6_HEADER_LEN
343    }
344
345    /// Determine the next layer kind based on next header value.
346    #[must_use]
347    pub fn next_layer(&self, buf: &[u8]) -> Option<LayerKind> {
348        use crate::layer::ipv4::protocol;
349        self.next_header(buf).ok().and_then(|nh| match nh {
350            protocol::TCP => Some(LayerKind::Tcp),
351            protocol::UDP => Some(LayerKind::Udp),
352            protocol::ICMPV6 => Some(LayerKind::Icmpv6),
353            _ => None,
354        })
355    }
356
357    /// Compute hash for packet matching (like Scapy's hashret).
358    #[must_use]
359    pub fn hashret(&self, buf: &[u8]) -> Vec<u8> {
360        let src = self.src(buf).map(|ip| ip.octets()).unwrap_or([0; 16]);
361        let dst = self.dst(buf).map(|ip| ip.octets()).unwrap_or([0; 16]);
362        let nh = self.next_header(buf).unwrap_or(0);
363
364        let mut result = Vec::with_capacity(17);
365        for i in 0..16 {
366            result.push(src[i] ^ dst[i]);
367        }
368        result.push(nh);
369        result
370    }
371
372    /// Check if this packet answers another (for `sr()` matching).
373    #[must_use]
374    pub fn answers(&self, buf: &[u8], other: &Ipv6Layer, other_buf: &[u8]) -> bool {
375        let self_src = self.src(buf).ok();
376        let self_dst = self.dst(buf).ok();
377        let other_src = other.src(other_buf).ok();
378        let other_dst = other.dst(other_buf).ok();
379
380        // Response src should match request dst
381        if self_src != other_dst {
382            return false;
383        }
384        // Response dst should match request src
385        if self_dst != other_src {
386            return false;
387        }
388        true
389    }
390}
391
392impl Layer for Ipv6Layer {
393    fn kind(&self) -> LayerKind {
394        LayerKind::Ipv6
395    }
396
397    fn summary(&self, data: &[u8]) -> String {
398        self.summary(data)
399    }
400
401    fn header_len(&self, data: &[u8]) -> usize {
402        self.header_len(data)
403    }
404
405    fn hashret(&self, data: &[u8]) -> Vec<u8> {
406        self.hashret(data)
407    }
408
409    fn answers(&self, data: &[u8], other: &Self, other_data: &[u8]) -> bool {
410        self.answers(data, other, other_data)
411    }
412
413    fn field_names(&self) -> &'static [&'static str] {
414        Self::field_names()
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421
422    fn make_ipv6_buf(src: Ipv6Addr, dst: Ipv6Addr, nh: u8) -> Vec<u8> {
423        let mut buf = vec![0u8; 40];
424        // Version = 6, TC = 0, FL = 0
425        buf[0] = 0x60;
426        buf[1] = 0x00;
427        buf[2] = 0x00;
428        buf[3] = 0x00;
429        // Payload length = 0
430        buf[4] = 0;
431        buf[5] = 0;
432        // Next header
433        buf[6] = nh;
434        // Hop limit
435        buf[7] = 64;
436        // Source address
437        buf[8..24].copy_from_slice(&src.octets());
438        // Destination address
439        buf[24..40].copy_from_slice(&dst.octets());
440        buf
441    }
442
443    #[test]
444    fn test_ipv6_version() {
445        let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
446        let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
447        let buf = make_ipv6_buf(src, dst, 58);
448        let layer = Ipv6Layer::at_start();
449        assert_eq!(layer.version(&buf).unwrap(), 6);
450    }
451
452    #[test]
453    fn test_ipv6_addresses() {
454        let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
455        let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
456        let buf = make_ipv6_buf(src, dst, 58);
457        let layer = Ipv6Layer::at_start();
458        assert_eq!(layer.src(&buf).unwrap(), src);
459        assert_eq!(layer.dst(&buf).unwrap(), dst);
460    }
461
462    #[test]
463    fn test_ipv6_next_header() {
464        let src = Ipv6Addr::LOCALHOST;
465        let dst = Ipv6Addr::LOCALHOST;
466        let buf = make_ipv6_buf(src, dst, 58); // ICMPv6
467        let layer = Ipv6Layer::at_start();
468        assert_eq!(layer.next_header(&buf).unwrap(), 58);
469    }
470
471    #[test]
472    fn test_ipv6_hop_limit() {
473        let src = Ipv6Addr::LOCALHOST;
474        let dst = Ipv6Addr::LOCALHOST;
475        let buf = make_ipv6_buf(src, dst, 6);
476        let layer = Ipv6Layer::at_start();
477        assert_eq!(layer.hop_limit(&buf).unwrap(), 64);
478    }
479
480    #[test]
481    fn test_ipv6_traffic_class() {
482        let mut buf = vec![0u8; 40];
483        // Version=6, TC=0xAB, FL=0
484        buf[0] = 0x60 | ((0xABu8 >> 4) & 0x0F); // 0x6A
485        buf[1] = ((0xABu8 & 0x0F) << 4) | 0x00; // 0xB0
486        let layer = Ipv6Layer::at_start();
487        assert_eq!(layer.traffic_class(&buf).unwrap(), 0xAB);
488    }
489
490    #[test]
491    fn test_ipv6_flow_label() {
492        let mut buf = vec![0u8; 40];
493        // Version=6, TC=0, FL=0x12345
494        buf[0] = 0x60;
495        buf[1] = 0x01; // FL bits 19-16 = 1
496        buf[2] = 0x23; // FL bits 15-8
497        buf[3] = 0x45; // FL bits 7-0
498        let layer = Ipv6Layer::at_start();
499        assert_eq!(layer.flow_label(&buf).unwrap(), 0x12345);
500    }
501
502    #[test]
503    fn test_ipv6_summary() {
504        let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
505        let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
506        let buf = make_ipv6_buf(src, dst, 58);
507        let layer = Ipv6Layer::at_start();
508        let summary = layer.summary(&buf);
509        assert!(summary.contains("IPv6"));
510        assert!(summary.contains("2001:db8::1"));
511        assert!(summary.contains("2001:db8::2"));
512    }
513
514    #[test]
515    fn test_ipv6_get_field() {
516        let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
517        let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
518        let buf = make_ipv6_buf(src, dst, 17); // UDP
519        let layer = Ipv6Layer::at_start();
520
521        if let Some(Ok(FieldValue::U8(v))) = layer.get_field(&buf, "version") {
522            assert_eq!(v, 6);
523        } else {
524            panic!("expected version = 6");
525        }
526
527        if let Some(Ok(FieldValue::U8(v))) = layer.get_field(&buf, "nh") {
528            assert_eq!(v, 17); // UDP
529        } else {
530            panic!("expected nh = 17");
531        }
532    }
533
534    #[test]
535    fn test_ipv6_set_field() {
536        let src = Ipv6Addr::LOCALHOST;
537        let dst = Ipv6Addr::LOCALHOST;
538        let mut buf = make_ipv6_buf(src, dst, 6);
539        let layer = Ipv6Layer::at_start();
540
541        // Change hop limit
542        layer
543            .set_field(&mut buf, "hlim", FieldValue::U8(128))
544            .unwrap()
545            .unwrap();
546        assert_eq!(layer.hop_limit(&buf).unwrap(), 128);
547    }
548
549    #[test]
550    fn test_ipv6_set_addresses() {
551        let src = Ipv6Addr::LOCALHOST;
552        let dst = Ipv6Addr::LOCALHOST;
553        let mut buf = make_ipv6_buf(src, dst, 6);
554        let layer = Ipv6Layer::at_start();
555
556        let new_src = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
557        layer.set_src(&mut buf, new_src).unwrap();
558        assert_eq!(layer.src(&buf).unwrap(), new_src);
559    }
560
561    #[test]
562    fn test_ipv6_payload_len() {
563        let src = Ipv6Addr::LOCALHOST;
564        let dst = Ipv6Addr::LOCALHOST;
565        let mut buf = make_ipv6_buf(src, dst, 58);
566        buf[4] = 0x00;
567        buf[5] = 0x28; // 40 bytes
568        let layer = Ipv6Layer::at_start();
569        assert_eq!(layer.payload_len(&buf).unwrap(), 40);
570    }
571
572    #[test]
573    fn test_ipv6_header_len() {
574        let buf = vec![0u8; 40];
575        let layer = Ipv6Layer::at_start();
576        assert_eq!(layer.header_len(&buf), IPV6_HEADER_LEN);
577        assert_eq!(IPV6_HEADER_LEN, 40);
578    }
579}