1use std::net::{IpAddr, Ipv4Addr};
2
3use crate::error::Error;
4
5const MINIMUM_PACKET_SIZE: usize = 20;
6
7#[derive(Debug, PartialEq)]
8pub enum IpV4Protocol {
9 Icmp,
10}
11
12impl IpV4Protocol {
13 fn decode(data: u8) -> Option<Self> {
14 match data {
15 1 => Some(IpV4Protocol::Icmp),
16 _ => None,
17 }
18 }
19}
20
21#[derive(Debug)]
22pub struct IpV4Packet<'a> {
23 pub protocol: IpV4Protocol,
24 pub source: IpAddr,
25 pub ttl: u8,
26 pub data: &'a [u8],
27}
28
29impl<'a> IpV4Packet<'a> {
30 pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
31 if data.len() < MINIMUM_PACKET_SIZE {
32 return Err(Error::TooSmallHeader);
33 }
34 let byte0 = data[0];
35 let version = (byte0 & 0xf0) >> 4;
36 let header_size = 4 * ((byte0 & 0x0f) as usize);
37
38 if version != 4 {
39 return Err(Error::InvalidVersion);
40 }
41
42 if header_size < MINIMUM_PACKET_SIZE {
43 return Err(Error::InvalidHeaderSize);
44 }
45
46 if data.len() < header_size {
47 return Err(Error::InvalidHeaderSize);
48 }
49
50 let protocol = match IpV4Protocol::decode(data[9]) {
51 Some(protocol) => protocol,
52 None => return Err(Error::UnknownProtocol),
53 };
54
55 Ok(Self {
56 protocol,
57 source: IpAddr::V4(Ipv4Addr::new(data[12], data[13], data[14], data[15])),
58 ttl: data[8],
59 data: &data[header_size..],
60 })
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn decodes_ipv4_header_metadata_and_payload() {
70 let packet = [
71 0x45, 0, 0, 28, 0, 0, 0, 0, 63, 1, 0, 0, 192, 0, 2, 1, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 0,
72 2,
73 ];
74
75 let decoded = IpV4Packet::decode(&packet).unwrap();
76
77 assert_eq!(decoded.protocol, IpV4Protocol::Icmp);
78 assert_eq!(decoded.source, IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
79 assert_eq!(decoded.ttl, 63);
80 assert_eq!(decoded.data, &[0, 0, 0, 0, 0, 1, 0, 2]);
81 }
82
83 #[test]
84 fn rejects_too_small_ihl() {
85 let packet = [
86 0x44, 0, 0, 20, 0, 0, 0, 0, 63, 1, 0, 0, 192, 0, 2, 1, 8, 8, 8, 8,
87 ];
88
89 assert!(matches!(
90 IpV4Packet::decode(&packet),
91 Err(Error::InvalidHeaderSize)
92 ));
93 }
94}