1use crate::ipv4::{address, parse_ipv4_header, IPv4Header};
4use nom::{bytes::streaming::take, number, IResult};
5use std::net::Ipv4Addr;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum Unreachable {
10    DestinationNetworkUnreachable,
11    DestinationHostUnreachable,
12    DestinationProtocolUnreachable,
13    DestinationPortUnreachable,
14    FragmentationRequired,
15    SourceRouteFailed,
16    DestinationNetworkUnknown,
17    DestinationHostUnknown,
18    SourceHostIsolated,
19    NetworkAdministrativelyProhibited,
20    HostAdministrativelyProhibited,
21    NetworkUnreachableForTos,
22    HostUnreachableForTos,
23    CommunicationAdministrativelyProhibited,
24    HostPrecedenceViolation,
25    PrecedentCutoffInEffect,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub enum Redirect {
31    Network,
32    Host,
33    TosAndNetwork,
34    TosAndHost,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub enum TimeExceeded {
40    TTL,
41    FragmentReassembly,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub enum ParameterProblem {
47    Pointer,
48    MissingRequiredOption,
49    BadLength,
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub enum ExtendedEchoReply {
55    NoError,
56    MalformedQuery,
57    NoSuchInterface,
58    NoSuchTableEntry,
59    MupltipleInterfacesStatisfyQuery,
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64pub enum IcmpCode {
65    EchoReply,
66    Reserved,
67    DestinationUnreachable(Unreachable),
68    SourceQuench,
69    Redirect(Redirect),
70    EchoRequest,
71    RouterAdvertisment,
72    RouterSolicication,
73    TimeExceeded(TimeExceeded),
74    ParameterProblem(ParameterProblem),
75    Timestamp,
76    TimestampReply,
77    ExtendedEchoRequest,
78    ExtendedEchoReply(ExtendedEchoReply),
79    Other(u16),
80}
81
82impl From<u16> for IcmpCode {
83    fn from(raw: u16) -> Self {
84        let [t, c] = raw.to_be_bytes();
85        match t {
86            0x00 => Self::EchoReply,
87            0x01 => Self::Reserved,
88            0x02 => Self::Reserved,
89            0x03 => match c {
90                0x00 => Self::DestinationUnreachable(Unreachable::DestinationNetworkUnreachable),
91                0x01 => Self::DestinationUnreachable(Unreachable::DestinationHostUnreachable),
92                0x02 => Self::DestinationUnreachable(Unreachable::DestinationProtocolUnreachable),
93                0x03 => Self::DestinationUnreachable(Unreachable::DestinationPortUnreachable),
94                0x04 => Self::DestinationUnreachable(Unreachable::FragmentationRequired),
95                0x05 => Self::DestinationUnreachable(Unreachable::SourceRouteFailed),
96                0x06 => Self::DestinationUnreachable(Unreachable::DestinationNetworkUnknown),
97                0x07 => Self::DestinationUnreachable(Unreachable::DestinationHostUnknown),
98                0x08 => Self::DestinationUnreachable(Unreachable::SourceHostIsolated),
99                0x09 => {
100                    Self::DestinationUnreachable(Unreachable::NetworkAdministrativelyProhibited)
101                }
102                0x0A => Self::DestinationUnreachable(Unreachable::HostAdministrativelyProhibited),
103                0x0B => Self::DestinationUnreachable(Unreachable::NetworkUnreachableForTos),
104                0x0C => Self::DestinationUnreachable(Unreachable::HostUnreachableForTos),
105                0x0D => Self::DestinationUnreachable(
106                    Unreachable::CommunicationAdministrativelyProhibited,
107                ),
108                0x0E => Self::DestinationUnreachable(Unreachable::HostPrecedenceViolation),
109                0x0F => Self::DestinationUnreachable(Unreachable::PrecedentCutoffInEffect),
110                _ => Self::Other(raw),
111            },
112            0x04 => match c {
113                0x00 => Self::SourceQuench,
114                _ => Self::Other(raw),
115            },
116            0x05 => match c {
117                0x00 => Self::Redirect(Redirect::Network),
118                0x01 => Self::Redirect(Redirect::Host),
119                0x02 => Self::Redirect(Redirect::TosAndNetwork),
120                0x03 => Self::Redirect(Redirect::TosAndHost),
121                _ => Self::Other(raw),
122            },
123            0x07 => Self::Reserved,
124            0x08 => Self::EchoRequest,
125            0x09 => Self::RouterAdvertisment,
126            0x0A => Self::RouterSolicication,
127            0x0B => match c {
128                0x00 => Self::TimeExceeded(TimeExceeded::TTL),
129                0x01 => Self::TimeExceeded(TimeExceeded::FragmentReassembly),
130                _ => Self::Other(raw),
131            },
132            0x0C => match c {
133                0x00 => Self::ParameterProblem(ParameterProblem::Pointer),
134                0x01 => Self::ParameterProblem(ParameterProblem::MissingRequiredOption),
135                0x02 => Self::ParameterProblem(ParameterProblem::BadLength),
136                _ => Self::Other(raw),
137            },
138            0x0D => Self::Timestamp,
139            0x0E => Self::TimestampReply,
140            0x2A => Self::ExtendedEchoRequest,
141            0x2B => match c {
142                0x00 => Self::ExtendedEchoReply(ExtendedEchoReply::NoError),
143                0x01 => Self::ExtendedEchoReply(ExtendedEchoReply::MalformedQuery),
144                0x02 => Self::ExtendedEchoReply(ExtendedEchoReply::NoSuchInterface),
145                0x03 => Self::ExtendedEchoReply(ExtendedEchoReply::NoSuchTableEntry),
146                0x04 => {
147                    Self::ExtendedEchoReply(ExtendedEchoReply::MupltipleInterfacesStatisfyQuery)
148                }
149                _ => Self::Other(raw),
150            },
151            _ => Self::Other(raw),
152        }
153    }
154}
155
156fn parse_icmp_code(input: &[u8]) -> IResult<&[u8], IcmpCode> {
157    let (input, code) = number::streaming::be_u16(input)?;
158
159    Ok((input, code.into()))
160}
161
162#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164#[repr(transparent)]
165pub struct IcmpPayloadPacket([u8; 8]);
166
167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169pub enum IcmpData {
170    Unreachable {
171        nexthop_mtu: u16,
172        header: IPv4Header,
173        packet: IcmpPayloadPacket,
174    },
175    Redirect {
176        gateway: Ipv4Addr,
177        header: IPv4Header,
178        packet: IcmpPayloadPacket,
179    },
180    TimeExceeded {
181        header: IPv4Header,
182        packet: IcmpPayloadPacket,
183    },
184    None,
185}
186
187fn parse_ipv4_header_and_packet(input: &[u8]) -> IResult<&[u8], (IPv4Header, IcmpPayloadPacket)> {
188    let (input, header) = parse_ipv4_header(input)?;
189    let mut packet: [u8; 8] = Default::default();
190    let (input, data) = take(8usize)(input)?;
191    packet.copy_from_slice(data);
192
193    Ok((input, (header, IcmpPayloadPacket(packet))))
194}
195
196fn parse_icmp_unreachable_data(input: &[u8]) -> IResult<&[u8], IcmpData> {
197    let (input, _) = number::streaming::be_u16(input)?;
198    let (input, nexthop_mtu) = number::streaming::be_u16(input)?;
199    let (input, (header, packet)) = parse_ipv4_header_and_packet(input)?;
200
201    Ok((
202        input,
203        IcmpData::Unreachable {
204            nexthop_mtu,
205            header,
206            packet,
207        },
208    ))
209}
210
211fn parse_icmp_redirect_data(input: &[u8]) -> IResult<&[u8], IcmpData> {
212    let (input, gateway) = address(input)?;
213    let (input, (header, packet)) = parse_ipv4_header_and_packet(input)?;
214
215    Ok((
216        input,
217        IcmpData::Redirect {
218            gateway,
219            header,
220            packet,
221        },
222    ))
223}
224
225fn parse_icmp_timeexceeded_data(input: &[u8]) -> IResult<&[u8], IcmpData> {
226    let (input, _) = number::streaming::be_u32(input)?;
227    let (input, (header, packet)) = parse_ipv4_header_and_packet(input)?;
228
229    Ok((input, IcmpData::TimeExceeded { header, packet }))
230}
231
232#[derive(Clone, Copy, Debug, PartialEq, Eq)]
233#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
234pub struct IcmpHeader {
235    pub code: IcmpCode,
236    pub checksum: u16,
237    pub data: IcmpData,
238}
239
240pub fn parse_icmp_header(input: &[u8]) -> IResult<&[u8], IcmpHeader> {
241    let (input, code) = parse_icmp_code(input)?;
242    let (input, checksum) = number::streaming::be_u16(input)?;
243
244    let (input, data) = match code {
245        IcmpCode::DestinationUnreachable(_) => parse_icmp_unreachable_data(input)?,
246        IcmpCode::Redirect(_) => parse_icmp_redirect_data(input)?,
247        IcmpCode::TimeExceeded(_) => parse_icmp_timeexceeded_data(input)?,
248        _ => (input, IcmpData::None),
249    };
250
251    Ok((
252        input,
253        IcmpHeader {
254            code,
255            checksum,
256            data,
257        },
258    ))
259}
260
261#[cfg(test)]
262mod tests {
263    use super::{
264        parse_icmp_header, IcmpCode, IcmpData, IcmpHeader, IcmpPayloadPacket, Redirect, Unreachable,
265    };
266    use crate::ip::IPProtocol;
267    use crate::ipv4::IPv4Header;
268    use nom::{Err, Needed};
269    use std::net::Ipv4Addr;
270
271    const EMPTY_SLICE: &'static [u8] = &[];
272
273    fn get_icmp_ipv4_header_and_packet() -> (IPv4Header, IcmpPayloadPacket) {
274        (
275            IPv4Header {
276                version: 4,
277                ihl: 5,
278                tos: 0,
279                length: 1500,
280                id: 0x1ae6,
281                flags: 0x01,
282                fragment_offset: 0,
283                ttl: 64,
284                protocol: IPProtocol::ICMP,
285                chksum: 0x22ed,
286                source_addr: Ipv4Addr::new(10, 10, 1, 135),
287                dest_addr: Ipv4Addr::new(10, 10, 1, 180),
288            },
289            IcmpPayloadPacket([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]),
290        )
291    }
292
293    fn get_icmp_redirect_data() -> (Vec<u8>, IcmpHeader) {
294        let bytes = [
295            5, 1, 0xaa, 0xbb, 0x0a, 0x0a, 0x01, 0x86, 0x45, 0x00, 0x05, 0xdc, 0x1a, 0xe6, 0x20, 0x00, 0x40, 0x01, 0x22, 0xed, 0x0a, 0x0a, 0x01, 0x87, 0x0a, 0x0a, 0x01, 0xb4, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
310        ];
311
312        let (header, packet) = get_icmp_ipv4_header_and_packet();
313
314        let expected = IcmpHeader {
315            code: IcmpCode::Redirect(Redirect::Host),
316            checksum: 0xaabb,
317            data: IcmpData::Redirect {
318                gateway: Ipv4Addr::new(10, 10, 1, 134),
319                header: header,
320                packet: packet,
321            },
322        };
323
324        (bytes.to_vec(), expected)
325    }
326
327    fn get_icmp_unreachable_data() -> (Vec<u8>, IcmpHeader) {
328        let bytes = [
329            3, 1, 0xaa, 0xbb, 0x00, 0x00, 0x00, 0x7,  0x45, 0x00, 0x05, 0xdc, 0x1a, 0xe6, 0x20, 0x00, 0x40, 0x01, 0x22, 0xed, 0x0a, 0x0a, 0x01, 0x87, 0x0a, 0x0a, 0x01, 0xb4, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
345        ];
346
347        let (header, packet) = get_icmp_ipv4_header_and_packet();
348
349        let expected = IcmpHeader {
350            code: IcmpCode::DestinationUnreachable(Unreachable::DestinationHostUnreachable),
351            checksum: 0xaabb,
352            data: IcmpData::Unreachable {
353                nexthop_mtu: 7,
354                header: header,
355                packet: packet,
356            },
357        };
358
359        (bytes.to_vec(), expected)
360    }
361
362    #[test]
363    fn icmp_unreachable() {
364        let (bytes, expected) = get_icmp_unreachable_data();
365        assert_eq!(parse_icmp_header(&bytes), Ok((EMPTY_SLICE, expected)))
366    }
367
368    #[test]
369    fn icmp_unreachable_incomplete() {
370        let (mut bytes, _) = get_icmp_unreachable_data();
371        bytes.pop();
372
373        assert_eq!(
374            parse_icmp_header(&bytes),
375            Err(Err::Incomplete(Needed::new(1)))
376        )
377    }
378
379    #[test]
380    fn icmp_redirect() {
381        let (bytes, expected) = get_icmp_redirect_data();
382        assert_eq!(parse_icmp_header(&bytes), Ok((EMPTY_SLICE, expected)))
383    }
384
385    #[test]
386    fn icmp_redirect_incomplete() {
387        let (mut bytes, _) = get_icmp_redirect_data();
388        bytes.pop();
389
390        assert_eq!(
391            parse_icmp_header(&bytes),
392            Err(Err::Incomplete(Needed::new(1)))
393        )
394    }
395}