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}