libpacket/icmp.rs
1// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! An ICMP packet abstraction.
10
11use crate::{types::*, util, Packet, PrimitiveValues};
12
13/// Represents the "ICMP type" header field.
14#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct IcmpType(pub u8);
16
17impl IcmpType {
18 /// Create a new `IcmpType` instance.
19 pub fn new(val: u8) -> IcmpType {
20 IcmpType(val)
21 }
22}
23
24impl PrimitiveValues for IcmpType {
25 type T = (u8,);
26 fn to_primitive_values(&self) -> (u8,) {
27 (self.0,)
28 }
29}
30
31/// Represents the "ICMP code" header field.
32#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct IcmpCode(pub u8);
34
35impl IcmpCode {
36 /// Create a new `IcmpCode` instance.
37 pub fn new(val: u8) -> IcmpCode {
38 IcmpCode(val)
39 }
40}
41
42impl PrimitiveValues for IcmpCode {
43 type T = (u8,);
44 fn to_primitive_values(&self) -> (u8,) {
45 (self.0,)
46 }
47}
48
49/// Represents a generic ICMP packet.
50#[derive(Debug, Packet)]
51pub struct Icmp {
52 #[construct_with(u8)]
53 pub icmp_type: IcmpType,
54 #[construct_with(u8)]
55 pub icmp_code: IcmpCode,
56 pub checksum: u16be,
57 // theoretically, the header is 64 bytes long, but since the "Rest Of Header" part depends on
58 // the ICMP type and ICMP code, we consider it's part of the payload.
59 // rest_of_header: u32be,
60 #[payload]
61 pub payload: Vec<u8>,
62}
63
64/// Calculates a checksum of an ICMP packet.
65pub fn checksum(packet: &IcmpPacket) -> u16be {
66 util::checksum(packet.packet(), 1)
67}
68
69#[cfg(test)]
70mod checksum_tests {
71 use super::*;
72
73 #[test]
74 fn checksum_zeros() {
75 let mut data = vec![0u8; 8];
76 let expected = 65535;
77 let mut pkg = MutableIcmpPacket::new(&mut data[..]).unwrap();
78 assert_eq!(checksum(&pkg.to_immutable()), expected);
79 pkg.set_checksum(123);
80 assert_eq!(checksum(&pkg.to_immutable()), expected);
81 }
82
83 #[test]
84 fn checksum_nonzero() {
85 let mut data = vec![255u8; 8];
86 let expected = 0;
87 let mut pkg = MutableIcmpPacket::new(&mut data[..]).unwrap();
88 assert_eq!(checksum(&pkg.to_immutable()), expected);
89 pkg.set_checksum(0);
90 assert_eq!(checksum(&pkg.to_immutable()), expected);
91 }
92
93 #[test]
94 fn checksum_odd_bytes() {
95 let mut data = vec![191u8; 7];
96 let expected = 49535;
97 let pkg = IcmpPacket::new(&mut data[..]).unwrap();
98 assert_eq!(checksum(&pkg), expected);
99 }
100}
101
102/// The enumeration of the recognized ICMP types.
103#[allow(non_snake_case)]
104#[allow(non_upper_case_globals)]
105pub mod IcmpTypes {
106
107 use crate::icmp::IcmpType;
108 /// ICMP type for "echo reply" packet.
109 pub const EchoReply: IcmpType = IcmpType(0);
110 /// ICMP type for "destination unreachable" packet.
111 pub const DestinationUnreachable: IcmpType = IcmpType(3);
112 /// ICMP type for "source quench" packet.
113 pub const SourceQuench: IcmpType = IcmpType(4);
114 /// ICMP type for "redirect message" packet.
115 pub const RedirectMessage: IcmpType = IcmpType(5);
116 /// ICMP type for "echo request" packet.
117 pub const EchoRequest: IcmpType = IcmpType(8);
118 /// ICMP type for "router advertisement" packet.
119 pub const RouterAdvertisement: IcmpType = IcmpType(9);
120 /// ICMP type for "router solicitation" packet.
121 pub const RouterSolicitation: IcmpType = IcmpType(10);
122 /// ICMP type for "time exceeded" packet.
123 pub const TimeExceeded: IcmpType = IcmpType(11);
124 /// ICMP type for "parameter problem" packet.
125 pub const ParameterProblem: IcmpType = IcmpType(12);
126 /// ICMP type for "timestamp" packet.
127 pub const Timestamp: IcmpType = IcmpType(13);
128 /// ICMP type for "timestamp reply" packet.
129 pub const TimestampReply: IcmpType = IcmpType(14);
130 /// ICMP type for "information request" packet.
131 pub const InformationRequest: IcmpType = IcmpType(15);
132 /// ICMP type for "information reply" packet.
133 pub const InformationReply: IcmpType = IcmpType(16);
134 /// ICMP type for "address mask request" packet.
135 pub const AddressMaskRequest: IcmpType = IcmpType(17);
136 /// ICMP type for "address mask reply" packet.
137 pub const AddressMaskReply: IcmpType = IcmpType(18);
138 /// ICMP type for "traceroute" packet.
139 pub const Traceroute: IcmpType = IcmpType(30);
140}
141
142pub mod echo_reply {
143 //! abstraction for ICMP "echo reply" packets.
144 //!
145 //! ```text
146 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
147 //! | Type | Code | Checksum |
148 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
149 //! | Identifier | Sequence Number |
150 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 //! | Data ...
152 //! +-+-+-+-+-
153 //! ```
154
155 use crate::icmp::{IcmpCode, IcmpType};
156 use crate::{types::*, Packet, PrimitiveValues};
157
158 /// Represent the "identifier" field of the ICMP echo replay header.
159 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
160 pub struct Identifier(pub u16);
161
162 impl Identifier {
163 /// Create a new `Identifier` instance.
164 pub fn new(val: u16) -> Identifier {
165 Identifier(val)
166 }
167 }
168
169 impl PrimitiveValues for Identifier {
170 type T = (u16,);
171 fn to_primitive_values(&self) -> (u16,) {
172 (self.0,)
173 }
174 }
175
176 /// Represent the "sequence number" field of the ICMP echo replay header.
177 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
178 pub struct SequenceNumber(pub u16);
179
180 impl SequenceNumber {
181 /// Create a new `SequenceNumber` instance.
182 pub fn new(val: u16) -> SequenceNumber {
183 SequenceNumber(val)
184 }
185 }
186
187 impl PrimitiveValues for SequenceNumber {
188 type T = (u16,);
189 fn to_primitive_values(&self) -> (u16,) {
190 (self.0,)
191 }
192 }
193
194 /// Enumeration of available ICMP codes for ICMP echo replay packets. There is actually only
195 /// one, since the only valid ICMP code is 0.
196 #[allow(non_snake_case)]
197 #[allow(non_upper_case_globals)]
198 pub mod IcmpCodes {
199 use crate::icmp::IcmpCode;
200 /// 0 is the only available ICMP code for "echo reply" ICMP packets.
201 pub const NoCode: IcmpCode = IcmpCode(0);
202 }
203
204 /// Represents an ICMP echo reply packet.
205 #[derive(Debug, Packet)]
206 pub struct EchoReply {
207 #[construct_with(u8)]
208 pub icmp_type: IcmpType,
209 #[construct_with(u8)]
210 pub icmp_code: IcmpCode,
211 pub checksum: u16be,
212 pub identifier: u16be,
213 pub sequence_number: u16be,
214 #[payload]
215 pub payload: Vec<u8>,
216 }
217}
218
219pub mod echo_request {
220 //! abstraction for "echo request" ICMP packets.
221 //!
222 //! ```text
223 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
224 //! | Type | Code | Checksum |
225 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
226 //! | Identifier | Sequence Number |
227 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228 //! | Data ...
229 //! +-+-+-+-+-
230 //! ```
231
232 use crate::icmp::{IcmpCode, IcmpType};
233 use crate::{types::*, Packet, PrimitiveValues};
234
235 /// Represents the identifier field.
236 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
237 pub struct Identifier(pub u16);
238
239 impl Identifier {
240 /// Create a new `Identifier` instance.
241 pub fn new(val: u16) -> Identifier {
242 Identifier(val)
243 }
244 }
245
246 impl PrimitiveValues for Identifier {
247 type T = (u16,);
248 fn to_primitive_values(&self) -> (u16,) {
249 (self.0,)
250 }
251 }
252
253 /// Represents the sequence number field.
254 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
255 pub struct SequenceNumber(pub u16);
256
257 impl SequenceNumber {
258 /// Create a new `SequenceNumber` instance.
259 pub fn new(val: u16) -> SequenceNumber {
260 SequenceNumber(val)
261 }
262 }
263
264 impl PrimitiveValues for SequenceNumber {
265 type T = (u16,);
266 fn to_primitive_values(&self) -> (u16,) {
267 (self.0,)
268 }
269 }
270
271 /// Enumeration of available ICMP codes for "echo reply" ICMP packets. There is actually only
272 /// one, since the only valid ICMP code is 0.
273 #[allow(non_snake_case)]
274 #[allow(non_upper_case_globals)]
275 pub mod IcmpCodes {
276 use crate::icmp::IcmpCode;
277 /// 0 is the only available ICMP code for "echo reply" ICMP packets.
278 pub const NoCode: IcmpCode = IcmpCode(0);
279 }
280
281 /// Represents an "echo request" ICMP packet.
282 #[derive(Debug, Packet)]
283 pub struct EchoRequest {
284 #[construct_with(u8)]
285 pub icmp_type: IcmpType,
286 #[construct_with(u8)]
287 pub icmp_code: IcmpCode,
288 pub checksum: u16be,
289 pub identifier: u16be,
290 pub sequence_number: u16be,
291 #[payload]
292 pub payload: Vec<u8>,
293 }
294}
295
296pub mod destination_unreachable {
297 //! abstraction for "destination unreachable" ICMP packets.
298 //!
299 //! ```text
300 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
301 //! | Type | Code | Checksum |
302 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303 //! | unused |
304 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305 //! | Internet Header + 64 bits of Original Data Datagram |
306 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307 //! ```
308
309 use crate::icmp::{IcmpCode, IcmpType};
310 use crate::{types::*, Packet};
311
312 /// Enumeration of the recognized ICMP codes for "destination unreachable" ICMP packets.
313 #[allow(non_snake_case)]
314 #[allow(non_upper_case_globals)]
315 pub mod IcmpCodes {
316 use crate::icmp::IcmpCode;
317 /// ICMP code for "destination network unreachable" packet.
318 pub const DestinationNetworkUnreachable: IcmpCode = IcmpCode(0);
319 /// ICMP code for "destination host unreachable" packet.
320 pub const DestinationHostUnreachable: IcmpCode = IcmpCode(1);
321 /// ICMP code for "destination protocol unreachable" packet.
322 pub const DestinationProtocolUnreachable: IcmpCode = IcmpCode(2);
323 /// ICMP code for "destination port unreachable" packet.
324 pub const DestinationPortUnreachable: IcmpCode = IcmpCode(3);
325 /// ICMP code for "fragmentation required and DFF flag set" packet.
326 pub const FragmentationRequiredAndDFFlagSet: IcmpCode = IcmpCode(4);
327 /// ICMP code for "source route failed" packet.
328 pub const SourceRouteFailed: IcmpCode = IcmpCode(5);
329 /// ICMP code for "destination network unknown" packet.
330 pub const DestinationNetworkUnknown: IcmpCode = IcmpCode(6);
331 /// ICMP code for "destination host unknown" packet.
332 pub const DestinationHostUnknown: IcmpCode = IcmpCode(7);
333 /// ICMP code for "source host isolated" packet.
334 pub const SourceHostIsolated: IcmpCode = IcmpCode(8);
335 /// ICMP code for "network administrative prohibited" packet.
336 pub const NetworkAdministrativelyProhibited: IcmpCode = IcmpCode(9);
337 /// ICMP code for "host administrative prohibited" packet.
338 pub const HostAdministrativelyProhibited: IcmpCode = IcmpCode(10);
339 /// ICMP code for "network unreachable for this Type Of Service" packet.
340 pub const NetworkUnreachableForTOS: IcmpCode = IcmpCode(11);
341 /// ICMP code for "host unreachable for this Type Of Service" packet.
342 pub const HostUnreachableForTOS: IcmpCode = IcmpCode(12);
343 /// ICMP code for "communication administratively prohibited" packet.
344 pub const CommunicationAdministrativelyProhibited: IcmpCode = IcmpCode(13);
345 /// ICMP code for "host precedence violation" packet.
346 pub const HostPrecedenceViolation: IcmpCode = IcmpCode(14);
347 /// ICMP code for "precedence cut off in effect" packet.
348 pub const PrecedenceCutoffInEffect: IcmpCode = IcmpCode(15);
349 }
350
351 /// Represents an "echo request" ICMP packet.
352 #[derive(Debug, Packet)]
353 pub struct DestinationUnreachable {
354 #[construct_with(u8)]
355 pub icmp_type: IcmpType,
356 #[construct_with(u8)]
357 pub icmp_code: IcmpCode,
358 pub checksum: u16be,
359 pub unused: u32be,
360 #[payload]
361 pub payload: Vec<u8>,
362 }
363}
364
365pub mod time_exceeded {
366 //! abstraction for "time exceeded" ICMP packets.
367 //!
368 //! ```text
369 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
370 //! | Type | Code | Checksum |
371 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
372 //! | unused |
373 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374 //! | Internet Header + 64 bits of Original Data Datagram |
375 //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 //! ```
377
378 use crate::icmp::{IcmpCode, IcmpType};
379 use crate::{types::*, Packet};
380
381 /// Enumeration of the recognized ICMP codes for "time exceeded" ICMP packets.
382 #[allow(non_snake_case)]
383 #[allow(non_upper_case_globals)]
384 pub mod IcmpCodes {
385 use crate::icmp::IcmpCode;
386 /// ICMP code for "time to live exceeded in transit" packet.
387 pub const TimeToLiveExceededInTransit: IcmpCode = IcmpCode(0);
388 /// ICMP code for "fragment reassembly time exceeded" packet.
389 pub const FragmentReasemblyTimeExceeded: IcmpCode = IcmpCode(1);
390 }
391
392 /// Represents an "echo request" ICMP packet.
393 #[derive(Debug, Packet)]
394 pub struct TimeExceeded {
395 #[construct_with(u8)]
396 pub icmp_type: IcmpType,
397 #[construct_with(u8)]
398 pub icmp_code: IcmpCode,
399 pub checksum: u16be,
400 pub unused: u32be,
401 #[payload]
402 pub payload: Vec<u8>,
403 }
404}