Skip to main content

icmp_socket2/
packet.rs

1// Copyright 2021 Jeremy Wall
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Packet parsing and construction.
15//!
16//! Where possible we use traits to support a common API for constructing the
17//! ICMPv4 and ICMPv6 versions of the packets.
18//!
19//! Both packet types can be constructed from a slice: `&[u8]` via the [`TryFrom`] trait.
20//!
21//! # Examples
22//!
23//! Constructing an ICMPv4 echo request.
24//! ```
25//! # use icmp_socket::packet::*;
26//! let packet = Icmpv4Packet::with_echo_request(
27//!     42, // An identifier so you can recognize responses to your own packets.
28//!     0, // the first echo request packet in our sequence.
29//!     "a payload big enough to matter".as_bytes().to_vec()
30//! ).unwrap();
31//! ```
32//!
33//! Parsing an ICMPv4 packet from a byte buffer.
34//! ```
35//! # use icmp_socket::packet::*;
36//! use std::convert::TryFrom;
37//! # let packet = Icmpv4Packet::with_echo_request(
38//! #     42, // An identifier so you can recognize responses to your own packets.
39//! #     0, // the first echo request packet in our sequence.
40//! #     "a payload big enough to matter".as_bytes().to_vec()
41//! # ).unwrap();
42//! # let mut byte_buffer = vec![0; 20];
43//! # byte_buffer.extend(packet.get_bytes(true)); // convert a packet to bytes with a checksum.
44//! let parsed_packet = Icmpv4Packet::try_from(byte_buffer.as_slice()).unwrap();
45//! ```
46use std::convert::TryFrom;
47
48use std::net::Ipv6Addr;
49
50fn ipv6_sum_words(ip: &Ipv6Addr) -> u32 {
51    ip.segments().iter().map(|x| *x as u32).sum()
52}
53
54fn sum_big_endian_words(bs: &[u8]) -> u32 {
55    if bs.len() == 0 {
56        return 0;
57    }
58
59    let len = bs.len();
60    let mut data = &bs[..];
61    let mut sum = 0u32;
62    // Iterate by word which is two bytes.
63    while data.len() >= 2 {
64        sum += u16_from_be(&data[0..2]) as u32;
65        // remove the first two bytes now that we've already summed them
66        data = &data[2..];
67    }
68
69    if (len % 2) != 0 {
70        // If odd then checksum the last byte
71        sum += (data[0] as u32) << 8;
72    }
73    return sum;
74}
75
76#[inline]
77fn u16_from_be(data: &[u8]) -> u16 {
78    u16::from_be_bytes(data.try_into().expect("Invalid slice size for u16"))
79}
80
81#[inline]
82fn u32_from_be(data: &[u8]) -> u32 {
83    u32::from_be_bytes(data.try_into().expect("Invalid slice size for u32"))
84}
85
86/// Construct a packet for the EchoRequest messages.
87pub trait WithEchoRequest {
88    type Packet;
89
90    fn with_echo_request(
91        identifier: u16,
92        sequence: u16,
93        payload: Vec<u8>,
94    ) -> Result<Self::Packet, IcmpPacketBuildError>;
95}
96
97/// Construct a packet for Echo Reply messages.
98/// This packet type is really only used for the ICMPv6 protocol.
99pub trait WithEchoReply {
100    type Packet;
101
102    fn with_echo_reply(
103        identifier: u16,
104        sequence: u16,
105        payload: Vec<u8>,
106    ) -> Result<Self::Packet, IcmpPacketBuildError>;
107}
108
109/// Construct a packet for the TimestampRequest messages.
110pub trait WithTimestampRequest {
111    type Packet;
112
113    fn with_timestamp_request(
114        identifier: u16,
115        sequence: u16,
116        originate: u32,
117        receive: u32,
118        transmit: u32,
119    ) -> Result<Self::Packet, IcmpPacketBuildError>;
120}
121
122/// Construct a packet for the TimestampReply messages.
123pub trait WithTimestampReply {
124    type Packet;
125
126    fn with_timestamp_reply(
127        identifier: u16,
128        sequence: u16,
129        originate: u32,
130        receive: u32,
131        transmit: u32,
132    ) -> Result<Self::Packet, IcmpPacketBuildError>;
133}
134
135/// Construct a packet for Destination Unreachable messages.
136pub trait WithUnreachable {
137    type Packet;
138
139    fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
140}
141
142/// Construct a packet for Parameter Problem messages.
143pub trait WithParameterProblem {
144    type Packet;
145    type Pointer;
146
147    fn with_parameter_problem(
148        code: u8,
149        pointer: Self::Pointer,
150        packet: Vec<u8>,
151    ) -> Result<Self::Packet, IcmpPacketBuildError>;
152}
153
154/// Construct a packet for Time Exceeded messages.
155pub trait WithTimeExceeded {
156    type Packet;
157
158    fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
159}
160
161/// The possible Icmpv6 Message types.
162#[derive(Debug, PartialEq)]
163pub enum Icmpv6Message {
164    // NOTE(JWALL): All of the below integers should be parsed as big endian on the
165    // wire.
166    Unreachable {
167        _unused: u32,
168        invoking_packet: Vec<u8>,
169    },
170    PacketTooBig {
171        mtu: u32,
172        invoking_packet: Vec<u8>,
173    },
174    TimeExceeded {
175        _unused: u32,
176        invoking_packet: Vec<u8>,
177    },
178    ParameterProblem {
179        pointer: u32,
180        invoking_packet: Vec<u8>,
181    },
182    PrivateExperimental {
183        padding: u32,
184        payload: Vec<u8>,
185    },
186    EchoRequest {
187        identifier: u16,
188        sequence: u16,
189        payload: Vec<u8>,
190    },
191    EchoReply {
192        identifier: u16,
193        sequence: u16,
194        payload: Vec<u8>,
195    },
196}
197
198use Icmpv6Message::{
199    EchoReply, EchoRequest, PacketTooBig, ParameterProblem, PrivateExperimental, TimeExceeded,
200    Unreachable,
201};
202
203impl Icmpv6Message {
204    /// Get this Icmpv6Message serialized to bytes.
205    pub fn get_bytes(&self) -> Vec<u8> {
206        let mut bytes = Vec::new();
207        match self {
208            Unreachable {
209                _unused: field1,
210                invoking_packet: field2,
211            }
212            | PacketTooBig {
213                mtu: field1,
214                invoking_packet: field2,
215            }
216            | TimeExceeded {
217                _unused: field1,
218                invoking_packet: field2,
219            }
220            | ParameterProblem {
221                pointer: field1,
222                invoking_packet: field2,
223            }
224            | PrivateExperimental {
225                padding: field1,
226                payload: field2,
227            } => {
228                bytes.extend_from_slice(&field1.to_be_bytes());
229                bytes.extend_from_slice(field2);
230            }
231            EchoRequest {
232                identifier,
233                sequence,
234                payload,
235            }
236            | EchoReply {
237                identifier,
238                sequence,
239                payload,
240            } => {
241                bytes.extend_from_slice(&identifier.to_be_bytes());
242                bytes.extend_from_slice(&sequence.to_be_bytes());
243                bytes.extend_from_slice(payload);
244            }
245        }
246        bytes
247    }
248}
249
250#[derive(Debug)]
251pub struct Icmpv6Packet {
252    // NOTE(JWALL): All of the below integers should be parsed as big endian on the
253    // wire.
254    pub typ: u8,
255    pub code: u8,
256    pub checksum: u16,
257    pub message: Icmpv6Message,
258}
259
260/// Error type returned by parsing the ICMP packets.
261#[derive(Debug)]
262pub enum PacketParseError {
263    /// Not enough bytes to properly parse the packet from.
264    PacketTooSmall(usize),
265    /// An unrecognized ICMP type.
266    UnrecognizedICMPType(u8),
267}
268
269impl Icmpv6Packet {
270    /// Construct a packet by parsing the provided bytes.
271    pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
272        let bytes = bytes.as_ref();
273        // NOTE(jwall): All ICMP packets are at least 8 bytes long.
274        if bytes.len() < 8 {
275            return Err(PacketParseError::PacketTooSmall(bytes.len()));
276        }
277        let (typ, code, checksum) = (bytes[0], bytes[1], u16_from_be(&bytes[2..4]));
278        
279        let next_field = u32_from_be(&bytes[4..8]);
280        let payload = bytes[8..].to_owned();
281        let message = match typ {
282            1 => Unreachable {
283                _unused: next_field,
284                invoking_packet: payload,
285            },
286            2 => PacketTooBig {
287                mtu: next_field,
288                invoking_packet: payload,
289            },
290            3 => TimeExceeded {
291                _unused: next_field,
292                invoking_packet: payload,
293            },
294            4 => ParameterProblem {
295                pointer: next_field,
296                invoking_packet: payload,
297            },
298            100 | 101 | 200 | 201 => PrivateExperimental {
299                padding: next_field,
300                payload: payload,
301            },
302            128 => EchoRequest {
303                identifier: u16_from_be(&bytes[4..6]),
304                sequence: u16_from_be(&bytes[6..8]),
305                payload: payload,
306            },
307            129 => EchoReply {
308                identifier: u16_from_be(&bytes[4..6]),
309                sequence: u16_from_be(&bytes[6..8]),
310                payload: payload,
311            },
312            t => return Err(PacketParseError::UnrecognizedICMPType(t)),
313        };
314        return Ok(Icmpv6Packet {
315            typ: typ,
316            code: code,
317            checksum: checksum,
318            message: message,
319        });
320    }
321
322    /// Get this packet serialized to bytes suitable for sending on the wire.
323    pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
324        let mut bytes = Vec::new();
325        bytes.push(self.typ);
326        bytes.push(self.code);
327        bytes.extend_from_slice(&(if with_checksum { self.checksum } else { 0 }).to_be_bytes());
328        bytes.append(&mut self.message.get_bytes());
329        return bytes;
330    }
331
332    /// Calculate the checksum for the packet given the provided source and destination
333    /// addresses.
334    pub fn calculate_checksum(&self, source: &Ipv6Addr, dest: &Ipv6Addr) -> u16 {
335        // First sum the pseudo header
336        let mut sum = 0u32;
337        sum += ipv6_sum_words(source);
338        sum += ipv6_sum_words(dest);
339        // according to rfc4443: https://tools.ietf.org/html/rfc4443#section-2.3
340        // the ip next header value is 58
341        sum += 58;
342
343        // Then sum the len of the message bytes and then the message bytes starting
344        // with the message type field and with the checksum field set to 0.
345        let bytes = self.get_bytes(false);
346        let len = bytes.len();
347        sum += len as u32;
348        sum += sum_big_endian_words(&bytes);
349
350        // handle the carry
351        while sum >> 16 != 0 {
352            sum = (sum >> 16) + (sum & 0xFFFF);
353        }
354        !sum as u16
355    }
356
357    /// Fill the checksum for the packet using the given source and destination
358    /// addresses.
359    pub fn with_checksum(mut self, source: &Ipv6Addr, dest: &Ipv6Addr) -> Self {
360        self.checksum = self.calculate_checksum(source, dest);
361        self
362    }
363
364    /// Construct a packet for Packet Too Big messages.
365    pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
366        Ok(Self {
367            typ: 2,
368            code: 0,
369            checksum: 0,
370            // TODO(jwall): Should we enforce that the packet isn't too big?
371            // It is not supposed to be larger than the minimum IPv6 MTU
372            message: PacketTooBig {
373                mtu: mtu,
374                invoking_packet: packet,
375            },
376        })
377    }
378}
379
380impl WithEchoRequest for Icmpv6Packet {
381    type Packet = Icmpv6Packet;
382
383    fn with_echo_request(
384        identifier: u16,
385        sequence: u16,
386        payload: Vec<u8>,
387    ) -> Result<Self::Packet, IcmpPacketBuildError> {
388        Ok(Self {
389            typ: 128,
390            code: 0,
391            checksum: 0,
392            message: EchoRequest {
393                identifier: identifier,
394                sequence: sequence,
395                payload: payload,
396            },
397        })
398    }
399}
400
401impl WithEchoReply for Icmpv6Packet {
402    type Packet = Icmpv6Packet;
403
404    fn with_echo_reply(
405        identifier: u16,
406        sequence: u16,
407        payload: Vec<u8>,
408    ) -> Result<Self, IcmpPacketBuildError> {
409        Ok(Self {
410            typ: 129,
411            code: 0,
412            checksum: 0,
413            message: EchoReply {
414                identifier: identifier,
415                sequence: sequence,
416                payload: payload,
417            },
418        })
419    }
420}
421
422impl WithUnreachable for Icmpv6Packet {
423    type Packet = Icmpv6Packet;
424
425    fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
426        if code > 6 {
427            return Err(IcmpPacketBuildError::InvalidCode(code));
428        }
429        Ok(Self {
430            typ: 1,
431            code: code,
432            checksum: 0,
433            // TODO(jwall): Should we enforce that the packet isn't too big?
434            // It is not supposed to be larger than the minimum IPv6 MTU
435            message: Unreachable {
436                _unused: 0,
437                invoking_packet: packet,
438            },
439        })
440    }
441}
442
443impl WithParameterProblem for Icmpv6Packet {
444    type Packet = Icmpv6Packet;
445    type Pointer = u32;
446
447    fn with_parameter_problem(
448        code: u8,
449        pointer: Self::Pointer,
450        packet: Vec<u8>,
451    ) -> Result<Self, IcmpPacketBuildError> {
452        if code > 1 {
453            return Err(IcmpPacketBuildError::InvalidCode(code));
454        }
455        Ok(Self {
456            typ: 4,
457            code: code,
458            checksum: 0,
459            message: ParameterProblem {
460                pointer: pointer,
461                invoking_packet: packet,
462            },
463        })
464    }
465}
466
467impl WithTimeExceeded for Icmpv6Packet {
468    type Packet = Icmpv6Packet;
469
470    fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
471        if code > 1 {
472            return Err(IcmpPacketBuildError::InvalidCode(code));
473        }
474        Ok(Self {
475            typ: 3,
476            code: code,
477            checksum: 0,
478            // TODO(jwall): Should we enforce that the packet isn't too big?
479            // It is not supposed to be larger than the minimum IPv6 MTU
480            message: TimeExceeded {
481                _unused: 0,
482                invoking_packet: packet,
483            },
484        })
485    }
486}
487
488impl TryFrom<&[u8]> for Icmpv6Packet {
489    type Error = PacketParseError;
490    fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
491        Icmpv6Packet::parse(b)
492    }
493}
494
495/// Errors returned by constructors for a given packet.
496#[derive(Debug, PartialEq)]
497pub enum IcmpPacketBuildError {
498    /// The code passed in for the payload was invalid for the message type.
499    InvalidCode(u8),
500}
501use IcmpPacketBuildError::InvalidCode;
502
503impl std::fmt::Display for IcmpPacketBuildError {
504    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
505        write!(
506            f,
507            "{}",
508            match self {
509                InvalidCode(c) => format!("Invalid Code: {}", c),
510            }
511        )
512    }
513}
514
515impl std::fmt::Display for PacketParseError {
516    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
517        write!(
518            f,
519            "{}",
520            match self {
521                PacketParseError::PacketTooSmall(c) => format!("Packet Too Small size: {}", c),
522                PacketParseError::UnrecognizedICMPType(t) => format!("UnrecognizedIcmpType({})", t),
523            }
524        )
525    }
526}
527
528impl From<IcmpPacketBuildError> for std::io::Error {
529    fn from(err: IcmpPacketBuildError) -> Self {
530        std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err))
531    }
532}
533
534impl From<PacketParseError> for std::io::Error {
535    fn from(err: PacketParseError) -> Self {
536        std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err))
537    }
538}
539
540/// The various messages for an Icmpv4 packet.
541#[derive(Debug)]
542pub enum Icmpv4Message {
543    Unreachable {
544        // type 3
545        padding: u32,
546        header: Vec<u8>,
547    },
548    TimeExceeded {
549        // type 11
550        padding: u32,
551        header: Vec<u8>,
552    },
553    ParameterProblem {
554        // type 12
555        pointer: u8,
556        padding: (u8, u16),
557        header: Vec<u8>,
558    },
559    Quench {
560        // type 4
561        padding: u32,
562        header: Vec<u8>,
563    },
564    Redirect {
565        // type 5
566        gateway: u32,
567        header: Vec<u8>,
568    },
569    Echo {
570        // type 8
571        identifier: u16,
572        sequence: u16,
573        payload: Vec<u8>,
574    },
575    EchoReply {
576        //  type 0
577        identifier: u16,
578        sequence: u16,
579        payload: Vec<u8>,
580    },
581    Timestamp {
582        // type 13
583        identifier: u16,
584        sequence: u16,
585        originate: u32,
586        receive: u32,
587        transmit: u32,
588    },
589    TimestampReply {
590        // type 14
591        identifier: u16,
592        sequence: u16,
593        originate: u32,
594        receive: u32,
595        transmit: u32,
596    },
597    Information {
598        // type 15
599        identifier: u16,
600        sequence: u16,
601    },
602    InformationReply {
603        // type 16
604        identifier: u16,
605        sequence: u16,
606    },
607}
608
609impl Icmpv4Message {
610    /// Get this Icmpv4Message serialized as bytes.
611    pub fn get_bytes(&self) -> Vec<u8> {
612        let mut bytes = Vec::with_capacity(20);
613        match self {
614            Self::Unreachable {
615                // type 3
616                padding,
617                header,
618            }
619            | Self::TimeExceeded {
620                // type 11
621                padding,
622                header,
623            }
624            | Self::Quench {
625                // type 4
626                padding,
627                header,
628            }
629            | Self::Redirect {
630                // type 5
631                gateway: padding,
632                header,
633            } => {
634                bytes.extend_from_slice(&padding.to_be_bytes());
635                bytes.extend_from_slice(header);
636            }
637            Self::Echo {
638                // type 8
639                identifier,
640                sequence,
641                payload,
642            }
643            | Self::EchoReply {
644                //  type 0
645                identifier,
646                sequence,
647                payload,
648            } => {
649                bytes.extend_from_slice(&identifier.to_be_bytes());
650                bytes.extend_from_slice(&sequence.to_be_bytes());
651                bytes.extend_from_slice(payload);
652            }
653            Self::ParameterProblem {
654                // type 12
655                pointer,
656                padding,
657                header,
658            } => {
659                bytes.push(*pointer);
660                bytes.push(padding.0);
661                bytes.extend_from_slice(&padding.1.to_be_bytes());
662                bytes.extend_from_slice(header);
663            }
664            Self::Timestamp {
665                // type 13
666                identifier,
667                sequence,
668                originate,
669                receive,
670                transmit,
671            }
672            | Self::TimestampReply {
673                // type 14
674                identifier,
675                sequence,
676                originate,
677                receive,
678                transmit,
679            } => {
680                bytes.extend_from_slice(&identifier.to_be_bytes());
681                bytes.extend_from_slice(&sequence.to_be_bytes());
682                bytes.extend_from_slice(&originate.to_be_bytes());
683                bytes.extend_from_slice(&receive.to_be_bytes());
684                bytes.extend_from_slice(&transmit.to_be_bytes());
685            }
686            Self::Information {
687                // type 15
688                identifier,
689                sequence,
690            }
691            | Self::InformationReply {
692                // type 16
693                identifier,
694                sequence,
695            } => {
696                bytes.extend_from_slice(&identifier.to_be_bytes());
697                bytes.extend_from_slice(&sequence.to_be_bytes());
698            }
699        }
700        bytes
701    }
702}
703
704/// An Icmpv4 Packet.
705#[derive(Debug)]
706pub struct Icmpv4Packet {
707    pub typ: u8,
708    pub code: u8,
709    pub checksum: u16,
710    pub message: Icmpv4Message,
711}
712
713impl Icmpv4Packet {
714    /// Parse an Icmpv4Packet from bytes including the IPv4 header.
715    pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
716        let mut bytes = bytes.as_ref();
717        let mut packet_len = bytes.len();
718        if bytes.len() < 28 {
719            return Err(PacketParseError::PacketTooSmall(packet_len));
720        }
721        // NOTE(jwall) Because we use raw sockets the first 20 bytes are the IPv4 header.
722        bytes = &bytes[20..];
723        // NOTE(jwall): All ICMP packets are at least 8 bytes long.
724        packet_len = bytes.len();
725        let (typ, code, checksum) = (bytes[0], bytes[1], u16_from_be(&bytes[2..4]));
726        let message = match typ {
727            3 => Icmpv4Message::Unreachable {
728                padding: u32_from_be(&bytes[4..8]),
729                header: bytes[8..].to_owned(),
730            },
731            11 => Icmpv4Message::TimeExceeded {
732                padding: u32_from_be(&bytes[4..8]),
733                header: bytes[8..].to_owned(),
734            },
735            4 => Icmpv4Message::Quench {
736                padding: u32_from_be(&bytes[4..8]),
737                header: bytes[8..].to_owned(),
738            },
739            5 => Icmpv4Message::Redirect {
740                gateway: u32_from_be(&bytes[4..8]),
741                header: bytes[8..].to_owned(),
742            },
743            8 => Icmpv4Message::Echo {
744                identifier: u16_from_be(&bytes[4..6]),
745                sequence: u16_from_be(&bytes[6..8]),
746                payload: bytes[8..].to_owned(),
747            },
748            0 => Icmpv4Message::EchoReply {
749                identifier: u16_from_be(&bytes[4..6]),
750                sequence: u16_from_be(&bytes[6..8]),
751                payload: bytes[8..].to_owned(),
752            },
753            15 => Icmpv4Message::Information {
754                identifier: u16_from_be(&bytes[4..6]),
755                sequence: u16_from_be(&bytes[6..8]),
756            },
757            16 => Icmpv4Message::InformationReply {
758                identifier: u16_from_be(&bytes[4..6]),
759                sequence: u16_from_be(&bytes[6..8]),
760            },
761            13 => {
762                if packet_len < 20 {
763                    return Err(PacketParseError::PacketTooSmall(bytes.len()));
764                }
765                Icmpv4Message::Timestamp {
766                    identifier: u16_from_be(&bytes[4..6]),
767                    sequence: u16_from_be(&bytes[6..8]),
768                    originate: u32_from_be(&bytes[8..12]),
769                    receive: u32_from_be(&bytes[12..16]),
770                    transmit: u32_from_be(&bytes[16..20]),
771                }
772            }
773            14 => {
774                if packet_len < 20 {
775                    return Err(PacketParseError::PacketTooSmall(bytes.len()));
776                }
777                Icmpv4Message::TimestampReply {
778                    identifier: u16_from_be(&bytes[4..6]),
779                    sequence: u16_from_be(&bytes[6..8]),
780                    originate: u32_from_be(&bytes[8..12]),
781                    receive: u32_from_be(&bytes[12..16]),
782                    transmit: u32_from_be(&bytes[16..20]),
783                }
784            }
785            t => {
786                dbg!(bytes);
787                return Err(PacketParseError::UnrecognizedICMPType(t));
788            }
789        };
790        return Ok(Icmpv4Packet {
791            typ: typ,
792            code: code,
793            checksum: checksum,
794            message: message,
795        });
796    }
797
798    /// Get this packet serialized to bytes suitable for sending on the wire.
799    pub fn get_bytes(&self, with_checksum: bool) -> Vec<u8> {
800        let mut bytes = Vec::new();
801        bytes.push(self.typ);
802        bytes.push(self.code);
803        bytes.extend_from_slice(&(if with_checksum { self.checksum } else { 0 }).to_be_bytes());
804        bytes.append(&mut self.message.get_bytes());
805        return bytes;
806    }
807
808    /// Calculate the checksum for the packet given the provided source and destination
809    /// addresses.
810    pub fn calculate_checksum(&self) -> u16 {
811        // First sum the pseudo header
812        let mut sum = 0u32;
813
814        // Then sum the len of the message bytes and then the message bytes starting
815        // with the message type field and with the checksum field set to 0.
816        let bytes = self.get_bytes(false);
817        sum += sum_big_endian_words(&bytes);
818
819        // handle the carry
820        while sum >> 16 != 0 {
821            sum = (sum >> 16) + (sum & 0xFFFF);
822        }
823        !sum as u16
824    }
825
826    /// Populate the checksum field of this Packet.
827    pub fn with_checksum(mut self) -> Self {
828        self.checksum = self.calculate_checksum();
829        self
830    }
831}
832
833impl WithEchoReply for Icmpv4Packet {
834    type Packet = Icmpv4Packet;
835
836    fn with_echo_reply(
837        identifier: u16,
838        sequence: u16,
839        payload: Vec<u8>,
840    ) -> Result<Self, IcmpPacketBuildError> {
841        Ok(Self {
842            typ: 0,
843            code: 0,
844            checksum: 0,
845            message: Icmpv4Message::EchoReply {
846                identifier: identifier,
847                sequence: sequence,
848                payload: payload,
849            },
850        })
851    }
852}
853
854impl TryFrom<&[u8]> for Icmpv4Packet {
855    type Error = PacketParseError;
856    fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
857        Icmpv4Packet::parse(b)
858    }
859}
860
861impl WithEchoRequest for Icmpv4Packet {
862    type Packet = Icmpv4Packet;
863
864    fn with_echo_request(
865        identifier: u16,
866        sequence: u16,
867        payload: Vec<u8>,
868    ) -> Result<Self::Packet, IcmpPacketBuildError> {
869        Ok(Self {
870            typ: 8,
871            code: 0,
872            checksum: 0,
873            message: Icmpv4Message::Echo {
874                identifier,
875                sequence,
876                payload,
877            },
878        })
879    }
880}
881
882impl WithTimestampRequest for Icmpv4Packet {
883    type Packet = Icmpv4Packet;
884
885    fn with_timestamp_request(
886        identifier: u16,
887        sequence: u16,
888        originate: u32,
889        receive: u32,
890        transmit: u32,
891    ) -> Result<Self::Packet, IcmpPacketBuildError> {
892        Ok(Self {
893            typ: 13,
894            code: 0,
895            checksum: 0,
896            message: Icmpv4Message::Timestamp {
897                identifier,
898                sequence,
899                originate,
900                receive,
901                transmit,
902            },
903        })
904    }
905}
906
907impl WithTimestampReply for Icmpv4Packet {
908    type Packet = Icmpv4Packet;
909
910    fn with_timestamp_reply(
911        identifier: u16,
912        sequence: u16,
913        originate: u32,
914        receive: u32,
915        transmit: u32,
916    ) -> Result<Self::Packet, IcmpPacketBuildError> {
917        Ok(Self {
918            typ: 14,
919            code: 0,
920            checksum: 0,
921            message: Icmpv4Message::TimestampReply {
922                identifier,
923                sequence,
924                originate,
925                receive,
926                transmit,
927            },
928        })
929    }
930}
931
932impl WithUnreachable for Icmpv4Packet {
933    type Packet = Icmpv4Packet;
934
935    fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
936        if code > 5 {
937            return Err(IcmpPacketBuildError::InvalidCode(code));
938        }
939        Ok(Self {
940            typ: 3,
941            code: code,
942            checksum: 0,
943            message: Icmpv4Message::Unreachable {
944                padding: 0,
945                header: packet,
946            },
947        })
948    }
949}
950
951impl WithParameterProblem for Icmpv4Packet {
952    type Packet = Icmpv4Packet;
953    type Pointer = u8;
954
955    fn with_parameter_problem(
956        code: u8,
957        pointer: Self::Pointer,
958        packet: Vec<u8>,
959    ) -> Result<Self::Packet, IcmpPacketBuildError> {
960        if code != 0 {
961            return Err(IcmpPacketBuildError::InvalidCode(code));
962        }
963        Ok(Self {
964            typ: 12,
965            code: code,
966            checksum: 0,
967            message: Icmpv4Message::ParameterProblem {
968                pointer: pointer,
969                padding: (0, 0),
970                header: packet,
971            },
972        })
973    }
974}
975
976impl WithTimeExceeded for Icmpv4Packet {
977    type Packet = Icmpv4Packet;
978
979    fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
980        if code > 1 {
981            return Err(IcmpPacketBuildError::InvalidCode(code));
982        }
983        Ok(Self {
984            typ: 11,
985            code: code,
986            checksum: 0,
987            message: Icmpv4Message::TimeExceeded {
988                padding: 0,
989                header: packet,
990            },
991        })
992    }
993}
994
995#[cfg(test)]
996mod tests {
997    use super::*;
998
999    #[test]
1000    fn packet_construction_echo_request_test() {
1001        let pkt = Icmpv6Packet::with_echo_request(42, 1, vec![1, 2, 3, 4]).unwrap();
1002        assert_eq!(pkt.typ, 128);
1003        assert_eq!(pkt.code, 0);
1004        assert_eq!(
1005            pkt.message,
1006            EchoRequest {
1007                identifier: 42,
1008                sequence: 1,
1009                payload: vec![1, 2, 3, 4],
1010            }
1011        );
1012    }
1013
1014    #[test]
1015    fn packet_construction_echo_reply_test() {
1016        let pkt = Icmpv6Packet::with_echo_reply(42, 1, vec![1, 2, 3, 4]).unwrap();
1017        assert_eq!(pkt.typ, 129);
1018        assert_eq!(pkt.code, 0);
1019        assert_eq!(
1020            pkt.message,
1021            EchoReply {
1022                identifier: 42,
1023                sequence: 1,
1024                payload: vec![1, 2, 3, 4],
1025            }
1026        );
1027    }
1028
1029    #[test]
1030    fn packet_construction_too_big_test() {
1031        let pkt = Icmpv6Packet::with_packet_too_big(3, vec![1, 2, 3, 4]).unwrap();
1032        assert_eq!(pkt.typ, 2);
1033        assert_eq!(pkt.code, 0);
1034        assert_eq!(
1035            pkt.message,
1036            PacketTooBig {
1037                mtu: 3,
1038                invoking_packet: vec![1, 2, 3, 4],
1039            }
1040        );
1041    }
1042
1043    #[test]
1044    fn packet_construction_time_exceeded() {
1045        let pkt = Icmpv6Packet::with_time_exceeded(0, vec![1, 2, 3, 4]).unwrap();
1046        assert_eq!(pkt.typ, 3);
1047        assert_eq!(pkt.code, 0);
1048        assert_eq!(
1049            pkt.message,
1050            TimeExceeded {
1051                _unused: 0,
1052                invoking_packet: vec![1, 2, 3, 4],
1053            }
1054        );
1055    }
1056
1057    #[test]
1058    fn packet_construction_time_exceeded_invalid_code() {
1059        let pkt = Icmpv6Packet::with_time_exceeded(2, vec![1, 2, 3, 4]);
1060        assert!(pkt.is_err());
1061        let e = pkt.unwrap_err();
1062        assert_eq!(e, IcmpPacketBuildError::InvalidCode(2));
1063    }
1064
1065    #[test]
1066    fn packet_construction_parameter_problem() {
1067        let pkt = Icmpv6Packet::with_parameter_problem(0, 30, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
1068            .unwrap();
1069        assert_eq!(pkt.typ, 4);
1070        assert_eq!(pkt.code, 0);
1071        assert_eq!(
1072            pkt.message,
1073            ParameterProblem {
1074                pointer: 30,
1075                invoking_packet: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1076            }
1077        );
1078    }
1079
1080    #[test]
1081    fn packet_construction_parameter_problem_invalid_code() {
1082        let pkt = Icmpv6Packet::with_parameter_problem(3, 30, vec![1, 2, 3, 4]);
1083        assert!(pkt.is_err());
1084        let e = pkt.unwrap_err();
1085        assert_eq!(e, IcmpPacketBuildError::InvalidCode(3));
1086    }
1087
1088    #[test]
1089    fn echo_packet_parse_test() {
1090        // NOTE(jwall): I am shamelessly ripping ff the cases for this from libpnet
1091        // The equivalent of your typical ping -6 ::1%lo
1092        let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
1093        let mut data = vec![
1094            0x80, // Icmpv6 Type
1095            0x00, // Code
1096            0xff, 0xff, // Checksum
1097            0x00, 0x00, // Id
1098            0x00, 0x01, // Sequence
1099            // 56 bytes of "random" data
1100            0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
1101            0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
1102            0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
1103            0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
1104        ];
1105        let mut pkt = Icmpv6Packet::parse(&data).unwrap();
1106        assert_eq!(pkt.typ, 128);
1107        assert_eq!(pkt.code, 0x00);
1108        if let EchoRequest {
1109            identifier,
1110            sequence,
1111            payload,
1112        } = &pkt.message
1113        {
1114            assert_eq!(*identifier, 0);
1115            assert_eq!(*sequence, 1);
1116            assert_eq!(
1117                payload,
1118                &[
1119                    0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
1120                    0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
1121                    0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
1122                    0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
1123                    0x69, 0x20, 0x20, 0x20
1124                ]
1125            );
1126        } else {
1127            assert!(
1128                false,
1129                "Packet did not parse as an EchoRequest {:?}",
1130                pkt.message
1131            );
1132        }
1133        assert_eq!(pkt.get_bytes(true), data);
1134        assert_eq!(pkt.calculate_checksum(lo, lo), 0x1d2e);
1135        pkt = pkt.with_checksum(lo, lo);
1136        assert_eq!(pkt.checksum, 0x1d2e);
1137
1138        // Check echo response as well
1139        data[0] = 0x81;
1140        let pkt = Icmpv6Packet::parse(&data).unwrap();
1141        assert_eq!(pkt.typ, 129);
1142        assert_eq!(pkt.code, 0);
1143        if let EchoReply {
1144            identifier,
1145            sequence,
1146            payload,
1147        } = &pkt.message
1148        {
1149            assert_eq!(*identifier, 0);
1150            assert_eq!(*sequence, 1);
1151            assert_eq!(
1152                payload,
1153                &[
1154                    0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68,
1155                    0x20, 0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62,
1156                    0x75, 0x74, 0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20,
1157                    0x20, 0x6b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e,
1158                    0x69, 0x20, 0x20, 0x20
1159                ]
1160            );
1161        } else {
1162            assert!(
1163                false,
1164                "Packet did not parse as an EchoReply {:?}",
1165                pkt.message
1166            );
1167        }
1168        assert_eq!(pkt.get_bytes(true), data);
1169        assert_eq!(pkt.calculate_checksum(lo, lo), 0x1c2e);
1170    }
1171}