1#[inline]
9pub fn read_u16be(buf: &[u8]) -> u16 {
10 u16::from_be_bytes([buf[0], buf[1]])
11}
12
13#[inline]
15pub fn read_u32be(buf: &[u8]) -> u32 {
16 u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]])
17}
18
19#[inline]
21pub fn write_u16be(buf: &mut [u8], val: u16) {
22 buf[..2].copy_from_slice(&val.to_be_bytes());
23}
24
25#[inline]
27pub fn write_u32be(buf: &mut [u8], val: u32) {
28 buf[..4].copy_from_slice(&val.to_be_bytes());
29}
30
31pub fn internet_checksum(mut data: &[u8]) -> u16 {
44 let mut sum: u32 = 0;
45 while data.len() >= 2 {
46 sum += read_u16be(data) as u32;
47 data = &data[2..];
48 }
49 if !data.is_empty() {
50 sum += (data[0] as u32) << 8;
52 }
53 while sum >> 16 != 0 {
55 sum = (sum & 0xFFFF) + (sum >> 16);
56 }
57 !(sum as u16)
58}
59
60pub fn pseudo_header_checksum(src: &[u8], dst: &[u8], protocol: u8, transport_data: &[u8]) -> u16 {
66 let mut sum: u32 = 0;
67
68 sum = add_slice_to_sum(sum, src);
70 sum = add_slice_to_sum(sum, dst);
72 sum += protocol as u32;
74 sum += transport_data.len() as u32;
76 sum = add_slice_to_sum(sum, transport_data);
78
79 while sum >> 16 != 0 {
80 sum = (sum & 0xFFFF) + (sum >> 16);
81 }
82 !(sum as u16)
83}
84
85fn add_slice_to_sum(mut sum: u32, mut data: &[u8]) -> u32 {
86 while data.len() >= 2 {
87 sum += read_u16be(data) as u32;
88 data = &data[2..];
89 }
90 if !data.is_empty() {
91 sum += (data[0] as u32) << 8;
92 }
93 sum
94}
95
96#[derive(Debug, Clone)]
102pub enum BuildError {
103 PayloadTooLarge { max: usize, actual: usize },
105 InvalidField { field: &'static str, reason: &'static str },
107}
108
109impl core::fmt::Display for BuildError {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111 match self {
112 Self::PayloadTooLarge { max, actual } => {
113 write!(f, "payload too large: max {max} bytes, got {actual}")
114 }
115 Self::InvalidField { field, reason } => {
116 write!(f, "invalid field `{field}`: {reason}")
117 }
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn read_u16be_works() {
128 assert_eq!(read_u16be(&[0x08, 0x00]), 0x0800);
129 assert_eq!(read_u16be(&[0xFF, 0xFF]), 0xFFFF);
130 }
131
132 #[test]
133 fn read_u32be_works() {
134 assert_eq!(read_u32be(&[0xC0, 0xA8, 0x01, 0x01]), 0xC0A80101);
135 }
136
137 #[test]
138 fn write_u16be_works() {
139 let mut buf = [0u8; 2];
140 write_u16be(&mut buf, 0x0800);
141 assert_eq!(buf, [0x08, 0x00]);
142 }
143
144 #[test]
145 fn write_u32be_works() {
146 let mut buf = [0u8; 4];
147 write_u32be(&mut buf, 0xC0A80101);
148 assert_eq!(buf, [0xC0, 0xA8, 0x01, 0x01]);
149 }
150
151 #[test]
153 fn checksum_empty() {
154 assert_eq!(internet_checksum(&[]), 0xFFFF);
156 }
157
158 #[test]
159 fn checksum_single_byte() {
160 assert_eq!(internet_checksum(&[0x01]), 0xFEFF);
161 }
162
163 #[test]
164 fn checksum_known_ip_header() {
165 let header: [u8; 20] = [
167 0x45, 0x00, 0x00, 0x3C, 0x1C, 0x46, 0x40, 0x00,
168 0x40, 0x06, 0x00, 0x00, 0xAC, 0x10, 0x0A, 0x63,
169 0xAC, 0x10, 0x0A, 0x0C,
170 ];
171 let checksum = internet_checksum(&header);
172 let mut with_checksum = header;
174 with_checksum[10..12].copy_from_slice(&checksum.to_be_bytes());
175 assert_eq!(internet_checksum(&with_checksum), 0);
176 }
177
178 #[test]
179 fn pseudo_header_checksum_basic() {
180 let src = [192u8, 168, 1, 1];
182 let dst = [192u8, 168, 1, 2];
183 let tcp_header = [0x00u8, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x50, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x00];
186 let csum = pseudo_header_checksum(&src, &dst, 6, &tcp_header);
187 assert_ne!(csum, 0);
189 }
190
191 #[test]
192 fn checksum_roundtrip() {
193 let data: [u8; 8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
194 let csum = internet_checksum(&data);
195 let mut combined = [0u8; 10];
197 combined[..8].copy_from_slice(&data);
198 combined[8..10].copy_from_slice(&csum.to_be_bytes());
199 assert_eq!(internet_checksum(&combined), 0);
200 }
201}