Skip to main content

stackforge_core/layer/ipv6/
builder.rs

1//! IPv6 packet builder.
2//!
3//! Provides a fluent API for constructing IPv6 packet headers.
4//!
5//! # Example
6//!
7//! ```rust
8//! use stackforge_core::layer::ipv6::Ipv6Builder;
9//! use std::net::Ipv6Addr;
10//!
11//! let bytes = Ipv6Builder::new()
12//!     .src(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1))
13//!     .dst(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2))
14//!     .hop_limit(64)
15//!     .next_header(58) // ICMPv6
16//!     .build();
17//!
18//! assert_eq!(bytes.len(), 40); // IPv6 header only (no payload)
19//! ```
20
21use std::net::Ipv6Addr;
22
23use super::IPV6_HEADER_LEN;
24
25/// Builder for IPv6 packet headers.
26#[derive(Debug, Clone)]
27pub struct Ipv6Builder {
28    /// Traffic Class (8 bits, DSCP+ECN)
29    traffic_class: u8,
30    /// Flow Label (20 bits)
31    flow_label: u32,
32    /// Next Header protocol number
33    next_header: u8,
34    /// Hop Limit (analogous to TTL in IPv4)
35    hop_limit: u8,
36    /// Source IPv6 address
37    src: Ipv6Addr,
38    /// Destination IPv6 address
39    dst: Ipv6Addr,
40    /// Payload bytes (appended after the 40-byte header)
41    payload: Vec<u8>,
42    /// If true, automatically compute `payload_len` from `payload.len()`
43    auto_length: bool,
44    /// Manual override for payload length (ignored if `auto_length=true`)
45    payload_len_override: Option<u16>,
46}
47
48impl Default for Ipv6Builder {
49    fn default() -> Self {
50        Self {
51            traffic_class: 0,
52            flow_label: 0,
53            next_header: 59, // No next header
54            hop_limit: 64,
55            src: Ipv6Addr::UNSPECIFIED,
56            dst: Ipv6Addr::UNSPECIFIED,
57            payload: Vec::new(),
58            auto_length: true,
59            payload_len_override: None,
60        }
61    }
62}
63
64impl Ipv6Builder {
65    /// Create a new IPv6 builder with default values.
66    #[must_use]
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    // ========== Field Setters ==========
72
73    /// Set the source IPv6 address.
74    #[must_use]
75    pub fn src(mut self, src: Ipv6Addr) -> Self {
76        self.src = src;
77        self
78    }
79
80    /// Set the destination IPv6 address.
81    #[must_use]
82    pub fn dst(mut self, dst: Ipv6Addr) -> Self {
83        self.dst = dst;
84        self
85    }
86
87    /// Set the Hop Limit (equivalent to TTL in IPv4).
88    #[must_use]
89    pub fn hop_limit(mut self, hlim: u8) -> Self {
90        self.hop_limit = hlim;
91        self
92    }
93
94    /// Set the Traffic Class field.
95    #[must_use]
96    pub fn traffic_class(mut self, tc: u8) -> Self {
97        self.traffic_class = tc;
98        self
99    }
100
101    /// Set the Flow Label (20 bits; upper 12 bits are ignored).
102    #[must_use]
103    pub fn flow_label(mut self, fl: u32) -> Self {
104        self.flow_label = fl & 0x000F_FFFF;
105        self
106    }
107
108    /// Set the Next Header field.
109    #[must_use]
110    pub fn next_header(mut self, nh: u8) -> Self {
111        self.next_header = nh;
112        self
113    }
114
115    /// Set the payload bytes (appended after the 40-byte header).
116    pub fn payload<T: Into<Vec<u8>>>(mut self, data: T) -> Self {
117        self.payload = data.into();
118        self
119    }
120
121    /// Configure automatic payload length calculation (default: true).
122    #[must_use]
123    pub fn auto_length(mut self, auto: bool) -> Self {
124        self.auto_length = auto;
125        self
126    }
127
128    /// Manually override the payload length field.
129    ///
130    /// Only used when `auto_length` is false.
131    #[must_use]
132    pub fn payload_len(mut self, len: u16) -> Self {
133        self.payload_len_override = Some(len);
134        self.auto_length = false;
135        self
136    }
137
138    // ========== Size Calculation ==========
139
140    /// Get the header size (always 40 bytes for IPv6).
141    #[must_use]
142    pub fn header_size(&self) -> usize {
143        IPV6_HEADER_LEN
144    }
145
146    /// Get the total packet size (header + payload).
147    #[must_use]
148    pub fn packet_size(&self) -> usize {
149        IPV6_HEADER_LEN + self.payload.len()
150    }
151
152    // ========== Build Methods ==========
153
154    /// Build the IPv6 header + payload into a byte vector.
155    ///
156    /// # Byte layout:
157    /// - Byte 0: version (6) in high 4 bits | TC high nibble in low 4 bits
158    /// - Byte 1: TC low nibble in high 4 bits | FL bits 19-16 in low 4 bits
159    /// - Bytes 2-3: FL bits 15-0 (big-endian)
160    /// - Bytes 4-5: `payload_len` (big-endian)
161    /// - Byte 6: `next_header`
162    /// - Byte 7: `hop_limit`
163    /// - Bytes 8-23: source address (16 bytes)
164    /// - Bytes 24-39: destination address (16 bytes)
165    /// - Bytes 40+: payload
166    #[must_use]
167    pub fn build(&self) -> Vec<u8> {
168        let total = self.packet_size();
169        let mut buf = vec![0u8; total];
170
171        // Byte 0: version (6) high nibble | TC high nibble low nibble
172        buf[0] = 0x60 | ((self.traffic_class >> 4) & 0x0F);
173
174        // Byte 1: TC low nibble high nibble | FL bits 19-16 low nibble
175        buf[1] = ((self.traffic_class & 0x0F) << 4) | (((self.flow_label >> 16) & 0x0F) as u8);
176
177        // Bytes 2-3: FL bits 15-0
178        buf[2] = ((self.flow_label >> 8) & 0xFF) as u8;
179        buf[3] = (self.flow_label & 0xFF) as u8;
180
181        // Bytes 4-5: payload length
182        let plen: u16 = if self.auto_length {
183            self.payload.len() as u16
184        } else {
185            self.payload_len_override
186                .unwrap_or(self.payload.len() as u16)
187        };
188        buf[4] = (plen >> 8) as u8;
189        buf[5] = (plen & 0xFF) as u8;
190
191        // Byte 6: next header
192        buf[6] = self.next_header;
193
194        // Byte 7: hop limit
195        buf[7] = self.hop_limit;
196
197        // Bytes 8-23: source address
198        buf[8..24].copy_from_slice(&self.src.octets());
199
200        // Bytes 24-39: destination address
201        buf[24..40].copy_from_slice(&self.dst.octets());
202
203        // Bytes 40+: payload
204        if !self.payload.is_empty() {
205            buf[40..40 + self.payload.len()].copy_from_slice(&self.payload);
206        }
207
208        buf
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn test_ipv6_builder_basic() {
218        let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
219        let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
220
221        let bytes = Ipv6Builder::new()
222            .src(src)
223            .dst(dst)
224            .hop_limit(64)
225            .next_header(58)
226            .build();
227
228        assert_eq!(bytes.len(), 40);
229        // Version = 6
230        assert_eq!((bytes[0] >> 4) & 0x0F, 6);
231        // Next header = 58 (ICMPv6)
232        assert_eq!(bytes[6], 58);
233        // Hop limit = 64
234        assert_eq!(bytes[7], 64);
235        // Source address
236        let mut src_bytes = [0u8; 16];
237        src_bytes.copy_from_slice(&bytes[8..24]);
238        assert_eq!(Ipv6Addr::from(src_bytes), src);
239        // Destination address
240        let mut dst_bytes = [0u8; 16];
241        dst_bytes.copy_from_slice(&bytes[24..40]);
242        assert_eq!(Ipv6Addr::from(dst_bytes), dst);
243    }
244
245    #[test]
246    fn test_ipv6_builder_with_payload() {
247        let payload = b"hello world";
248        let bytes = Ipv6Builder::new()
249            .src(Ipv6Addr::LOCALHOST)
250            .dst(Ipv6Addr::LOCALHOST)
251            .next_header(59)
252            .payload(payload.as_ref())
253            .build();
254
255        assert_eq!(bytes.len(), 40 + payload.len());
256        // Payload length in header
257        let plen = u16::from_be_bytes([bytes[4], bytes[5]]);
258        assert_eq!(plen as usize, payload.len());
259        // Payload bytes at offset 40
260        assert_eq!(&bytes[40..], payload.as_ref());
261    }
262
263    #[test]
264    fn test_ipv6_traffic_class() {
265        let bytes = Ipv6Builder::new().traffic_class(0xAB).build();
266
267        // Byte 0: 0x60 | (0xAB >> 4 = 0x0A) = 0x6A
268        assert_eq!(bytes[0], 0x6A);
269        // Byte 1: (0xAB & 0x0F = 0x0B) << 4 = 0xB0
270        assert_eq!(bytes[1] & 0xF0, 0xB0);
271        // Reconstruct TC
272        let tc = ((bytes[0] & 0x0F) << 4) | ((bytes[1] >> 4) & 0x0F);
273        assert_eq!(tc, 0xAB);
274    }
275
276    #[test]
277    fn test_ipv6_flow_label() {
278        let bytes = Ipv6Builder::new().flow_label(0x12345).build();
279
280        // FL bits 19-16 in byte 1 low nibble
281        assert_eq!(bytes[1] & 0x0F, 0x01);
282        // FL bits 15-8 in byte 2
283        assert_eq!(bytes[2], 0x23);
284        // FL bits 7-0 in byte 3
285        assert_eq!(bytes[3], 0x45);
286        // Reconstruct FL
287        let fl = ((bytes[1] as u32 & 0x0F) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32);
288        assert_eq!(fl, 0x12345);
289    }
290
291    #[test]
292    fn test_ipv6_manual_payload_len() {
293        let bytes = Ipv6Builder::new()
294            .payload(vec![0u8; 20])
295            .payload_len(100) // Override with manual value
296            .build();
297
298        let plen = u16::from_be_bytes([bytes[4], bytes[5]]);
299        assert_eq!(plen, 100);
300    }
301
302    #[test]
303    fn test_ipv6_header_size() {
304        let builder = Ipv6Builder::new();
305        assert_eq!(builder.header_size(), 40);
306    }
307}