1use core::fmt;
4
5use crate::internet_checksum;
6#[cfg(all(feature = "tcp", feature = "ipv4"))]
7use crate::internet_checksum::add_ipv4_pseudoheader;
8#[cfg(all(feature = "tcp", feature = "ipv6"))]
9use crate::internet_checksum::add_ipv6_pseudoheader;
10#[cfg(all(feature = "tcp", feature = "ipv4"))]
11use crate::ipv4::address::Ipv4Address;
12#[cfg(all(feature = "tcp", feature = "ipv6"))]
13use crate::ipv6::address::Ipv6Address;
14use crate::tcp::segment::{TcpSegment, TcpSegmentMut};
15
16const TCP_PROTOCOL: u8 = 6;
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum TcpChecksumError {
21 LengthTooLarge {
23 maximum: usize,
25 actual: usize,
27 },
28}
29
30impl fmt::Display for TcpChecksumError {
31 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::LengthTooLarge { maximum, actual } => write!(
34 formatter,
35 "transport message length {actual} exceeds pseudoheader maximum {maximum}"
36 ),
37 }
38 }
39}
40
41#[cfg(all(feature = "tcp", feature = "ipv4"))]
42impl TcpSegment<'_> {
43 pub fn checksum_is_valid_ipv4(
45 &self,
46 source: Ipv4Address,
47 destination: Ipv4Address,
48 ) -> Result<bool, TcpChecksumError> {
49 Ok(tcp_ipv4_sum(self.as_bytes(), source, destination)? == 0xffff)
50 }
51}
52
53#[cfg(all(feature = "tcp", feature = "ipv4"))]
54impl TcpSegmentMut<'_> {
55 pub fn checksum_is_valid_ipv4(
57 &self,
58 source: Ipv4Address,
59 destination: Ipv4Address,
60 ) -> Result<bool, TcpChecksumError> {
61 Ok(tcp_ipv4_sum(self.as_bytes(), source, destination)? == 0xffff)
62 }
63
64 pub fn update_checksum_ipv4(
66 &mut self,
67 source: Ipv4Address,
68 destination: Ipv4Address,
69 ) -> Result<(), TcpChecksumError> {
70 let sum = tcp_ipv4_sum_with_zero_checksum(self.as_bytes(), source, destination)?;
71 let value = internet_checksum::checksum(sum);
72 self.as_bytes_mut()[16..18].copy_from_slice(&value.to_be_bytes());
73 Ok(())
74 }
75}
76
77#[cfg(all(feature = "tcp", feature = "ipv4"))]
78fn tcp_ipv4_sum(
79 bytes: &[u8],
80 source: Ipv4Address,
81 destination: Ipv4Address,
82) -> Result<u32, TcpChecksumError> {
83 let length = tcp_ipv4_length(bytes)?;
84 Ok(internet_checksum::add_bytes(
85 add_ipv4_pseudoheader(
86 0,
87 source.octets(),
88 destination.octets(),
89 TCP_PROTOCOL,
90 length,
91 ),
92 bytes,
93 ))
94}
95
96#[cfg(all(feature = "tcp", feature = "ipv4"))]
97fn tcp_ipv4_sum_with_zero_checksum(
98 bytes: &[u8],
99 source: Ipv4Address,
100 destination: Ipv4Address,
101) -> Result<u32, TcpChecksumError> {
102 let length = tcp_ipv4_length(bytes)?;
103 let sum = add_ipv4_pseudoheader(
104 0,
105 source.octets(),
106 destination.octets(),
107 TCP_PROTOCOL,
108 length,
109 );
110 let sum = internet_checksum::add_bytes(sum, &bytes[..16]);
111 let sum = internet_checksum::add_bytes(sum, &[0, 0]);
112 Ok(internet_checksum::add_bytes(sum, &bytes[18..]))
113}
114
115#[cfg(all(feature = "tcp", feature = "ipv4"))]
116fn tcp_ipv4_length(bytes: &[u8]) -> Result<u16, TcpChecksumError> {
117 u16::try_from(bytes.len()).map_err(|_| TcpChecksumError::LengthTooLarge {
118 maximum: usize::from(u16::MAX),
119 actual: bytes.len(),
120 })
121}
122
123#[cfg(all(feature = "tcp", feature = "ipv6"))]
124impl TcpSegment<'_> {
125 pub fn checksum_is_valid_ipv6(
127 &self,
128 source: Ipv6Address,
129 destination: Ipv6Address,
130 ) -> Result<bool, TcpChecksumError> {
131 let length = tcp_ipv6_length(self.as_bytes())?;
132 Ok(tcp_ipv6_sum(self.as_bytes(), source, destination, length) == 0xffff)
133 }
134}
135
136#[cfg(all(feature = "tcp", feature = "ipv6"))]
137impl TcpSegmentMut<'_> {
138 pub fn checksum_is_valid_ipv6(
140 &self,
141 source: Ipv6Address,
142 destination: Ipv6Address,
143 ) -> Result<bool, TcpChecksumError> {
144 let length = tcp_ipv6_length(self.as_bytes())?;
145 Ok(tcp_ipv6_sum(self.as_bytes(), source, destination, length) == 0xffff)
146 }
147
148 pub fn update_checksum_ipv6(
150 &mut self,
151 source: Ipv6Address,
152 destination: Ipv6Address,
153 ) -> Result<(), TcpChecksumError> {
154 let length = tcp_ipv6_length(self.as_bytes())?;
155 let bytes = self.as_bytes();
156 let sum = tcp_ipv6_sum_with_zero_checksum(bytes, source, destination, length);
157 let value = internet_checksum::checksum(sum);
158 self.as_bytes_mut()[16..18].copy_from_slice(&value.to_be_bytes());
159 Ok(())
160 }
161}
162
163#[cfg(all(feature = "tcp", feature = "ipv6"))]
164fn tcp_ipv6_length(bytes: &[u8]) -> Result<u32, TcpChecksumError> {
165 let maximum = usize::try_from(u32::MAX).unwrap_or(usize::MAX);
166 u32::try_from(bytes.len()).map_err(|_| TcpChecksumError::LengthTooLarge {
167 maximum,
168 actual: bytes.len(),
169 })
170}
171
172#[cfg(all(feature = "tcp", feature = "ipv6"))]
173fn tcp_ipv6_sum(bytes: &[u8], source: Ipv6Address, destination: Ipv6Address, length: u32) -> u32 {
174 internet_checksum::add_bytes(
175 add_ipv6_pseudoheader(
176 0,
177 source.octets(),
178 destination.octets(),
179 TCP_PROTOCOL,
180 length,
181 ),
182 bytes,
183 )
184}
185
186#[cfg(all(feature = "tcp", feature = "ipv6"))]
187fn tcp_ipv6_sum_with_zero_checksum(
188 bytes: &[u8],
189 source: Ipv6Address,
190 destination: Ipv6Address,
191 length: u32,
192) -> u32 {
193 let sum = add_ipv6_pseudoheader(
194 0,
195 source.octets(),
196 destination.octets(),
197 TCP_PROTOCOL,
198 length,
199 );
200 let sum = internet_checksum::add_bytes(sum, &bytes[..16]);
201 let sum = internet_checksum::add_bytes(sum, &[0, 0]);
202 internet_checksum::add_bytes(sum, &bytes[18..])
203}