internet/ietf/ipv4/encoding/
header.rs1use crate::ietf::{
8 ip::{Protocol, Version},
9 ipv4::{Address, Options},
10};
11use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Dscp(pub u8);
18
19impl Dscp {
20 pub fn new(value: u8) -> BufResult<Self> {
26 if value <= 63 {
27 Ok(Self(value))
28 } else {
29 Err(BufError::UnexpectedValue)
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[repr(u8)]
39pub enum Ecn {
40 NonEct = 0,
42 Ect0 = 1,
44 Ect1 = 2,
46 Ce = 3,
48}
49
50impl Ecn {
51 fn from_bits(bits: u8) -> BufResult<Self> {
53 match bits & 0x03 {
54 0 => Ok(Self::NonEct),
55 1 => Ok(Self::Ect0),
56 2 => Ok(Self::Ect1),
57 3 => Ok(Self::Ce),
58 _ => Err(BufError::UnexpectedValue),
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub struct Ihl(pub u8);
68
69impl Ihl {
70 pub fn new(value: u8) -> BufResult<Self> {
79 if (5..=15).contains(&value) {
80 Ok(Self(value))
81 } else {
82 Err(BufError::UnexpectedValue)
83 }
84 }
85}
86
87impl PartialEq<u8> for Ihl {
88 fn eq(&self, other: &u8) -> bool {
89 self.0 == *other
90 }
91}
92
93impl PartialEq<usize> for Ihl {
94 fn eq(&self, other: &usize) -> bool {
95 (self.0 as usize) == *other
96 }
97}
98
99impl PartialOrd<u8> for Ihl {
100 fn partial_cmp(&self, other: &u8) -> Option<core::cmp::Ordering> {
101 self.0.partial_cmp(other)
102 }
103}
104
105impl PartialOrd<usize> for Ihl {
106 fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
107 (self.0 as usize).partial_cmp(other)
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117pub struct Checksum(pub u16);
118
119impl Checksum {
120 pub fn calculate(data: &[u8]) -> Self {
141 let mut sum: u32 = 0;
142 let mut i = 0;
143
144 while i + 1 < data.len() {
145 sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
146 i += 2;
147 }
148
149 if i < data.len() {
150 sum += (data[i] as u32) << 8;
151 }
152
153 while sum >> 16 != 0 {
154 sum = (sum & 0xFFFF) + (sum >> 16);
155 }
156
157 Self(!(sum as u16))
158 }
159}
160
161impl Codec for Checksum {
162 fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
163 self.0.encode(writer, ())
164 }
165
166 fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
167 Ok(Self(u16::decode(reader, ())?))
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
175pub struct Header {
176 pub dscp: Dscp,
178 pub ecn: Ecn,
180 pub ihl: Ihl,
182 pub total_length: u16,
184 pub identification: u16,
186 pub df: bool,
188 pub mf: bool,
190 pub fragment_offset: u16,
192 pub ttl: u8,
194 pub protocol: Protocol,
196 pub checksum: Checksum,
198 pub source_address: Address,
200 pub destination_address: Address,
202 pub options: Options,
204}
205
206impl Codec for Header {
207 fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
208 let version_ihl = ((Version::Ip as u8) << 4) | (self.ihl.0 & 0x0F);
209 writer.write_u8(version_ihl)?;
210
211 let dscp_ecn = (self.dscp.0 << 2) | (self.ecn as u8);
212 writer.write_u8(dscp_ecn)?;
213
214 writer.write_u16_be(self.total_length)?;
215 writer.write_u16_be(self.identification)?;
216
217 let mut flags_offset = self.fragment_offset & 0x1FFF;
218 if self.df {
219 flags_offset |= 0x4000;
220 }
221 if self.mf {
222 flags_offset |= 0x2000;
223 }
224 writer.write_u16_be(flags_offset)?;
225
226 writer.write_u8(self.ttl)?;
227 self.protocol.encode(writer, ())?;
228 self.checksum.encode(writer, ())?;
229
230 self.source_address.encode(writer, ())?;
231 self.destination_address.encode(writer, ())?;
232
233 let options_len = ((self.ihl.0 as usize) * 4).saturating_sub(20);
234 self.options.encode(writer, options_len)?;
235
236 Ok(())
237 }
238
239 fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
240 let version_ihl = reader.read_u8()?;
241 let version = version_ihl >> 4;
242
243 if version != (Version::Ip as u8) {
244 return Err(BufError::UnexpectedValue);
245 }
246
247 let ihl = Ihl::new(version_ihl & 0x0F)?;
248
249 let dscp_ecn = reader.read_u8()?;
250 let dscp = Dscp::new(dscp_ecn >> 2)?;
251 let ecn = Ecn::from_bits(dscp_ecn)?;
252
253 let total_length = reader.read_u16_be()?;
254 let identification = reader.read_u16_be()?;
255
256 let flags_offset = reader.read_u16_be()?;
257 let df = (flags_offset & 0x4000) != 0;
258 let mf = (flags_offset & 0x2000) != 0;
259 let fragment_offset = flags_offset & 0x1FFF;
260
261 let ttl = reader.read_u8()?;
262 let protocol = Protocol::decode(reader, ())?;
263 let checksum = Checksum::decode(reader, ())?;
264
265 let source_address = Address::decode(reader, ())?;
266 let destination_address = Address::decode(reader, ())?;
267
268 let options_len = ((ihl.0 as usize) * 4).saturating_sub(20);
269 let options = Options::decode(reader, options_len)?;
270
271 Ok(Self {
272 dscp,
273 ecn,
274 ihl,
275 total_length,
276 identification,
277 df,
278 mf,
279 fragment_offset,
280 ttl,
281 protocol,
282 checksum,
283 source_address,
284 destination_address,
285 options,
286 })
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293 use crate::Cursor;
294
295 #[test]
296 fn ihl_comparisons() {
297 let ihl = Ihl::new(5).unwrap();
298 assert_eq!(ihl, 5u8);
299 assert_eq!(ihl, 5usize);
300 assert!(ihl < 6u8);
301 assert!(ihl <= 5usize);
302 }
303
304 #[test]
305 fn ihl_validation() {
306 assert!(Ihl::new(4).is_err());
307 assert!(Ihl::new(5).is_ok());
308 assert!(Ihl::new(15).is_ok());
309 assert!(Ihl::new(16).is_err());
310 }
311
312 #[test]
313 fn header_roundtrip() {
314 let etalon_bytes = &[
315 0x45, 0x00, 0x00, 0x34, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8,
316 0x00, 0x01, 0xc0, 0xa8, 0x00, 0x02,
317 ];
318
319 let etalon_struct = Header {
320 dscp: Dscp(0),
321 ecn: Ecn::NonEct,
322 ihl: Ihl(5),
323 total_length: 52,
324 identification: 0,
325 df: true,
326 mf: false,
327 fragment_offset: 0,
328 ttl: 64,
329 protocol: Protocol::UDP,
330 checksum: Checksum(0),
331 source_address: Address::from([192, 168, 0, 1]),
332 destination_address: Address::from([192, 168, 0, 2]),
333 options: Options::try_from(&[][..]).unwrap(),
334 };
335
336 let mut encoded_bytes = vec![];
337 {
338 let writer = &mut Cursor::new(&mut encoded_bytes);
339 etalon_struct.encode(writer, ()).unwrap();
340 }
341 assert_eq!(&etalon_bytes.to_vec(), &encoded_bytes);
342
343 let decoded_struct = {
344 let reader = &mut Cursor::new(&encoded_bytes);
345 Header::decode(reader, ()).unwrap()
346 };
347 assert_eq!(etalon_struct, decoded_struct);
348 }
349}