Skip to main content

net_wire/udp/
checksum.rs

1//! UDP checksum integration for IP pseudoheaders.
2
3use crate::internet_checksum;
4#[cfg(all(feature = "udp", feature = "ipv4"))]
5use crate::internet_checksum::add_ipv4_pseudoheader;
6#[cfg(all(feature = "udp", feature = "ipv6"))]
7use crate::internet_checksum::add_ipv6_pseudoheader;
8#[cfg(all(feature = "udp", feature = "ipv4"))]
9use crate::ipv4::address::Ipv4Address;
10#[cfg(all(feature = "udp", feature = "ipv6"))]
11use crate::ipv6::address::Ipv6Address;
12use crate::udp::datagram::{UdpDatagram, UdpDatagramMut};
13
14const UDP_PROTOCOL: u8 = 17;
15
16/// The validation state of an IPv4 UDP checksum.
17#[cfg(all(feature = "udp", feature = "ipv4"))]
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum UdpChecksumStatus {
20    /// UDP has no IPv4 checksum.
21    NotPresent,
22    /// The encoded checksum is valid.
23    Valid,
24    /// The encoded checksum is invalid.
25    Invalid,
26}
27
28#[cfg(all(feature = "udp", feature = "ipv4"))]
29impl UdpDatagram<'_> {
30    /// Returns the validation state of this datagram's IPv4 checksum.
31    pub fn checksum_status_ipv4(
32        &self,
33        source: Ipv4Address,
34        destination: Ipv4Address,
35    ) -> UdpChecksumStatus {
36        if self.checksum() == 0 {
37            return UdpChecksumStatus::NotPresent;
38        }
39        if udp_ipv4_checksum_sum(self.as_bytes(), source, destination) == 0xffff {
40            UdpChecksumStatus::Valid
41        } else {
42            UdpChecksumStatus::Invalid
43        }
44    }
45}
46
47#[cfg(all(feature = "udp", feature = "ipv4"))]
48impl UdpDatagramMut<'_> {
49    /// Returns the validation state of this datagram's IPv4 checksum.
50    pub fn checksum_status_ipv4(
51        &self,
52        source: Ipv4Address,
53        destination: Ipv4Address,
54    ) -> UdpChecksumStatus {
55        if self.checksum() == 0 {
56            return UdpChecksumStatus::NotPresent;
57        }
58        if udp_ipv4_checksum_sum(self.as_bytes(), source, destination) == 0xffff {
59            UdpChecksumStatus::Valid
60        } else {
61            UdpChecksumStatus::Invalid
62        }
63    }
64
65    /// Recomputes this datagram's IPv4 checksum.
66    pub fn update_checksum_ipv4(&mut self, source: Ipv4Address, destination: Ipv4Address) {
67        let bytes = self.as_bytes_mut();
68        bytes[6..8].fill(0);
69        let value = internet_checksum::checksum(udp_ipv4_checksum_sum(bytes, source, destination));
70        bytes[6..8].copy_from_slice(&(if value == 0 { 0xffff } else { value }).to_be_bytes());
71    }
72}
73
74#[cfg(all(feature = "udp", feature = "ipv4"))]
75fn udp_ipv4_checksum_sum(bytes: &[u8], source: Ipv4Address, destination: Ipv4Address) -> u32 {
76    let length = u16::try_from(bytes.len()).expect("UDP length is validated");
77    internet_checksum::add_bytes(
78        add_ipv4_pseudoheader(
79            0,
80            source.octets(),
81            destination.octets(),
82            UDP_PROTOCOL,
83            length,
84        ),
85        bytes,
86    )
87}
88
89#[cfg(all(feature = "udp", feature = "ipv6"))]
90impl UdpDatagram<'_> {
91    /// Checks this datagram against its required IPv6 pseudoheader checksum.
92    pub fn checksum_is_valid_ipv6(&self, source: Ipv6Address, destination: Ipv6Address) -> bool {
93        self.checksum() != 0
94            && udp_ipv6_checksum_sum(
95                self.as_bytes(),
96                source,
97                destination,
98                u32::from(self.length()),
99            ) == 0xffff
100    }
101}
102
103#[cfg(all(feature = "udp", feature = "ipv6"))]
104impl UdpDatagramMut<'_> {
105    /// Checks this datagram against its required IPv6 pseudoheader checksum.
106    pub fn checksum_is_valid_ipv6(&self, source: Ipv6Address, destination: Ipv6Address) -> bool {
107        self.checksum() != 0
108            && udp_ipv6_checksum_sum(
109                self.as_bytes(),
110                source,
111                destination,
112                u32::from(self.length()),
113            ) == 0xffff
114    }
115
116    /// Recomputes this datagram's IPv6 pseudoheader checksum.
117    pub fn update_checksum_ipv6(&mut self, source: Ipv6Address, destination: Ipv6Address) {
118        let length = u32::from(self.length());
119        let bytes = self.as_bytes_mut();
120        bytes[6..8].fill(0);
121        let value =
122            internet_checksum::checksum(udp_ipv6_checksum_sum(bytes, source, destination, length));
123        bytes[6..8].copy_from_slice(&(if value == 0 { 0xffff } else { value }).to_be_bytes());
124    }
125}
126
127#[cfg(all(feature = "udp", feature = "ipv6"))]
128fn udp_ipv6_checksum_sum(
129    bytes: &[u8],
130    source: Ipv6Address,
131    destination: Ipv6Address,
132    length: u32,
133) -> u32 {
134    internet_checksum::add_bytes(
135        add_ipv6_pseudoheader(
136            0,
137            source.octets(),
138            destination.octets(),
139            UDP_PROTOCOL,
140            length,
141        ),
142        bytes,
143    )
144}