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