Skip to main content

iris_core/protocols/packet/
ipv4.rs

1//! IPv4 packet.
2
3use crate::memory::mbuf::Mbuf;
4use crate::protocols::packet::{Packet, PacketHeader, PacketParseError};
5use crate::utils::types::*;
6
7use std::net::Ipv4Addr;
8
9use anyhow::{bail, Result};
10
11/// IPv4 EtherType
12const IPV4_PROTOCOL: usize = 0x0800;
13/// Flag: "Reserved bit"
14const IPV4_RF: u16 = 0x8000;
15/// Flag: "Don't fragment"
16const IPV4_DF: u16 = 0x4000;
17/// Flag: "More fragments"
18const IPV4_MF: u16 = 0x2000;
19/// Fragment offset part
20const IPV4_FRAG_OFFSET: u16 = 0x1FFF;
21
22/// An IPv4 packet.
23///
24/// IPv4 options are not parsed by default.
25#[derive(Debug)]
26pub struct Ipv4<'a> {
27    /// Fixed header.
28    header: Ipv4Header,
29    /// Offset to `header` from the start of `mbuf`.
30    offset: usize,
31    /// Packet buffer.
32    mbuf: &'a Mbuf,
33}
34
35impl Ipv4<'_> {
36    /// Returns the IP protocol version.
37    #[inline]
38    pub fn version(&self) -> u8 {
39        (self.header.version_ihl & 0xf0) >> 4
40    }
41
42    /// Returns the header length measured in 32-bit words (IHL).
43    #[inline]
44    pub fn ihl(&self) -> u8 {
45        self.header.version_ihl & 0x0f
46    }
47
48    /// Returns the 8-bit field containing the version and IHL.
49    #[inline]
50    pub fn version_ihl(&self) -> u8 {
51        self.header.version_ihl
52    }
53
54    /// Returns the differentiated services code point (DSCP).
55    #[inline]
56    pub fn dscp(&self) -> u8 {
57        self.header.dscp_ecn >> 2
58    }
59
60    /// Returns the explicit congestion notification (ECN).
61    #[inline]
62    pub fn ecn(&self) -> u8 {
63        self.header.dscp_ecn & 0x03
64    }
65
66    /// Returns the differentiated services field.
67    #[inline]
68    pub fn dscp_ecn(&self) -> u8 {
69        self.header.dscp_ecn
70    }
71
72    /// Returns the type of service (former name of the differentiated services field).
73    #[inline]
74    pub fn type_of_service(&self) -> u8 {
75        self.dscp_ecn()
76    }
77
78    /// Returns the total length of the packet in bytes, including the header and data.
79    #[inline]
80    pub fn total_length(&self) -> u16 {
81        self.header.total_length.into()
82    }
83
84    /// Returns the identification field.
85    #[inline]
86    pub fn identification(&self) -> u16 {
87        self.header.identification.into()
88    }
89
90    /// Returns the 16-bit field containing the 3-bit flags and 13-bit fragment offset.
91    #[inline]
92    pub fn flags_to_fragment_offset(&self) -> u16 {
93        self.header.flags_to_fragment_offset.into()
94    }
95
96    /// Returns the 3-bit IP flags.
97    #[inline]
98    pub fn flags(&self) -> u8 {
99        (self.flags_to_fragment_offset() >> 13) as u8
100    }
101
102    /// Returns `true` if the Reserved flag is set.
103    #[inline]
104    pub fn rf(&self) -> bool {
105        (self.flags_to_fragment_offset() & IPV4_RF) != 0
106    }
107
108    /// Returns `true` if the Don't Fragment flag is set.
109    #[inline]
110    pub fn df(&self) -> bool {
111        (self.flags_to_fragment_offset() & IPV4_DF) != 0
112    }
113
114    /// Returns `true` if the More Fragments flag is set.
115    #[inline]
116    pub fn mf(&self) -> bool {
117        (self.flags_to_fragment_offset() & IPV4_MF) != 0
118    }
119
120    /// Returns the fragment offset in units of 8 bytes.
121    #[inline]
122    pub fn fragment_offset(&self) -> u16 {
123        self.flags_to_fragment_offset() & IPV4_FRAG_OFFSET
124    }
125
126    /// Returns the time to live (TTL) of the packet.
127    #[inline]
128    pub fn time_to_live(&self) -> u8 {
129        self.header.time_to_live
130    }
131
132    /// Returns the encapsulated protocol identifier.
133    #[inline]
134    pub fn protocol(&self) -> u8 {
135        self.header.protocol
136    }
137
138    /// Returns the IPv4 header checksum.
139    #[inline]
140    pub fn header_checksum(&self) -> u16 {
141        self.header.header_checksum.into()
142    }
143
144    /// Returns the sender's IPv4 address.
145    #[inline]
146    pub fn src_addr(&self) -> Ipv4Addr {
147        self.header.src_addr
148    }
149
150    /// Returns the receiver's IPv4 address.
151    #[inline]
152    pub fn dst_addr(&self) -> Ipv4Addr {
153        self.header.dst_addr
154    }
155}
156
157impl<'a> Packet<'a> for Ipv4<'a> {
158    fn mbuf(&self) -> &Mbuf {
159        self.mbuf
160    }
161
162    fn header_len(&self) -> usize {
163        self.header.length()
164    }
165
166    fn next_header_offset(&self) -> usize {
167        self.offset + self.header_len()
168    }
169
170    fn next_header(&self) -> Option<usize> {
171        Some(self.protocol().into())
172    }
173
174    fn parse_from(outer: &'a impl Packet<'a>) -> Result<Self>
175    where
176        Self: Sized,
177    {
178        let offset = outer.next_header_offset();
179        if let Ok(header) = outer.mbuf().get_data(offset) {
180            match outer.next_header() {
181                Some(IPV4_PROTOCOL) => Ok(Ipv4 {
182                    header: unsafe { *header },
183                    offset,
184                    mbuf: outer.mbuf(),
185                }),
186                _ => bail!(PacketParseError::InvalidProtocol),
187            }
188        } else {
189            bail!(PacketParseError::InvalidRead)
190        }
191    }
192}
193
194/// Fixed portion of an IPv4 header.
195#[derive(Debug, Clone, Copy)]
196#[repr(C, packed)]
197struct Ipv4Header {
198    version_ihl: u8,
199    dscp_ecn: u8,
200    total_length: u16be,
201    identification: u16be,
202    flags_to_fragment_offset: u16be,
203    time_to_live: u8,
204    protocol: u8,
205    header_checksum: u16be,
206    src_addr: Ipv4Addr,
207    dst_addr: Ipv4Addr,
208}
209
210impl PacketHeader for Ipv4Header {
211    /// Header length measured in bytes. Equivalent to the payload offset.
212    ///
213    /// This differs from the value of the `IHL` field, which measures header length in 32-bit
214    /// words.
215    fn length(&self) -> usize {
216        ((self.version_ihl & 0xf) << 2).into()
217    }
218}