net_wire/icmpv6/
checksum.rs1use core::fmt;
4
5use crate::icmpv6::message::{Icmpv6Message, Icmpv6MessageMut};
6use crate::internet_checksum::{self, add_ipv6_pseudoheader};
7use crate::ipv6::address::Ipv6Address;
8
9const ICMPV6_NEXT_HEADER: u8 = 58;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum Icmpv6ChecksumError {
14 LengthTooLarge {
16 maximum: usize,
18 actual: usize,
20 },
21}
22
23impl fmt::Display for Icmpv6ChecksumError {
24 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
25 match self {
26 Self::LengthTooLarge { maximum, actual } => write!(
27 formatter,
28 "transport message length {actual} exceeds pseudoheader maximum {maximum}"
29 ),
30 }
31 }
32}
33
34impl Icmpv6Message<'_> {
35 pub fn checksum_is_valid_ipv6(
37 &self,
38 source: Ipv6Address,
39 destination: Ipv6Address,
40 ) -> Result<bool, Icmpv6ChecksumError> {
41 let length = icmpv6_length(self.as_bytes())?;
42 Ok(icmpv6_checksum_sum(self.as_bytes(), source, destination, length) == 0xffff)
43 }
44}
45
46impl Icmpv6MessageMut<'_> {
47 pub fn checksum_is_valid_ipv6(
49 &self,
50 source: Ipv6Address,
51 destination: Ipv6Address,
52 ) -> Result<bool, Icmpv6ChecksumError> {
53 let length = icmpv6_length(self.as_bytes())?;
54 Ok(icmpv6_checksum_sum(self.as_bytes(), source, destination, length) == 0xffff)
55 }
56
57 pub fn update_checksum_ipv6(
59 &mut self,
60 source: Ipv6Address,
61 destination: Ipv6Address,
62 ) -> Result<(), Icmpv6ChecksumError> {
63 let length = icmpv6_length(self.as_bytes())?;
64 let bytes = self.as_bytes_mut();
65 bytes[2..4].fill(0);
66 let value =
67 internet_checksum::checksum(icmpv6_checksum_sum(bytes, source, destination, length));
68 bytes[2..4].copy_from_slice(&value.to_be_bytes());
69 Ok(())
70 }
71}
72
73fn icmpv6_length(bytes: &[u8]) -> Result<u32, Icmpv6ChecksumError> {
74 let maximum = usize::try_from(u32::MAX).unwrap_or(usize::MAX);
75 u32::try_from(bytes.len()).map_err(|_| Icmpv6ChecksumError::LengthTooLarge {
76 maximum,
77 actual: bytes.len(),
78 })
79}
80
81fn icmpv6_checksum_sum(
82 bytes: &[u8],
83 source: Ipv6Address,
84 destination: Ipv6Address,
85 length: u32,
86) -> u32 {
87 internet_checksum::add_bytes(
88 add_ipv6_pseudoheader(
89 0,
90 source.octets(),
91 destination.octets(),
92 ICMPV6_NEXT_HEADER,
93 length,
94 ),
95 bytes,
96 )
97}