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