stackforge_core/layer/icmpv6/
builder.rs1use std::net::Ipv6Addr;
22
23use super::{ICMPV6_MIN_HEADER_LEN, icmpv6_checksum, offsets, types};
24
25#[derive(Debug, Clone)]
27pub struct Icmpv6Builder {
28 icmpv6_type: u8,
30 code: u8,
32 checksum: Option<u16>,
34 id: Option<u16>,
36 seq: Option<u16>,
38 target: Option<Ipv6Addr>,
40 mtu: Option<u32>,
42 type_specific: [u8; 4],
44 payload: Vec<u8>,
46 auto_checksum: bool,
48 src_ip: Option<Ipv6Addr>,
50 dst_ip: Option<Ipv6Addr>,
52}
53
54impl Default for Icmpv6Builder {
55 fn default() -> Self {
56 Self {
57 icmpv6_type: types::ECHO_REQUEST,
58 code: 0,
59 checksum: None,
60 id: None,
61 seq: None,
62 target: None,
63 mtu: None,
64 type_specific: [0; 4],
65 payload: Vec::new(),
66 auto_checksum: true,
67 src_ip: None,
68 dst_ip: None,
69 }
70 }
71}
72
73impl Icmpv6Builder {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn echo_request(id: u16, seq: u16) -> Self {
87 let mut b = Self::new();
88 b.icmpv6_type = types::ECHO_REQUEST;
89 b.code = 0;
90 b.id = Some(id);
91 b.seq = Some(seq);
92 b.type_specific[0] = (id >> 8) as u8;
93 b.type_specific[1] = (id & 0xFF) as u8;
94 b.type_specific[2] = (seq >> 8) as u8;
95 b.type_specific[3] = (seq & 0xFF) as u8;
96 b
97 }
98
99 pub fn echo_reply(id: u16, seq: u16) -> Self {
105 let mut b = Self::echo_request(id, seq);
106 b.icmpv6_type = types::ECHO_REPLY;
107 b
108 }
109
110 pub fn neighbor_solicitation(target: Ipv6Addr) -> Self {
115 let mut b = Self::new();
116 b.icmpv6_type = types::NEIGHBOR_SOLICIT;
117 b.code = 0;
118 b.target = Some(target);
119 b
121 }
122
123 pub fn neighbor_advertisement(target: Ipv6Addr) -> Self {
128 let mut b = Self::new();
129 b.icmpv6_type = types::NEIGHBOR_ADVERT;
130 b.code = 0;
131 b.target = Some(target);
132 b.type_specific[0] = 0x60; b
136 }
137
138 pub fn router_solicitation() -> Self {
140 let mut b = Self::new();
141 b.icmpv6_type = types::ROUTER_SOLICIT;
142 b.code = 0;
143 b
145 }
146
147 pub fn router_advertisement() -> Self {
149 let mut b = Self::new();
150 b.icmpv6_type = types::ROUTER_ADVERT;
151 b.code = 0;
152 b
155 }
156
157 pub fn dest_unreachable(code: u8, payload: Vec<u8>) -> Self {
163 let mut b = Self::new();
164 b.icmpv6_type = types::DEST_UNREACH;
165 b.code = code;
166 b.payload = payload;
167 b
169 }
170
171 pub fn time_exceeded(code: u8, payload: Vec<u8>) -> Self {
177 let mut b = Self::new();
178 b.icmpv6_type = types::TIME_EXCEEDED;
179 b.code = code;
180 b.payload = payload;
181 b
183 }
184
185 pub fn pkt_too_big(mtu: u32, payload: Vec<u8>) -> Self {
191 let mut b = Self::new();
192 b.icmpv6_type = types::PKT_TOO_BIG;
193 b.code = 0;
194 b.mtu = Some(mtu);
195 b.type_specific = [
196 (mtu >> 24) as u8,
197 ((mtu >> 16) & 0xFF) as u8,
198 ((mtu >> 8) & 0xFF) as u8,
199 (mtu & 0xFF) as u8,
200 ];
201 b.payload = payload;
202 b
203 }
204
205 pub fn icmpv6_type(mut self, t: u8) -> Self {
209 self.icmpv6_type = t;
210 self
211 }
212
213 pub fn code(mut self, c: u8) -> Self {
215 self.code = c;
216 self
217 }
218
219 pub fn checksum(mut self, csum: u16) -> Self {
221 self.checksum = Some(csum);
222 self.auto_checksum = false;
223 self
224 }
225
226 pub fn chksum(self, csum: u16) -> Self {
228 self.checksum(csum)
229 }
230
231 pub fn payload<T: Into<Vec<u8>>>(mut self, data: T) -> Self {
233 self.payload = data.into();
234 self
235 }
236
237 pub fn enable_auto_checksum(mut self) -> Self {
239 self.auto_checksum = true;
240 self.checksum = None;
241 self
242 }
243
244 pub fn disable_auto_checksum(mut self) -> Self {
246 self.auto_checksum = false;
247 self
248 }
249
250 pub fn set_src_ip(mut self, src: Ipv6Addr) -> Self {
252 self.src_ip = Some(src);
253 self
254 }
255
256 pub fn set_dst_ip(mut self, dst: Ipv6Addr) -> Self {
258 self.dst_ip = Some(dst);
259 self
260 }
261
262 pub fn packet_size(&self) -> usize {
266 let base = ICMPV6_MIN_HEADER_LEN;
267 let extra = match self.icmpv6_type {
268 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT => 16, _ => 0,
270 };
271 base + extra + self.payload.len()
272 }
273
274 pub fn header_size(&self) -> usize {
276 ICMPV6_MIN_HEADER_LEN
277 }
278
279 pub fn build(&self) -> Vec<u8> {
290 let total = self.packet_size();
291 let mut buf = vec![0u8; total];
292
293 buf[offsets::TYPE] = self.icmpv6_type;
295
296 buf[offsets::CODE] = self.code;
298
299 buf[offsets::CHECKSUM] = 0;
301 buf[offsets::CHECKSUM + 1] = 0;
302
303 buf[4..8].copy_from_slice(&self.type_specific);
305
306 let mut offset = 8;
307
308 match self.icmpv6_type {
310 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT | types::REDIRECT => {
311 if let Some(target) = self.target {
312 if offset + 16 <= buf.len() {
313 buf[offset..offset + 16].copy_from_slice(&target.octets());
314 offset += 16;
315 }
316 }
317 }
318 _ => {}
319 }
320
321 if !self.payload.is_empty() && offset + self.payload.len() <= buf.len() {
323 buf[offset..offset + self.payload.len()].copy_from_slice(&self.payload);
324 }
325
326 if self.auto_checksum {
328 if let (Some(src), Some(dst)) = (self.src_ip, self.dst_ip) {
329 let csum = icmpv6_checksum(src, dst, &buf);
330 buf[offsets::CHECKSUM] = (csum >> 8) as u8;
331 buf[offsets::CHECKSUM + 1] = (csum & 0xFF) as u8;
332 }
333 } else if let Some(csum) = self.checksum {
335 buf[offsets::CHECKSUM] = (csum >> 8) as u8;
336 buf[offsets::CHECKSUM + 1] = (csum & 0xFF) as u8;
337 }
338
339 buf
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_echo_request_builder() {
349 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
350 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
351
352 let bytes = Icmpv6Builder::echo_request(0x1234, 5)
353 .set_src_ip(src)
354 .set_dst_ip(dst)
355 .payload(b"ping".as_ref())
356 .build();
357
358 assert_eq!(bytes[0], types::ECHO_REQUEST);
359 assert_eq!(bytes[1], 0); assert_eq!(u16::from_be_bytes([bytes[4], bytes[5]]), 0x1234); assert_eq!(u16::from_be_bytes([bytes[6], bytes[7]]), 5); assert_eq!(&bytes[8..], b"ping");
363
364 let chksum = u16::from_be_bytes([bytes[2], bytes[3]]);
366 assert_ne!(chksum, 0);
367 }
368
369 #[test]
370 fn test_echo_reply_builder() {
371 let bytes = Icmpv6Builder::echo_reply(0x5678, 10).build();
372
373 assert_eq!(bytes[0], types::ECHO_REPLY);
374 assert_eq!(u16::from_be_bytes([bytes[4], bytes[5]]), 0x5678);
375 assert_eq!(u16::from_be_bytes([bytes[6], bytes[7]]), 10);
376 }
377
378 #[test]
379 fn test_neighbor_solicitation_builder() {
380 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
381 let bytes = Icmpv6Builder::neighbor_solicitation(target).build();
382
383 assert_eq!(bytes[0], types::NEIGHBOR_SOLICIT);
384 assert_eq!(bytes.len(), 24); let mut addr_bytes = [0u8; 16];
387 addr_bytes.copy_from_slice(&bytes[8..24]);
388 assert_eq!(Ipv6Addr::from(addr_bytes), target);
389 }
390
391 #[test]
392 fn test_neighbor_advertisement_builder() {
393 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
394 let bytes = Icmpv6Builder::neighbor_advertisement(target).build();
395
396 assert_eq!(bytes[0], types::NEIGHBOR_ADVERT);
397 assert_eq!(bytes.len(), 24); assert_eq!(bytes[4] & 0x60, 0x60);
400 }
401
402 #[test]
403 fn test_router_solicitation_builder() {
404 let bytes = Icmpv6Builder::router_solicitation().build();
405 assert_eq!(bytes[0], types::ROUTER_SOLICIT);
406 assert_eq!(bytes.len(), 8);
407 }
408
409 #[test]
410 fn test_pkt_too_big_builder() {
411 let payload = vec![0xAAu8; 20];
412 let bytes = Icmpv6Builder::pkt_too_big(1500, payload.clone()).build();
413
414 assert_eq!(bytes[0], types::PKT_TOO_BIG);
415 assert_eq!(bytes[1], 0); let mtu = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
417 assert_eq!(mtu, 1500);
418 assert_eq!(&bytes[8..], payload.as_slice());
419 }
420
421 #[test]
422 fn test_dest_unreachable_builder() {
423 let payload = vec![0u8; 10];
424 let bytes = Icmpv6Builder::dest_unreachable(3, payload.clone()).build();
425
426 assert_eq!(bytes[0], types::DEST_UNREACH);
427 assert_eq!(bytes[1], 3); assert_eq!(&bytes[8..], payload.as_slice());
429 }
430
431 #[test]
432 fn test_time_exceeded_builder() {
433 let bytes = Icmpv6Builder::time_exceeded(0, vec![]).build();
434 assert_eq!(bytes[0], types::TIME_EXCEEDED);
435 assert_eq!(bytes[1], 0);
436 }
437
438 #[test]
439 fn test_manual_checksum() {
440 let bytes = Icmpv6Builder::echo_request(1, 1).checksum(0xABCD).build();
441
442 assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 0xABCD);
443 }
444
445 #[test]
446 fn test_checksum_verification() {
447 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
448 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
449
450 let bytes = Icmpv6Builder::echo_request(0x1234, 1)
451 .set_src_ip(src)
452 .set_dst_ip(dst)
453 .build();
454
455 assert!(super::super::verify_icmpv6_checksum(src, dst, &bytes));
457 }
458
459 #[test]
460 fn test_no_checksum_without_src_dst() {
461 let bytes = Icmpv6Builder::echo_request(1, 1).build();
462 let chksum = u16::from_be_bytes([bytes[2], bytes[3]]);
464 assert_eq!(chksum, 0);
465 }
466
467 #[test]
468 fn test_packet_size() {
469 let builder = Icmpv6Builder::echo_request(1, 1).payload(vec![0u8; 10]);
470 assert_eq!(builder.packet_size(), 18); let ns_builder = Icmpv6Builder::neighbor_solicitation(Ipv6Addr::LOCALHOST);
473 assert_eq!(ns_builder.packet_size(), 24); }
475}