1use std::net::Ipv6Addr;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(u8)]
12pub enum Icmpv6Type {
13 DestinationUnreachable = 1,
15 PacketTooBig = 2,
17 TimeExceeded = 3,
19 ParameterProblem = 4,
21 EchoRequest = 128,
23 EchoReply = 129,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[repr(u8)]
30pub enum DestUnreachableCode {
31 NoRoute = 0,
33 AdminProhibited = 1,
35 BeyondScope = 2,
37 AddressUnreachable = 3,
39 PortUnreachable = 4,
41 SourcePolicy = 5,
43 RejectRoute = 6,
45}
46
47pub const IPPROTO_ICMPV6: u8 = 58;
49
50const MIN_IPV6_MTU: usize = 1280;
52
53const IPV6_HEADER_LEN: usize = 40;
55
56const ICMPV6_HEADER_LEN: usize = 8;
58
59const MAX_ORIGINAL_PACKET: usize = MIN_IPV6_MTU - IPV6_HEADER_LEN - ICMPV6_HEADER_LEN;
61
62pub const FIPS_OVERHEAD: u16 = 16 + 16 + 5 + 35 + 12 + 6 + 16; pub const FIPS_IPV6_OVERHEAD: u16 = 77;
105
106pub fn effective_ipv6_mtu(transport_mtu: u16) -> u16 {
112 transport_mtu.saturating_sub(FIPS_IPV6_OVERHEAD)
113}
114
115pub fn should_send_icmp_error(packet: &[u8]) -> bool {
125 if packet.len() < IPV6_HEADER_LEN {
127 return false;
128 }
129
130 let version = packet[0] >> 4;
132 if version != 6 {
133 return false;
134 }
135
136 let src = Ipv6Addr::from(<[u8; 16]>::try_from(&packet[8..24]).unwrap());
138
139 if src.is_unspecified() {
141 return false;
142 }
143
144 if src.octets()[0] == 0xff {
146 return false;
147 }
148
149 let dst = Ipv6Addr::from(<[u8; 16]>::try_from(&packet[24..40]).unwrap());
151
152 if dst.octets()[0] == 0xff {
155 return false;
156 }
157
158 let next_header = packet[6];
160 if next_header == IPPROTO_ICMPV6 && packet.len() > IPV6_HEADER_LEN {
161 let icmp_type = packet[IPV6_HEADER_LEN];
162 if icmp_type < 128 {
164 return false;
165 }
166 }
167
168 true
169}
170
171pub fn build_dest_unreachable(
183 original_packet: &[u8],
184 code: DestUnreachableCode,
185 our_addr: Ipv6Addr,
186) -> Option<Vec<u8>> {
187 if original_packet.len() < IPV6_HEADER_LEN {
189 return None;
190 }
191
192 let dest_addr = Ipv6Addr::from(<[u8; 16]>::try_from(&original_packet[8..24]).unwrap());
194
195 let original_len = original_packet.len().min(MAX_ORIGINAL_PACKET);
197 let icmpv6_len = ICMPV6_HEADER_LEN + original_len;
198 let total_len = IPV6_HEADER_LEN + icmpv6_len;
199
200 let mut response = vec![0u8; total_len];
201
202 response[0] = 0x60; let payload_len = icmpv6_len as u16;
209 response[4..6].copy_from_slice(&payload_len.to_be_bytes());
210
211 response[6] = IPPROTO_ICMPV6;
213
214 response[7] = 64;
216
217 response[8..24].copy_from_slice(&our_addr.octets());
219
220 response[24..40].copy_from_slice(&dest_addr.octets());
222
223 let icmp_start = IPV6_HEADER_LEN;
225
226 response[icmp_start] = Icmpv6Type::DestinationUnreachable as u8;
228
229 response[icmp_start + 1] = code as u8;
231
232 response[icmp_start + ICMPV6_HEADER_LEN..].copy_from_slice(&original_packet[..original_len]);
241
242 let checksum = icmpv6_checksum(&response[icmp_start..], &our_addr, &dest_addr);
244 response[icmp_start + 2..icmp_start + 4].copy_from_slice(&checksum.to_be_bytes());
245
246 Some(response)
247}
248
249pub fn build_packet_too_big(
276 original_packet: &[u8],
277 mtu: u32,
278 our_addr: Ipv6Addr,
279) -> Option<Vec<u8>> {
280 if original_packet.len() < IPV6_HEADER_LEN {
282 return None;
283 }
284
285 let version = original_packet[0] >> 4;
287 if version != 6 {
288 return None;
289 }
290
291 let src_addr = Ipv6Addr::from(<[u8; 16]>::try_from(&original_packet[8..24]).unwrap());
293
294 if src_addr.is_unspecified() || src_addr.octets()[0] == 0xff {
298 return None;
299 }
300
301 let next_header = original_packet[6];
303 if next_header == IPPROTO_ICMPV6 && original_packet.len() > IPV6_HEADER_LEN {
304 let icmp_type = original_packet[IPV6_HEADER_LEN];
305 if icmp_type < 128 {
307 return None;
308 }
309 }
310
311 let original_len = original_packet.len().min(MAX_ORIGINAL_PACKET);
314 let icmpv6_len = ICMPV6_HEADER_LEN + original_len;
315 let total_len = IPV6_HEADER_LEN + icmpv6_len;
316
317 let mut response = vec![0u8; total_len];
318
319 response[0] = 0x60; let payload_len = icmpv6_len as u16;
326 response[4..6].copy_from_slice(&payload_len.to_be_bytes());
327
328 response[6] = IPPROTO_ICMPV6;
330
331 response[7] = 64;
333
334 response[8..24].copy_from_slice(&our_addr.octets());
336
337 response[24..40].copy_from_slice(&src_addr.octets());
339
340 let icmp_start = IPV6_HEADER_LEN;
342
343 response[icmp_start] = Icmpv6Type::PacketTooBig as u8;
345
346 response[icmp_start + 1] = 0;
348
349 response[icmp_start + 4..icmp_start + 8].copy_from_slice(&mtu.to_be_bytes());
354
355 response[icmp_start + ICMPV6_HEADER_LEN..].copy_from_slice(&original_packet[..original_len]);
358
359 let checksum = icmpv6_checksum(&response[icmp_start..], &our_addr, &src_addr);
361 response[icmp_start + 2..icmp_start + 4].copy_from_slice(&checksum.to_be_bytes());
362
363 Some(response)
364}
365
366fn icmpv6_checksum(icmpv6_message: &[u8], src: &Ipv6Addr, dst: &Ipv6Addr) -> u16 {
370 let mut sum: u32 = 0;
371
372 for chunk in src.octets().chunks(2) {
374 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
375 }
376
377 for chunk in dst.octets().chunks(2) {
379 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
380 }
381
382 let len = icmpv6_message.len() as u32;
384 sum += len >> 16;
385 sum += len & 0xffff;
386
387 sum += IPPROTO_ICMPV6 as u32;
389
390 let mut i = 0;
392 while i + 1 < icmpv6_message.len() {
393 if i == 2 {
395 i += 2;
396 continue;
397 }
398 sum += u16::from_be_bytes([icmpv6_message[i], icmpv6_message[i + 1]]) as u32;
399 i += 2;
400 }
401
402 if i < icmpv6_message.len() {
404 sum += (icmpv6_message[i] as u32) << 8;
405 }
406
407 while sum >> 16 != 0 {
409 sum = (sum & 0xffff) + (sum >> 16);
410 }
411
412 !(sum as u16)
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419
420 fn make_ipv6_packet(src: Ipv6Addr, dst: Ipv6Addr, next_header: u8, payload: &[u8]) -> Vec<u8> {
421 let mut packet = vec![0u8; IPV6_HEADER_LEN + payload.len()];
422
423 packet[0] = 0x60;
425
426 let len = payload.len() as u16;
428 packet[4..6].copy_from_slice(&len.to_be_bytes());
429
430 packet[6] = next_header;
432
433 packet[7] = 64;
435
436 packet[8..24].copy_from_slice(&src.octets());
438
439 packet[24..40].copy_from_slice(&dst.octets());
441
442 packet[IPV6_HEADER_LEN..].copy_from_slice(payload);
444
445 packet
446 }
447
448 #[test]
449 fn test_should_send_error_valid_packet() {
450 let src = "fd00::1".parse().unwrap();
451 let dst = "fd00::2".parse().unwrap();
452 let packet = make_ipv6_packet(src, dst, 17, &[0u8; 8]); assert!(should_send_icmp_error(&packet));
455 }
456
457 #[test]
458 fn test_should_not_send_error_unspecified_source() {
459 let src = Ipv6Addr::UNSPECIFIED;
460 let dst = "fd00::2".parse().unwrap();
461 let packet = make_ipv6_packet(src, dst, 17, &[0u8; 8]);
462
463 assert!(!should_send_icmp_error(&packet));
464 }
465
466 #[test]
467 fn test_should_not_send_error_multicast_source() {
468 let src = "ff02::1".parse().unwrap();
469 let dst = "fd00::2".parse().unwrap();
470 let packet = make_ipv6_packet(src, dst, 17, &[0u8; 8]);
471
472 assert!(!should_send_icmp_error(&packet));
473 }
474
475 #[test]
476 fn test_should_not_send_error_multicast_destination() {
477 let src = "fe80::1".parse().unwrap();
478 let dst = "ff02::2".parse().unwrap(); let packet = make_ipv6_packet(src, dst, 17, &[0u8; 8]);
480
481 assert!(!should_send_icmp_error(&packet));
482 }
483
484 #[test]
485 fn test_should_not_send_error_for_icmp_error() {
486 let src = "fd00::1".parse().unwrap();
487 let dst = "fd00::2".parse().unwrap();
488 let icmp_payload = [1u8, 0, 0, 0, 0, 0, 0, 0];
490 let packet = make_ipv6_packet(src, dst, IPPROTO_ICMPV6, &icmp_payload);
491
492 assert!(!should_send_icmp_error(&packet));
493 }
494
495 #[test]
496 fn test_should_send_error_for_icmp_echo() {
497 let src = "fd00::1".parse().unwrap();
498 let dst = "fd00::2".parse().unwrap();
499 let icmp_payload = [128u8, 0, 0, 0, 0, 0, 0, 0];
501 let packet = make_ipv6_packet(src, dst, IPPROTO_ICMPV6, &icmp_payload);
502
503 assert!(should_send_icmp_error(&packet));
504 }
505
506 #[test]
507 fn test_should_not_send_error_short_packet() {
508 let packet = vec![0u8; 20]; assert!(!should_send_icmp_error(&packet));
510 }
511
512 #[test]
513 fn test_build_dest_unreachable() {
514 let src: Ipv6Addr = "fd00::1".parse().unwrap();
515 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
516 let original = make_ipv6_packet(src, dst, 17, &[0u8; 8]);
517
518 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
519 let response = build_dest_unreachable(&original, DestUnreachableCode::NoRoute, our_addr);
520
521 assert!(response.is_some());
522 let response = response.unwrap();
523
524 assert_eq!(response[0] >> 4, 6); assert_eq!(response[6], IPPROTO_ICMPV6); let resp_src = Ipv6Addr::from(<[u8; 16]>::try_from(&response[8..24]).unwrap());
530 assert_eq!(resp_src, our_addr);
531
532 let resp_dst = Ipv6Addr::from(<[u8; 16]>::try_from(&response[24..40]).unwrap());
534 assert_eq!(resp_dst, src);
535
536 assert_eq!(response[IPV6_HEADER_LEN], 1); assert_eq!(response[IPV6_HEADER_LEN + 1], 0); }
540
541 #[test]
542 fn test_build_dest_unreachable_invalid_input() {
543 let short_packet = vec![0u8; 20];
544 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
545
546 let response =
547 build_dest_unreachable(&short_packet, DestUnreachableCode::NoRoute, our_addr);
548 assert!(response.is_none());
549 }
550
551 #[test]
552 fn test_build_dest_unreachable_truncates_large_packet() {
553 let src: Ipv6Addr = "fd00::1".parse().unwrap();
554 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
555 let original = make_ipv6_packet(src, dst, 17, &[0u8; 2000]);
557
558 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
559 let response = build_dest_unreachable(&original, DestUnreachableCode::NoRoute, our_addr);
560
561 assert!(response.is_some());
562 let response = response.unwrap();
563
564 assert!(response.len() <= MIN_IPV6_MTU);
566 }
567
568 #[test]
569 fn test_build_packet_too_big() {
570 let src: Ipv6Addr = "fd00::1".parse().unwrap();
571 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
572 let original = make_ipv6_packet(src, dst, 17, &[0u8; 1200]); let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
575 let mtu = 1070u32;
576 let response = build_packet_too_big(&original, mtu, our_addr);
577
578 assert!(response.is_some());
579 let response = response.unwrap();
580
581 assert_eq!(response[0] >> 4, 6); assert_eq!(response[6], IPPROTO_ICMPV6); let resp_src = Ipv6Addr::from(<[u8; 16]>::try_from(&response[8..24]).unwrap());
587 assert_eq!(resp_src, our_addr);
588
589 let resp_dst = Ipv6Addr::from(<[u8; 16]>::try_from(&response[24..40]).unwrap());
591 assert_eq!(resp_dst, src);
592
593 assert_eq!(response[IPV6_HEADER_LEN], 2); assert_eq!(response[IPV6_HEADER_LEN + 1], 0); let reported_mtu = u32::from_be_bytes([
599 response[IPV6_HEADER_LEN + 4],
600 response[IPV6_HEADER_LEN + 5],
601 response[IPV6_HEADER_LEN + 6],
602 response[IPV6_HEADER_LEN + 7],
603 ]);
604 assert_eq!(reported_mtu, mtu);
605
606 assert!(response.len() <= MIN_IPV6_MTU);
608 }
609
610 #[test]
611 fn test_build_packet_too_big_invalid_input() {
612 let short_packet = vec![0u8; 20];
613 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
614
615 let response = build_packet_too_big(&short_packet, 1280, our_addr);
616 assert!(response.is_none());
617 }
618
619 #[test]
620 fn test_build_packet_too_big_multicast_source() {
621 let src: Ipv6Addr = "ff02::1".parse().unwrap(); let dst: Ipv6Addr = "fd00::2".parse().unwrap();
623 let original = make_ipv6_packet(src, dst, 17, &[0u8; 1200]);
624
625 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
626 let response = build_packet_too_big(&original, 1280, our_addr);
627
628 assert!(response.is_none());
630 }
631
632 #[test]
633 fn test_build_packet_too_big_unspecified_source() {
634 let src = Ipv6Addr::UNSPECIFIED;
635 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
636 let original = make_ipv6_packet(src, dst, 17, &[0u8; 1200]);
637
638 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
639 let response = build_packet_too_big(&original, 1280, our_addr);
640
641 assert!(response.is_none());
643 }
644
645 #[test]
646 fn test_build_packet_too_big_for_icmp_error() {
647 let src: Ipv6Addr = "fd00::1".parse().unwrap();
648 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
649 let icmp_payload = [1u8, 0, 0, 0, 0, 0, 0, 0];
651 let original = make_ipv6_packet(src, dst, IPPROTO_ICMPV6, &icmp_payload);
652
653 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
654 let response = build_packet_too_big(&original, 1280, our_addr);
655
656 assert!(response.is_none());
658 }
659
660 #[test]
661 fn test_build_packet_too_big_for_icmp_echo() {
662 let src: Ipv6Addr = "fd00::1".parse().unwrap();
663 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
664 let icmp_payload = [128u8, 0, 0, 0, 0, 0, 0, 0];
666 let original = make_ipv6_packet(src, dst, IPPROTO_ICMPV6, &icmp_payload);
667
668 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
669 let response = build_packet_too_big(&original, 1280, our_addr);
670
671 assert!(response.is_some());
673 }
674
675 #[test]
676 fn test_build_packet_too_big_truncates_large_packet() {
677 let src: Ipv6Addr = "fd00::1".parse().unwrap();
678 let dst: Ipv6Addr = "fd00::2".parse().unwrap();
679 let original = make_ipv6_packet(src, dst, 17, &[0u8; 2000]);
681
682 let our_addr: Ipv6Addr = "fd00::ffff".parse().unwrap();
683 let response = build_packet_too_big(&original, 1070, our_addr);
684
685 assert!(response.is_some());
686 let response = response.unwrap();
687
688 assert!(response.len() <= MIN_IPV6_MTU);
690 }
691
692 #[test]
700 fn test_build_packet_too_big_remote_source_for_pmtud() {
701 let local_addr: Ipv6Addr = "fd41::1".parse().unwrap();
702 let remote_addr: Ipv6Addr = "fddf::2".parse().unwrap();
703 let original = make_ipv6_packet(local_addr, remote_addr, 6, &[0u8; 1200]); let response = build_packet_too_big(&original, 1203, remote_addr);
708 assert!(response.is_some());
709 let response = response.unwrap();
710
711 let ptb_src = Ipv6Addr::from(<[u8; 16]>::try_from(&response[8..24]).unwrap());
713 assert_eq!(
714 ptb_src, remote_addr,
715 "PTB source must be remote peer address"
716 );
717
718 let ptb_dst = Ipv6Addr::from(<[u8; 16]>::try_from(&response[24..40]).unwrap());
720 assert_eq!(
721 ptb_dst, local_addr,
722 "PTB destination must be original sender"
723 );
724
725 assert_eq!(response[IPV6_HEADER_LEN], 2); assert_eq!(response[IPV6_HEADER_LEN + 1], 0); let reported_mtu = u32::from_be_bytes([
731 response[IPV6_HEADER_LEN + 4],
732 response[IPV6_HEADER_LEN + 5],
733 response[IPV6_HEADER_LEN + 6],
734 response[IPV6_HEADER_LEN + 7],
735 ]);
736 assert_eq!(reported_mtu, 1203);
737
738 let stored_checksum =
740 u16::from_be_bytes([response[IPV6_HEADER_LEN + 2], response[IPV6_HEADER_LEN + 3]]);
741 let recomputed = icmpv6_checksum(&response[IPV6_HEADER_LEN..], &remote_addr, &local_addr);
742 assert_eq!(stored_checksum, recomputed, "ICMPv6 checksum must be valid");
743 }
744}