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 #[must_use]
76 pub fn new() -> Self {
77 Self::default()
78 }
79
80 #[must_use]
88 pub fn echo_request(id: u16, seq: u16) -> Self {
89 let mut b = Self::new();
90 b.icmpv6_type = types::ECHO_REQUEST;
91 b.code = 0;
92 b.id = Some(id);
93 b.seq = Some(seq);
94 b.type_specific[0] = (id >> 8) as u8;
95 b.type_specific[1] = (id & 0xFF) as u8;
96 b.type_specific[2] = (seq >> 8) as u8;
97 b.type_specific[3] = (seq & 0xFF) as u8;
98 b
99 }
100
101 #[must_use]
107 pub fn echo_reply(id: u16, seq: u16) -> Self {
108 let mut b = Self::echo_request(id, seq);
109 b.icmpv6_type = types::ECHO_REPLY;
110 b
111 }
112
113 #[must_use]
118 pub fn neighbor_solicitation(target: Ipv6Addr) -> Self {
119 let mut b = Self::new();
120 b.icmpv6_type = types::NEIGHBOR_SOLICIT;
121 b.code = 0;
122 b.target = Some(target);
123 b
125 }
126
127 #[must_use]
132 pub fn neighbor_advertisement(target: Ipv6Addr) -> Self {
133 let mut b = Self::new();
134 b.icmpv6_type = types::NEIGHBOR_ADVERT;
135 b.code = 0;
136 b.target = Some(target);
137 b.type_specific[0] = 0x60; b
141 }
142
143 #[must_use]
145 pub fn router_solicitation() -> Self {
146 let mut b = Self::new();
147 b.icmpv6_type = types::ROUTER_SOLICIT;
148 b.code = 0;
149 b
151 }
152
153 #[must_use]
155 pub fn router_advertisement() -> Self {
156 let mut b = Self::new();
157 b.icmpv6_type = types::ROUTER_ADVERT;
158 b.code = 0;
159 b
162 }
163
164 #[must_use]
170 pub fn dest_unreachable(code: u8, payload: Vec<u8>) -> Self {
171 let mut b = Self::new();
172 b.icmpv6_type = types::DEST_UNREACH;
173 b.code = code;
174 b.payload = payload;
175 b
177 }
178
179 #[must_use]
185 pub fn time_exceeded(code: u8, payload: Vec<u8>) -> Self {
186 let mut b = Self::new();
187 b.icmpv6_type = types::TIME_EXCEEDED;
188 b.code = code;
189 b.payload = payload;
190 b
192 }
193
194 #[must_use]
200 pub fn pkt_too_big(mtu: u32, payload: Vec<u8>) -> Self {
201 let mut b = Self::new();
202 b.icmpv6_type = types::PKT_TOO_BIG;
203 b.code = 0;
204 b.mtu = Some(mtu);
205 b.type_specific = [
206 (mtu >> 24) as u8,
207 ((mtu >> 16) & 0xFF) as u8,
208 ((mtu >> 8) & 0xFF) as u8,
209 (mtu & 0xFF) as u8,
210 ];
211 b.payload = payload;
212 b
213 }
214
215 #[must_use]
219 pub fn icmpv6_type(mut self, t: u8) -> Self {
220 self.icmpv6_type = t;
221 self
222 }
223
224 #[must_use]
226 pub fn code(mut self, c: u8) -> Self {
227 self.code = c;
228 self
229 }
230
231 #[must_use]
233 pub fn checksum(mut self, csum: u16) -> Self {
234 self.checksum = Some(csum);
235 self.auto_checksum = false;
236 self
237 }
238
239 #[must_use]
241 pub fn chksum(self, csum: u16) -> Self {
242 self.checksum(csum)
243 }
244
245 pub fn payload<T: Into<Vec<u8>>>(mut self, data: T) -> Self {
247 self.payload = data.into();
248 self
249 }
250
251 #[must_use]
253 pub fn enable_auto_checksum(mut self) -> Self {
254 self.auto_checksum = true;
255 self.checksum = None;
256 self
257 }
258
259 #[must_use]
261 pub fn disable_auto_checksum(mut self) -> Self {
262 self.auto_checksum = false;
263 self
264 }
265
266 #[must_use]
268 pub fn set_src_ip(mut self, src: Ipv6Addr) -> Self {
269 self.src_ip = Some(src);
270 self
271 }
272
273 #[must_use]
275 pub fn set_dst_ip(mut self, dst: Ipv6Addr) -> Self {
276 self.dst_ip = Some(dst);
277 self
278 }
279
280 #[must_use]
284 pub fn packet_size(&self) -> usize {
285 let base = ICMPV6_MIN_HEADER_LEN;
286 let extra = match self.icmpv6_type {
287 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT => 16, _ => 0,
289 };
290 base + extra + self.payload.len()
291 }
292
293 #[must_use]
295 pub fn header_size(&self) -> usize {
296 ICMPV6_MIN_HEADER_LEN
297 }
298
299 #[must_use]
310 pub fn build(&self) -> Vec<u8> {
311 let total = self.packet_size();
312 let mut buf = vec![0u8; total];
313
314 buf[offsets::TYPE] = self.icmpv6_type;
316
317 buf[offsets::CODE] = self.code;
319
320 buf[offsets::CHECKSUM] = 0;
322 buf[offsets::CHECKSUM + 1] = 0;
323
324 buf[4..8].copy_from_slice(&self.type_specific);
326
327 let mut offset = 8;
328
329 match self.icmpv6_type {
331 types::NEIGHBOR_SOLICIT | types::NEIGHBOR_ADVERT | types::REDIRECT => {
332 if let Some(target) = self.target
333 && offset + 16 <= buf.len()
334 {
335 buf[offset..offset + 16].copy_from_slice(&target.octets());
336 offset += 16;
337 }
338 },
339 _ => {},
340 }
341
342 if !self.payload.is_empty() && offset + self.payload.len() <= buf.len() {
344 buf[offset..offset + self.payload.len()].copy_from_slice(&self.payload);
345 }
346
347 if self.auto_checksum {
349 if let (Some(src), Some(dst)) = (self.src_ip, self.dst_ip) {
350 let csum = icmpv6_checksum(src, dst, &buf);
351 buf[offsets::CHECKSUM] = (csum >> 8) as u8;
352 buf[offsets::CHECKSUM + 1] = (csum & 0xFF) as u8;
353 }
354 } else if let Some(csum) = self.checksum {
356 buf[offsets::CHECKSUM] = (csum >> 8) as u8;
357 buf[offsets::CHECKSUM + 1] = (csum & 0xFF) as u8;
358 }
359
360 buf
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn test_echo_request_builder() {
370 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
371 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
372
373 let bytes = Icmpv6Builder::echo_request(0x1234, 5)
374 .set_src_ip(src)
375 .set_dst_ip(dst)
376 .payload(b"ping".as_ref())
377 .build();
378
379 assert_eq!(bytes[0], types::ECHO_REQUEST);
380 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");
384
385 let chksum = u16::from_be_bytes([bytes[2], bytes[3]]);
387 assert_ne!(chksum, 0);
388 }
389
390 #[test]
391 fn test_echo_reply_builder() {
392 let bytes = Icmpv6Builder::echo_reply(0x5678, 10).build();
393
394 assert_eq!(bytes[0], types::ECHO_REPLY);
395 assert_eq!(u16::from_be_bytes([bytes[4], bytes[5]]), 0x5678);
396 assert_eq!(u16::from_be_bytes([bytes[6], bytes[7]]), 10);
397 }
398
399 #[test]
400 fn test_neighbor_solicitation_builder() {
401 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
402 let bytes = Icmpv6Builder::neighbor_solicitation(target).build();
403
404 assert_eq!(bytes[0], types::NEIGHBOR_SOLICIT);
405 assert_eq!(bytes.len(), 24); let mut addr_bytes = [0u8; 16];
408 addr_bytes.copy_from_slice(&bytes[8..24]);
409 assert_eq!(Ipv6Addr::from(addr_bytes), target);
410 }
411
412 #[test]
413 fn test_neighbor_advertisement_builder() {
414 let target = Ipv6Addr::new(0xfe80, 0, 0, 0, 1, 0, 0, 1);
415 let bytes = Icmpv6Builder::neighbor_advertisement(target).build();
416
417 assert_eq!(bytes[0], types::NEIGHBOR_ADVERT);
418 assert_eq!(bytes.len(), 24); assert_eq!(bytes[4] & 0x60, 0x60);
421 }
422
423 #[test]
424 fn test_router_solicitation_builder() {
425 let bytes = Icmpv6Builder::router_solicitation().build();
426 assert_eq!(bytes[0], types::ROUTER_SOLICIT);
427 assert_eq!(bytes.len(), 8);
428 }
429
430 #[test]
431 fn test_pkt_too_big_builder() {
432 let payload = vec![0xAAu8; 20];
433 let bytes = Icmpv6Builder::pkt_too_big(1500, payload.clone()).build();
434
435 assert_eq!(bytes[0], types::PKT_TOO_BIG);
436 assert_eq!(bytes[1], 0); let mtu = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
438 assert_eq!(mtu, 1500);
439 assert_eq!(&bytes[8..], payload.as_slice());
440 }
441
442 #[test]
443 fn test_dest_unreachable_builder() {
444 let payload = vec![0u8; 10];
445 let bytes = Icmpv6Builder::dest_unreachable(3, payload.clone()).build();
446
447 assert_eq!(bytes[0], types::DEST_UNREACH);
448 assert_eq!(bytes[1], 3); assert_eq!(&bytes[8..], payload.as_slice());
450 }
451
452 #[test]
453 fn test_time_exceeded_builder() {
454 let bytes = Icmpv6Builder::time_exceeded(0, vec![]).build();
455 assert_eq!(bytes[0], types::TIME_EXCEEDED);
456 assert_eq!(bytes[1], 0);
457 }
458
459 #[test]
460 fn test_manual_checksum() {
461 let bytes = Icmpv6Builder::echo_request(1, 1).checksum(0xABCD).build();
462
463 assert_eq!(u16::from_be_bytes([bytes[2], bytes[3]]), 0xABCD);
464 }
465
466 #[test]
467 fn test_checksum_verification() {
468 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
469 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
470
471 let bytes = Icmpv6Builder::echo_request(0x1234, 1)
472 .set_src_ip(src)
473 .set_dst_ip(dst)
474 .build();
475
476 assert!(super::super::verify_icmpv6_checksum(src, dst, &bytes));
478 }
479
480 #[test]
481 fn test_no_checksum_without_src_dst() {
482 let bytes = Icmpv6Builder::echo_request(1, 1).build();
483 let chksum = u16::from_be_bytes([bytes[2], bytes[3]]);
485 assert_eq!(chksum, 0);
486 }
487
488 #[test]
489 fn test_packet_size() {
490 let builder = Icmpv6Builder::echo_request(1, 1).payload(vec![0u8; 10]);
491 assert_eq!(builder.packet_size(), 18); let ns_builder = Icmpv6Builder::neighbor_solicitation(Ipv6Addr::LOCALHOST);
494 assert_eq!(ns_builder.packet_size(), 24); }
496}