pktparse/
ipv4.rs

1//! Handles parsing of IPv4 headers
2
3use crate::ip::{self, IPProtocol};
4use nom::bits;
5use nom::bytes;
6use nom::error::Error;
7use nom::number;
8use nom::sequence;
9use nom::IResult;
10use std::convert::TryFrom;
11use std::net::Ipv4Addr;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct IPv4Header {
16    pub version: u8,
17    pub ihl: u8,
18    pub tos: u8,
19    pub length: u16,
20    pub id: u16,
21    pub flags: u8,
22    pub fragment_offset: u16,
23    pub ttl: u8,
24    pub protocol: IPProtocol,
25    pub chksum: u16,
26    pub source_addr: Ipv4Addr,
27    pub dest_addr: Ipv4Addr,
28}
29
30fn flag_frag_offset(input: &[u8]) -> IResult<&[u8], (u8, u16)> {
31    bits::bits::<_, _, Error<_>, _, _>(sequence::pair(
32        bits::streaming::take(3u8),
33        bits::streaming::take(13u16),
34    ))(input)
35}
36
37pub(crate) fn address(input: &[u8]) -> IResult<&[u8], Ipv4Addr> {
38    let (input, ipv4) = bytes::streaming::take(4u8)(input)?;
39
40    Ok((input, Ipv4Addr::from(<[u8; 4]>::try_from(ipv4).unwrap())))
41}
42
43pub fn parse_ipv4_header(input: &[u8]) -> IResult<&[u8], IPv4Header> {
44    let (input, verihl) = ip::two_nibbles(input)?;
45    let (input, tos) = number::streaming::be_u8(input)?;
46    let (input, length) = number::streaming::be_u16(input)?;
47    let (input, id) = number::streaming::be_u16(input)?;
48    let (input, flag_frag_offset) = flag_frag_offset(input)?;
49    let (input, ttl) = number::streaming::be_u8(input)?;
50    let (input, protocol) = ip::protocol(input)?;
51    let (input, chksum) = number::streaming::be_u16(input)?;
52    let (input, source_addr) = address(input)?;
53    let (input, dest_addr) = address(input)?;
54
55    Ok((
56        input,
57        IPv4Header {
58            version: verihl.0,
59            ihl: verihl.1,
60            tos,
61            length,
62            id,
63            flags: flag_frag_offset.0,
64            fragment_offset: flag_frag_offset.1,
65            ttl,
66            protocol,
67            chksum,
68            source_addr,
69            dest_addr,
70        },
71    ))
72}
73
74#[cfg(test)]
75mod tests {
76    use super::{ip::protocol, parse_ipv4_header, IPProtocol, IPv4Header};
77    use std::net::Ipv4Addr;
78
79    const EMPTY_SLICE: &'static [u8] = &[];
80    macro_rules! mk_protocol_test {
81        ($func_name:ident, $bytes:expr, $correct_proto:expr) => {
82            #[test]
83            fn $func_name() {
84                let bytes = $bytes;
85                assert_eq!(protocol(&bytes), Ok((EMPTY_SLICE, $correct_proto)));
86            }
87        };
88    }
89
90    mk_protocol_test!(protocol_gets_icmp_correct, [1], IPProtocol::ICMP);
91    mk_protocol_test!(protocol_gets_tcp_correct, [6], IPProtocol::TCP);
92    mk_protocol_test!(protocol_gets_udp_correct, [17], IPProtocol::UDP);
93
94    #[test]
95    fn ipparse_gets_packet_correct() {
96        let bytes = [
97            0x45, /* IP version and length = 20 */
98            0x00, /* Differentiated services field */
99            0x05, 0xdc, /* Total length */
100            0x1a, 0xe6, /* Identification */
101            0x20, 0x00, /* flags and fragment offset */
102            0x40, /* TTL */
103            0x01, /* protocol */
104            0x22, 0xed, /* checksum */
105            0x0a, 0x0a, 0x01, 0x87, /* source IP */
106            0x0a, 0x0a, 0x01, 0xb4, /* destination IP */
107        ];
108
109        let expectation = IPv4Header {
110            version: 4,
111            ihl: 5,
112            tos: 0,
113            length: 1500,
114            id: 0x1ae6,
115            flags: 0x01,
116            fragment_offset: 0,
117            ttl: 64,
118            protocol: IPProtocol::ICMP,
119            chksum: 0x22ed,
120            source_addr: Ipv4Addr::new(10, 10, 1, 135),
121            dest_addr: Ipv4Addr::new(10, 10, 1, 180),
122        };
123        assert_eq!(parse_ipv4_header(&bytes), Ok((EMPTY_SLICE, expectation)));
124    }
125}