1use 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#[cfg(all(feature = "udp", feature = "ipv4"))]
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum UdpChecksumStatus {
20 NotPresent,
22 Valid,
24 Invalid,
26}
27
28#[cfg(all(feature = "udp", feature = "ipv4"))]
29impl UdpDatagram<'_> {
30 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 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 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 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 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 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}