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}