Skip to main content

packet_formats/icmp/
mod.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Parsing and serialization of Internet Control Message Protocol (ICMP)
6//! packets.
7//!
8//! This module supports both ICMPv4 and ICMPv6.
9//!
10//! The ICMPv4 packet format is defined in [RFC 792], and the ICMPv6
11//! packet format is defined in [RFC 4443 Section 2.1].
12//!
13//! [RFC 792]: https://datatracker.ietf.org/doc/html/rfc792
14//! [RFC 4443 Section 2.1]: https://datatracker.ietf.org/doc/html/rfc4443#section-2.1
15
16#[macro_use]
17mod macros;
18mod common;
19mod icmpv4;
20mod icmpv6;
21pub mod mld;
22pub mod ndp;
23
24#[cfg(test)]
25mod testdata;
26
27pub use self::common::*;
28pub use self::icmpv4::*;
29pub use self::icmpv6::*;
30
31use core::fmt::Debug;
32use core::marker::PhantomData;
33use core::{cmp, mem};
34
35use byteorder::{ByteOrder, NetworkEndian};
36use derivative::Derivative;
37use internet_checksum::Checksum;
38use net_types::ip::{GenericOverIp, Ip, IpAddress, IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
39use packet::records::options::{Options, OptionsImpl};
40use packet::{
41    AsFragmentedByteSlice, BufferView, FragmentedByteSlice, FragmentedBytesMut, FromRaw,
42    NestablePacketBuilder, NoOpSerializationContext, PacketBuilder, PacketConstraints,
43    ParsablePacket, ParseMetadata, PartialPacketBuilder, SerializationContext, SerializeTarget,
44};
45use zerocopy::byteorder::network_endian::U16;
46use zerocopy::{
47    FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, Unaligned,
48};
49
50use crate::error::{NotZeroError, ParseError, ParseResult};
51use crate::ip::{IpProtoExt, Ipv4Proto, Ipv6Proto};
52use crate::ipv4::{self, Ipv4PacketRaw};
53use crate::ipv6::Ipv6PacketRaw;
54
55#[derive(Copy, Clone, Default, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
56#[repr(C)]
57struct HeaderPrefix {
58    msg_type: u8,
59    code: u8,
60    checksum: [u8; 2],
61    /* NOTE: The "Rest of Header" field is stored in message types rather than
62     * in the HeaderPrefix. This helps consolidate how callers access data about the
63     * packet, and is consistent with ICMPv6, which treats the field as part of
64     * messages rather than the header. */
65}
66
67impl HeaderPrefix {
68    fn set_msg_type<T: Into<u8>>(&mut self, msg_type: T) {
69        self.msg_type = msg_type.into();
70    }
71}
72
73/// Peek at an ICMP header to see what message type is present.
74///
75/// Since `IcmpPacket` is statically typed with the message type expected, this
76/// type must be known ahead of time before calling `parse`. If multiple
77/// different types are valid in a given parsing context, and so the caller
78/// cannot know ahead of time which type to use, `peek_message_type` can be used
79/// to peek at the header first to figure out which static type should be used
80/// in a subsequent call to `parse`.
81///
82/// Note that `peek_message_type` only inspects certain fields in the header,
83/// and so `peek_message_type` succeeding does not guarantee that a subsequent
84/// call to `parse` will also succeed.
85pub fn peek_message_type<MessageType: TryFrom<u8>>(bytes: &[u8]) -> ParseResult<MessageType> {
86    let (hdr_pfx, _) = Ref::<_, HeaderPrefix>::from_prefix(bytes).map_err(Into::into).map_err(
87        |_: zerocopy::SizeError<_, _>| debug_err!(ParseError::Format, "too few bytes for header"),
88    )?;
89    MessageType::try_from(hdr_pfx.msg_type).map_err(|_| {
90        debug_err!(ParseError::NotSupported, "unrecognized message type: {:x}", hdr_pfx.msg_type,)
91    })
92}
93
94/// An extension trait adding ICMP-related functionality to `Ipv4` and `Ipv6`.
95pub trait IcmpIpExt: IpProtoExt {
96    /// The ICMP packet type for this IP version.
97    type IcmpPacketTypeRaw<B: SplitByteSliceMut>: IcmpPacketTypeRaw<B, Self>
98        + GenericOverIp<Self, Type = Self::IcmpPacketTypeRaw<B>>
99        + GenericOverIp<Ipv4, Type = Icmpv4PacketRaw<B>>
100        + GenericOverIp<Ipv6, Type = Icmpv6PacketRaw<B>>;
101
102    /// The type of ICMP messages.
103    ///
104    /// For `Ipv4`, this is `Icmpv4MessageType`, and for `Ipv6`, this is
105    /// `Icmpv6MessageType`.
106    type IcmpMessageType: IcmpMessageType
107        + GenericOverIp<Self, Type = Self::IcmpMessageType>
108        + GenericOverIp<Ipv4, Type = Icmpv4MessageType>
109        + GenericOverIp<Ipv6, Type = Icmpv6MessageType>;
110
111    /// The type of an ICMP Destination Unreachable Code.
112    ///
113    /// For `Ipv4`, this is `Icmpv4DestUnreachableCode`, and for `Ipv6` this
114    /// is `Icmpv6DestUnreachableCode`.
115    type DestUnreachableCode: PartialEq + Send + Sync + Debug;
116
117    /// The type of an ICMP parameter problem code.
118    ///
119    /// For `Ipv4`, this is `Icmpv4ParameterProblemCode`, and for `Ipv6` this
120    /// is `Icmpv6ParameterProblemCode`.
121    type ParameterProblemCode: PartialEq + Send + Sync + Debug;
122
123    /// The type of an ICMP parameter problem pointer.
124    ///
125    /// For `Ipv4`, this is `u8`, and for `Ipv6` this is `u32`.
126    type ParameterProblemPointer: PartialEq + Send + Sync + Debug;
127
128    /// The identifier for this ICMP version.
129    ///
130    /// This value will be found in an IPv4 packet's Protocol field (for ICMPv4
131    /// packets) or an IPv6 fixed header's or last extension header's Next
132    /// Heeader field (for ICMPv6 packets).
133    const ICMP_IP_PROTO: <Self as IpProtoExt>::Proto;
134
135    /// Computes the length of the header of the packet prefix stored in
136    /// `bytes`.
137    ///
138    /// Given the prefix of a packet stored in `bytes`, compute the length of
139    /// the header of that packet, or `bytes.len()` if `bytes` does not contain
140    /// the entire header. If the version is IPv6, the returned length should
141    /// include all extension headers.
142    fn header_len(bytes: &[u8]) -> usize;
143
144    /// Icmp{v4,v6}MessageType::EchoReply.
145    const ECHO_REPLY: Self::IcmpMessageType;
146    /// Icmp{v4,v6}MessageType::EchoRequest.
147    const ECHO_REQUEST: Self::IcmpMessageType;
148}
149
150impl IcmpIpExt for Ipv4 {
151    type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv4PacketRaw<B>;
152    type IcmpMessageType = Icmpv4MessageType;
153    type DestUnreachableCode = Icmpv4DestUnreachableCode;
154    type ParameterProblemCode = Icmpv4ParameterProblemCode;
155    type ParameterProblemPointer = u8;
156
157    const ICMP_IP_PROTO: Ipv4Proto = Ipv4Proto::Icmp;
158
159    fn header_len(bytes: &[u8]) -> usize {
160        if bytes.len() < ipv4::IPV4_MIN_HDR_LEN {
161            return bytes.len();
162        }
163        let (header_prefix, _) = Ref::<_, ipv4::HeaderPrefix>::from_prefix(bytes).unwrap();
164        cmp::min(header_prefix.ihl() as usize * 4, bytes.len())
165    }
166
167    const ECHO_REPLY: Icmpv4MessageType = Icmpv4MessageType::EchoReply;
168    const ECHO_REQUEST: Icmpv4MessageType = Icmpv4MessageType::EchoRequest;
169}
170
171impl IcmpIpExt for Ipv6 {
172    type IcmpPacketTypeRaw<B: SplitByteSliceMut> = Icmpv6PacketRaw<B>;
173    type IcmpMessageType = Icmpv6MessageType;
174    type DestUnreachableCode = Icmpv6DestUnreachableCode;
175    type ParameterProblemCode = Icmpv6ParameterProblemCode;
176    type ParameterProblemPointer = u32;
177
178    const ICMP_IP_PROTO: Ipv6Proto = Ipv6Proto::Icmpv6;
179
180    // TODO: Re-implement this in terms of partial parsing, and then get rid of
181    // the `header_len` method.
182    fn header_len(_bytes: &[u8]) -> usize {
183        // NOTE: We panic here rather than doing log_unimplemented! because
184        // there's no sane default value for this function. If it's called, it
185        // doesn't make sense for the program to continue executing; if we did,
186        // it would cause bugs in the caller.
187        unimplemented!()
188    }
189
190    const ECHO_REPLY: Icmpv6MessageType = Icmpv6MessageType::EchoReply;
191    const ECHO_REQUEST: Icmpv6MessageType = Icmpv6MessageType::EchoRequest;
192}
193
194/// An ICMP or ICMPv6 packet
195///
196/// 'IcmpPacketType' is implemented by `Icmpv4Packet` and `Icmpv6Packet`
197pub trait IcmpPacketTypeRaw<B: SplitByteSliceMut, I: Ip>:
198    Sized + ParsablePacket<B, (), Error = ParseError>
199{
200    /// Update the checksum to reflect an updated address in the pseudo header.
201    fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr);
202
203    /// Update the checksum to reflect a field change in the header.
204    ///
205    /// It is the caller's responsibility to ensure the field is actually part
206    /// of an ICMP header for checksumming.
207    fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F);
208
209    /// Like [`IcmpPacketTypeRaw::update_checksum_header_field`], but takes
210    /// native endian u16s.
211    fn update_checksum_header_field_u16(&mut self, old: u16, new: u16) {
212        self.update_checksum_header_field(U16::new(old), U16::new(new))
213    }
214
215    /// Recalculates and attempts to write a checksum for this packet.
216    ///
217    /// Returns whether the checksum was successfully calculated and written. In
218    /// the false case, self is left unmodified.
219    fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool;
220
221    /// Returns a mutable reference to the body of this packet.
222    fn message_body_mut(&mut self) -> &mut B;
223}
224
225impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv4> for Icmpv4PacketRaw<B> {
226    fn update_checksum_pseudo_header_address(&mut self, old: Ipv4Addr, new: Ipv4Addr) {
227        crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
228    }
229
230    fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
231        crate::icmpv4_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
232    }
233
234    fn try_write_checksum(&mut self, src_addr: Ipv4Addr, dst_addr: Ipv4Addr) -> bool {
235        crate::icmpv4_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
236    }
237
238    fn message_body_mut(&mut self) -> &mut B {
239        crate::icmpv4_dispatch!(self: raw, p => p.message_body_mut())
240    }
241}
242
243impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv4PacketRaw<B> {
244    type Type = I::IcmpPacketTypeRaw<B>;
245}
246
247impl<B: SplitByteSliceMut> IcmpPacketTypeRaw<B, Ipv6> for Icmpv6PacketRaw<B> {
248    fn update_checksum_pseudo_header_address(&mut self, old: Ipv6Addr, new: Ipv6Addr) {
249        crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_pseudo_header_address(old, new))
250    }
251
252    fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
253        crate::icmpv6_dispatch!(self: raw, p => p.update_checksum_header_field(old, new))
254    }
255
256    fn try_write_checksum(&mut self, src_addr: Ipv6Addr, dst_addr: Ipv6Addr) -> bool {
257        crate::icmpv6_dispatch!(self: raw, p => p.try_write_checksum(src_addr, dst_addr))
258    }
259
260    fn message_body_mut(&mut self) -> &mut B {
261        crate::icmpv6_dispatch!(self: raw, p => p.message_body_mut())
262    }
263}
264
265impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketTypeRaw<B, I>
266    for IcmpPacketRaw<I, B, M>
267{
268    fn update_checksum_pseudo_header_address(&mut self, old: I::Addr, new: I::Addr) {
269        match I::VERSION {
270            IpVersion::V4 => {
271                // ICMPv4 does not have a pseudo header.
272            }
273            IpVersion::V6 => {
274                let checksum = &mut self.header.prefix.checksum;
275                *checksum = internet_checksum::update(*checksum, old.bytes(), new.bytes());
276            }
277        }
278    }
279
280    fn update_checksum_header_field<F: IntoBytes + Immutable>(&mut self, old: F, new: F) {
281        let checksum = &mut self.header.prefix.checksum;
282        *checksum = internet_checksum::update(*checksum, old.as_bytes(), new.as_bytes());
283    }
284
285    fn try_write_checksum(&mut self, src_addr: I::Addr, dst_addr: I::Addr) -> bool {
286        self.try_write_checksum(src_addr, dst_addr)
287    }
288
289    fn message_body_mut(&mut self) -> &mut B {
290        self.message_body_mut()
291    }
292}
293
294impl<I: IcmpIpExt, B: SplitByteSliceMut> GenericOverIp<I> for Icmpv6PacketRaw<B> {
295    type Type = I::IcmpPacketTypeRaw<B>;
296}
297
298/// Empty message.
299#[derive(Derivative, Debug, Clone, Copy, PartialEq, Eq)]
300#[derivative(Default(bound = ""))]
301pub struct EmptyMessage<B>(core::marker::PhantomData<B>);
302
303/// `MessageBody` represents the parsed body of the ICMP packet.
304///
305/// - For messages that expect no body, the `MessageBody` is of type `EmptyMessage`.
306/// - For NDP messages, the `MessageBody` is of the type `ndp::Options`.
307/// - For all other messages, the `MessageBody` will be of the type
308///   `OriginalPacket`, which is a thin wrapper around `B`.
309pub trait MessageBody: Sized {
310    /// The underlying byteslice.
311    type B: SplitByteSlice;
312
313    /// Parse the MessageBody from the provided bytes.
314    fn parse(bytes: Self::B) -> ParseResult<Self>;
315
316    /// The length of the underlying buffer.
317    fn len(&self) -> usize;
318
319    /// Is the body empty?
320    ///
321    /// `b.is_empty()` is equivalent to `b.len() == 0`.
322    fn is_empty(&self) -> bool {
323        self.len() == 0
324    }
325
326    /// Return the underlying bytes.
327    ///
328    /// Not all ICMP messages have a fixed size, some messages like MLDv2 Query or MLDv2 Report
329    /// ([RFC 3810 section 5.1] and [RFC 3810 section 5.2]) contain a fixed amount of information
330    /// followed by a variable amount of records.
331    /// The first value returned contains the fixed size part, while the second value contains the
332    /// records for the messages that support them, more precisely, the second value is [None] if
333    /// the message does not have a variable part, otherwise it will contain the serialized list of
334    /// records.
335    ///
336    /// [RFC 3810 section 5.1]: https://datatracker.ietf.org/doc/html/rfc3810#section-5.1
337    /// [RFC 3810 section 5.2]: https://datatracker.ietf.org/doc/html/rfc3810#section-5.2
338    fn bytes(&self) -> (&[u8], Option<&[u8]>);
339}
340
341impl<B: SplitByteSlice> MessageBody for EmptyMessage<B> {
342    type B = B;
343
344    fn parse(bytes: B) -> ParseResult<Self> {
345        if !bytes.is_empty() {
346            return debug_err!(Err(ParseError::Format), "unexpected message body");
347        }
348
349        Ok(EmptyMessage::default())
350    }
351
352    fn len(&self) -> usize {
353        0
354    }
355
356    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
357        (&[], None)
358    }
359}
360
361/// A thin wrapper around B which implements `MessageBody`.
362#[derive(Debug)]
363pub struct OriginalPacket<B>(B);
364
365impl<B: SplitByteSlice> OriginalPacket<B> {
366    /// Returns the the body of the original packet.
367    pub fn body<I: IcmpIpExt>(&self) -> &[u8] {
368        // TODO(joshlf): Can these debug_asserts be triggered by external input?
369        let header_len = I::header_len(&self.0);
370        debug_assert!(header_len <= self.0.len());
371        debug_assert!(I::VERSION.is_v6() || self.0.len() - header_len == 8);
372        &self.0[header_len..]
373    }
374}
375
376impl<B: SplitByteSlice> MessageBody for OriginalPacket<B> {
377    type B = B;
378
379    fn parse(bytes: B) -> ParseResult<OriginalPacket<B>> {
380        Ok(OriginalPacket(bytes))
381    }
382
383    fn len(&self) -> usize {
384        self.0.len()
385    }
386
387    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
388        (&self.0, None)
389    }
390}
391
392impl<B: SplitByteSlice, O: OptionsImpl> MessageBody for Options<B, O> {
393    type B = B;
394    fn parse(bytes: B) -> ParseResult<Options<B, O>> {
395        Self::parse(bytes).map_err(|_e| debug_err!(ParseError::Format, "unable to parse options"))
396    }
397
398    fn len(&self) -> usize {
399        self.bytes().len()
400    }
401
402    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
403        (self.bytes(), None)
404    }
405}
406
407/// An ICMP message.
408pub trait IcmpMessage<I: IcmpIpExt>:
409    Sized + Copy + FromBytes + IntoBytes + KnownLayout + Immutable + Unaligned
410{
411    /// Whether or not a message body is expected in an ICMP packet.
412    const EXPECTS_BODY: bool = true;
413
414    /// The type of codes used with this message.
415    ///
416    /// The ICMP header includes an 8-bit "code" field. For a given message
417    /// type, different values of this field carry different meanings. Not all
418    /// code values are used - some may be invalid. This type represents a
419    /// parsed code. For example, for TODO, it is the TODO type.
420    type Code: Into<u8> + Copy + Debug;
421
422    /// The type of the body used with this message.
423    type Body<B: SplitByteSlice>: MessageBody<B = B>;
424
425    /// The type corresponding to this message type.
426    ///
427    /// The value of the "type" field in the ICMP header corresponding to
428    /// messages of this type.
429    const TYPE: I::IcmpMessageType;
430
431    /// Parse a `Code` from an 8-bit number.
432    ///
433    /// Parse a `Code` from the 8-bit "code" field in the ICMP header. Not all
434    /// values for this field are valid. If an invalid value is passed,
435    /// `code_from_u8` returns `None`.
436    fn code_from_u8(code: u8) -> Option<Self::Code>;
437}
438
439/// The type of an ICMP message.
440///
441/// `IcmpMessageType` is implemented by `Icmpv4MessageType` and
442/// `Icmpv6MessageType`.
443pub trait IcmpMessageType: TryFrom<u8> + Into<u8> + Copy + Debug {
444    /// Is this an error message?
445    ///
446    /// For ICMP, this is true for the Destination Unreachable, Redirect, Source
447    /// Quench, Time Exceeded, and Parameter Problem message types. For ICMPv6,
448    /// this is true for the Destination Unreachable, Packet Too Big, Time
449    /// Exceeded, and Parameter Problem message types.
450    fn is_error_or_redirect(self) -> bool;
451}
452
453#[derive(Copy, Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
454#[repr(C)]
455struct Header<M> {
456    prefix: HeaderPrefix,
457    message: M,
458}
459
460/// A partially parsed and not yet validated ICMP packet.
461///
462/// An `IcmpPacketRaw` provides minimal parsing of an ICMP packet. Namely, it
463/// only requires that the header and message (in ICMPv6, these are both
464/// considered part of the header) are present, and that the header has the
465/// expected message type. The body may be missing (or an unexpected body may be
466/// present). Other than the message type, no header, message, or body field
467/// values will be validated.
468///
469/// [`IcmpPacket`] provides a [`FromRaw`] implementation that can be used to
470/// validate an [`IcmpPacketRaw`].
471#[derive(Debug)]
472pub struct IcmpPacketRaw<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
473    header: Ref<B, Header<M>>,
474    message_body: B,
475    _marker: PhantomData<I>,
476}
477
478impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
479    /// Get the ICMP message.
480    pub fn message(&self) -> &M {
481        &self.header.message
482    }
483
484    /// Get the ICMP message body.
485    pub fn message_body(&self) -> &B {
486        &self.message_body
487    }
488}
489
490impl<I: IcmpIpExt, B: SplitByteSliceMut, M: IcmpMessage<I>> IcmpPacketRaw<I, B, M> {
491    /// Get the mutable ICMP message.
492    pub fn message_mut(&mut self) -> &mut M {
493        &mut self.header.message
494    }
495
496    /// Get the mutable message body of the ICMP message.
497    pub fn message_body_mut(&mut self) -> &mut B {
498        &mut self.message_body
499    }
500
501    /// Attempts to calculate and write a Checksum for this [`IcmpPacketRaw`].
502    ///
503    /// Returns whether the checksum was successfully calculated & written. In
504    /// the false case, self is left unmodified.
505    pub(crate) fn try_write_checksum(&mut self, src_ip: I::Addr, dst_ip: I::Addr) -> bool {
506        // NB: Zero the checksum to avoid interference when computing it.
507        let original_checksum = self.header.prefix.checksum;
508        self.header.prefix.checksum = [0, 0];
509
510        if let Some(checksum) = IcmpPacket::<I, B, M>::compute_checksum(
511            &self.header,
512            &self.message_body,
513            src_ip,
514            dst_ip,
515        ) {
516            self.header.prefix.checksum = checksum;
517            true
518        } else {
519            self.header.prefix.checksum = original_checksum;
520            false
521        }
522    }
523}
524
525/// An ICMP packet.
526///
527/// An `IcmpPacket` shares its underlying memory with the byte slice it was
528/// parsed from, meaning that no copying or extra allocation is necessary.
529#[derive(Debug)]
530pub struct IcmpPacket<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> {
531    header: Ref<B, Header<M>>,
532    message_body: M::Body<B>,
533    _marker: PhantomData<I>,
534}
535
536/// Arguments required to parse an ICMP packet.
537pub struct IcmpParseArgs<A: IpAddress> {
538    src_ip: A,
539    dst_ip: A,
540}
541
542impl<A: IpAddress> IcmpParseArgs<A> {
543    /// Construct a new `IcmpParseArgs`.
544    pub fn new<S: Into<A>, D: Into<A>>(src_ip: S, dst_ip: D) -> IcmpParseArgs<A> {
545        IcmpParseArgs { src_ip: src_ip.into(), dst_ip: dst_ip.into() }
546    }
547}
548
549impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, ()>
550    for IcmpPacketRaw<I, B, M>
551{
552    type Error = ParseError;
553
554    fn parse_metadata(&self) -> ParseMetadata {
555        ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
556    }
557
558    fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
559        let header = buffer.take_obj_front::<Header<M>>().ok_or(ParseError::Format)?;
560        let message_body = buffer.into_rest();
561        if header.prefix.msg_type != M::TYPE.into() {
562            return Err(ParseError::NotExpected);
563        }
564        Ok(IcmpPacketRaw { header, message_body, _marker: PhantomData })
565    }
566}
567
568impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>>
569    FromRaw<IcmpPacketRaw<I, B, M>, IcmpParseArgs<I::Addr>> for IcmpPacket<I, B, M>
570{
571    type Error = ParseError;
572
573    fn try_from_raw_with(
574        raw: IcmpPacketRaw<I, B, M>,
575        args: IcmpParseArgs<I::Addr>,
576    ) -> ParseResult<Self> {
577        let IcmpPacketRaw { header, message_body, _marker } = raw;
578        if !M::EXPECTS_BODY && !message_body.is_empty() {
579            return Err(ParseError::Format);
580        }
581        let _: M::Code = M::code_from_u8(header.prefix.code).ok_or(ParseError::Format)?;
582        let checksum = Self::compute_checksum(&header, &message_body, args.src_ip, args.dst_ip)
583            .ok_or(ParseError::Format)?;
584        if checksum != [0, 0] {
585            return Err(ParseError::Checksum);
586        }
587        let message_body = M::Body::parse(message_body)?;
588        Ok(IcmpPacket { header, message_body, _marker })
589    }
590}
591
592impl<B: SplitByteSlice, I: IcmpIpExt, M: IcmpMessage<I>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
593    for IcmpPacket<I, B, M>
594{
595    type Error = ParseError;
596
597    fn parse_metadata(&self) -> ParseMetadata {
598        ParseMetadata::from_packet(Ref::bytes(&self.header).len(), self.message_body.len(), 0)
599    }
600
601    fn parse<BV: BufferView<B>>(buffer: BV, args: IcmpParseArgs<I::Addr>) -> ParseResult<Self> {
602        IcmpPacketRaw::parse(buffer, ()).and_then(|p| IcmpPacket::try_from_raw_with(p, args))
603    }
604}
605
606impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
607    /// Get the ICMP message.
608    pub fn message(&self) -> &M {
609        &self.header.message
610    }
611
612    /// Get the ICMP body.
613    pub fn body(&self) -> &M::Body<B> {
614        &self.message_body
615    }
616
617    /// Get the ICMP message code.
618    ///
619    /// The code provides extra details about the message. Each message type has
620    /// its own set of codes that are allowed.
621    pub fn code(&self) -> M::Code {
622        // infallible since it was validated in parse
623        M::code_from_u8(self.header.prefix.code).unwrap()
624    }
625
626    /// Construct a builder with the same contents as this packet.
627    pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, M> {
628        IcmpPacketBuilder { src_ip, dst_ip, code: self.code(), msg: *self.message() }
629    }
630}
631
632fn compute_checksum_fragmented<I: IcmpIpExt, BB: packet::Fragment, M: IcmpMessage<I>>(
633    header: &Header<M>,
634    message_body: &FragmentedByteSlice<'_, BB>,
635    src_ip: I::Addr,
636    dst_ip: I::Addr,
637) -> Option<[u8; 2]> {
638    let mut c = Checksum::new();
639    if I::VERSION.is_v6() {
640        c.add_bytes(src_ip.bytes());
641        c.add_bytes(dst_ip.bytes());
642        let icmpv6_len = mem::size_of::<Header<M>>() + message_body.len();
643        let mut len_bytes = [0; 4];
644        NetworkEndian::write_u32(&mut len_bytes, icmpv6_len.try_into().ok()?);
645        c.add_bytes(&len_bytes[..]);
646        c.add_bytes(&[0, 0, 0]);
647        c.add_bytes(&[Ipv6Proto::Icmpv6.into()]);
648    }
649    c.add_bytes(&[header.prefix.msg_type, header.prefix.code]);
650    c.add_bytes(&header.prefix.checksum);
651    c.add_bytes(header.message.as_bytes());
652    for p in message_body.iter_fragments() {
653        c.add_bytes(p);
654    }
655    Some(c.checksum())
656}
657
658impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I>> IcmpPacket<I, B, M> {
659    /// Compute the checksum, including the checksum field itself.
660    ///
661    /// `compute_checksum` returns `None` if the version is IPv6 and the total
662    /// ICMP packet length overflows a u32.
663    fn compute_checksum(
664        header: &Header<M>,
665        message_body: &[u8],
666        src_ip: I::Addr,
667        dst_ip: I::Addr,
668    ) -> Option<[u8; 2]> {
669        let mut body = [message_body];
670        compute_checksum_fragmented(header, &body.as_fragmented_byte_slice(), src_ip, dst_ip)
671    }
672}
673
674impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = OriginalPacket<B>>>
675    IcmpPacket<I, B, M>
676{
677    /// Get the body of the packet that caused this ICMP message.
678    ///
679    /// This ICMP message contains some of the bytes of the packet that caused
680    /// this message to be emitted. `original_packet_body` returns as much of
681    /// the body of that packet as is contained in this message. For IPv4, this
682    /// is guaranteed to be 8 bytes. For IPv6, there are no guarantees about the
683    /// length.
684    pub fn original_packet_body(&self) -> &[u8] {
685        self.message_body.body::<I>()
686    }
687
688    /// Returns the original packt that caused this ICMP message.
689    ///
690    /// This ICMP message contains some of the bytes of the packet that caused
691    /// this message to be emitted. `original_packet` returns as much of the
692    /// body of that packet as is contained in this message. For IPv4, this is
693    /// guaranteed to be 8 bytes. For IPv6, there are no guarantees about the
694    /// length.
695    pub fn original_packet(&self) -> &OriginalPacket<B> {
696        &self.message_body
697    }
698}
699
700impl<B: SplitByteSlice, M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv4, B, M> {
701    /// Attempt to partially parse the original packet as an IPv4 packet.
702    ///
703    /// `f` will be invoked on the result of calling `Ipv4PacketRaw::parse` on
704    /// the original packet.
705    pub fn with_original_packet<O, F: FnOnce(Result<Ipv4PacketRaw<&[u8]>, &[u8]>) -> O>(
706        &self,
707        f: F,
708    ) -> O {
709        let mut bv = self.message_body.0.deref();
710        f(Ipv4PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
711    }
712}
713
714impl<B: SplitByteSlice, M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>> IcmpPacket<Ipv6, B, M> {
715    /// Attempt to partially parse the original packet as an IPv6 packet.
716    ///
717    /// `f` will be invoked on the result of calling `Ipv6PacketRaw::parse` on
718    /// the original packet.
719    pub fn with_original_packet<O, F: FnOnce(Result<Ipv6PacketRaw<&[u8]>, &[u8]>) -> O>(
720        &self,
721        f: F,
722    ) -> O {
723        let mut bv = self.message_body.0.deref();
724        f(Ipv6PacketRaw::parse(&mut bv, ()).map_err(|_| self.message_body.0.deref()))
725    }
726}
727
728impl<I: IcmpIpExt, B: SplitByteSlice, M: IcmpMessage<I, Body<B> = ndp::Options<B>>>
729    IcmpPacket<I, B, M>
730{
731    /// Get the pared list of NDP options from the ICMP message.
732    pub fn ndp_options(&self) -> &ndp::Options<B> {
733        &self.message_body
734    }
735}
736
737/// ICMP packet context relevant to serialization.
738pub struct IcmpEnvelope;
739
740/// A trait for ICMP serialization contexts.
741pub trait IcmpSerializationContext: SerializationContext {
742    /// Converts an `IcmpEnvelope` into the serialization context's state.
743    fn envelope_to_state(envelope: IcmpEnvelope) -> Self::ContextState;
744}
745
746impl IcmpSerializationContext for NoOpSerializationContext {
747    fn envelope_to_state(_envelope: IcmpEnvelope) -> Self::ContextState {
748        ()
749    }
750}
751
752/// A builder for ICMP packets.
753#[derive(Debug, PartialEq, Clone)]
754pub struct IcmpPacketBuilder<I: IcmpIpExt, M: IcmpMessage<I>> {
755    src_ip: I::Addr,
756    dst_ip: I::Addr,
757    code: M::Code,
758    msg: M,
759}
760
761impl<I: IcmpIpExt, M: IcmpMessage<I>> IcmpPacketBuilder<I, M> {
762    /// Construct a new `IcmpPacketBuilder`.
763    pub fn new<S: Into<I::Addr>, D: Into<I::Addr>>(
764        src_ip: S,
765        dst_ip: D,
766        code: M::Code,
767        msg: M,
768    ) -> IcmpPacketBuilder<I, M> {
769        IcmpPacketBuilder { src_ip: src_ip.into(), dst_ip: dst_ip.into(), code, msg }
770    }
771
772    /// Returns the message in the ICMP packet.
773    pub fn message(&self) -> &M {
774        &self.msg
775    }
776
777    /// Returns a mutable reference to the message in the ICMP packet.
778    pub fn message_mut(&mut self) -> &mut M {
779        &mut self.msg
780    }
781
782    /// Sets the source IP address of the ICMP packet.
783    pub fn set_src_ip(&mut self, addr: I::Addr) {
784        self.src_ip = addr;
785    }
786
787    /// Sets the destination IP address of the ICMP packet.
788    pub fn set_dst_ip(&mut self, addr: I::Addr) {
789        self.dst_ip = addr;
790    }
791
792    fn serialize_header(
793        &self,
794        mut header: &mut [u8],
795        message_body: Option<FragmentedBytesMut<'_, '_>>,
796    ) {
797        use packet::BufferViewMut;
798
799        // Implements BufferViewMut, giving us take_obj_xxx_zero methods.
800        let mut prefix = &mut header;
801
802        // SECURITY: Use _zero constructors to ensure we zero memory to prevent
803        // leaking information from packets previously stored in this buffer.
804        let mut header =
805            prefix.take_obj_front_zero::<Header<M>>().expect("too few bytes for ICMP message");
806        header.prefix.set_msg_type(M::TYPE);
807        header.prefix.code = self.code.into();
808        header.message = self.msg;
809
810        if let Some(message_body) = message_body {
811            assert!(
812                M::EXPECTS_BODY || message_body.is_empty(),
813                "body provided for message that doesn't take a body"
814            );
815            let checksum = compute_checksum_fragmented(
816                &header,
817                &message_body,
818                self.src_ip,
819                self.dst_ip,
820            )
821            .unwrap_or_else(|| {
822                panic!(
823                    "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
824                    Ref::bytes(&header).len() + message_body.len(),
825                )
826            });
827            header.prefix.checksum = checksum;
828        }
829    }
830}
831
832// TODO(joshlf): Figure out a way to split body and non-body message types by
833// trait and implement PacketBuilder for some and InnerPacketBuilder for others.
834
835impl<I: IcmpIpExt, M: IcmpMessage<I>> NestablePacketBuilder for IcmpPacketBuilder<I, M> {
836    fn constraints(&self) -> PacketConstraints {
837        // The maximum body length constraint to make sure the body length
838        // doesn't overflow the 32-bit length field in the pseudo-header used
839        // for calculating the checksum.
840        //
841        // Note that, for messages that don't take bodies, it's important that
842        // we don't just set this to 0. Trying to serialize a body in a message
843        // type which doesn't take bodies is a programmer error, so we should
844        // panic in that case. Setting the max_body_len to 0 would surface the
845        // issue as an MTU error, which would hide the underlying problem.
846        // Instead, we assert in serialize. Eventually, we will hopefully figure
847        // out a way to implement InnerPacketBuilder (rather than PacketBuilder)
848        // for these message types, and this won't be an issue anymore.
849        PacketConstraints::new(mem::size_of::<Header<M>>(), 0, 0, core::u32::MAX as usize)
850    }
851}
852
853impl<I: IcmpIpExt, M: IcmpMessage<I>, C: IcmpSerializationContext> PacketBuilder<C>
854    for IcmpPacketBuilder<I, M>
855{
856    fn context_state(&self) -> C::ContextState {
857        C::envelope_to_state(IcmpEnvelope)
858    }
859
860    fn serialize(
861        &self,
862        _context: &mut C,
863        target: &mut SerializeTarget<'_>,
864        message_body: FragmentedBytesMut<'_, '_>,
865    ) {
866        self.serialize_header(target.header, Some(message_body));
867    }
868}
869
870impl<I: IcmpIpExt, M: IcmpMessage<I>, C: IcmpSerializationContext> PartialPacketBuilder<C>
871    for IcmpPacketBuilder<I, M>
872{
873    fn partial_serialize(&self, _context: &mut C, _body_len: usize, buffer: &mut [u8]) {
874        self.serialize_header(buffer, None);
875    }
876}
877
878/// An ICMP code that must be zero.
879///
880/// Some ICMP messages do not use codes. In Rust, the `IcmpMessage::Code` type
881/// associated with these messages is `IcmpZeroCode`. The only valid numerical
882/// value for this code is 0.
883#[derive(Copy, Clone, Debug, Eq, PartialEq)]
884pub struct IcmpZeroCode;
885
886impl From<IcmpZeroCode> for u8 {
887    fn from(_: IcmpZeroCode) -> u8 {
888        0
889    }
890}
891
892impl TryFrom<u8> for IcmpZeroCode {
893    type Error = NotZeroError<u8>;
894
895    fn try_from(value: u8) -> Result<Self, NotZeroError<u8>> {
896        if value == 0 { Ok(Self) } else { Err(NotZeroError(value)) }
897    }
898}
899
900/// An ICMP code that is zero on serialization, but ignored on parsing.
901///
902/// This is used for ICMP messages whose specification states that senders must
903/// set Code to 0 but receivers must ignore it (e.g. MLD/MLDv2).
904#[derive(Copy, Clone, Debug, Eq, PartialEq)]
905pub struct IcmpSenderZeroCode;
906
907impl From<IcmpSenderZeroCode> for u8 {
908    fn from(_: IcmpSenderZeroCode) -> u8 {
909        0
910    }
911}
912
913impl From<u8> for IcmpSenderZeroCode {
914    fn from(_: u8) -> Self {
915        Self
916    }
917}
918
919// TODO(https://github.com/google/zerocopy/issues/1292),
920// TODO(https://github.com/rust-lang/rust/issues/45713): This needs to be public
921// in order to work around a Rust compiler bug. Once that bug is resolved, this
922// can be made private again.
923#[doc(hidden)]
924#[derive(
925    Copy, Clone, Debug, Eq, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
926)]
927#[repr(C)]
928pub struct IdAndSeq {
929    id: U16,
930    seq: U16,
931}
932
933impl IdAndSeq {
934    fn new(id: u16, seq: u16) -> IdAndSeq {
935        IdAndSeq { id: U16::new(id), seq: U16::new(seq) }
936    }
937}
938
939#[cfg(test)]
940mod tests {
941    use ip_test_macro::ip_test;
942    use packet::{EmptyBuf, NoOpSerializationContext, ParseBuffer, Serializer, SliceBufViewMut};
943    use test_case::test_case;
944
945    use super::*;
946
947    #[test]
948    fn test_partial_parse() {
949        // Test various behaviors of parsing the `IcmpPacketRaw` type.
950
951        let reference_header = Header {
952            prefix: HeaderPrefix {
953                msg_type: <IcmpEchoRequest as IcmpMessage<Ipv4>>::TYPE.into(),
954                code: 0,
955                checksum: [0, 0],
956            },
957            message: IcmpEchoRequest::new(1, 1),
958        };
959
960        // Test that a too-short header is always rejected even if its contents
961        // are otherwise valid (the checksum here is probably invalid, but we
962        // explicitly check that it's a `Format` error, not a `Checksum`
963        // error).
964        let mut buf = &reference_header.as_bytes()[..7];
965        assert_eq!(
966            buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
967            ParseError::Format
968        );
969
970        // Test that a properly-sized header is rejected if the message type is wrong.
971        let mut header = reference_header;
972        header.prefix.msg_type = <IcmpEchoReply as IcmpMessage<Ipv4>>::TYPE.into();
973        let mut buf = header.as_bytes();
974        assert_eq!(
975            buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().unwrap_err(),
976            ParseError::NotExpected
977        );
978
979        // Test that an invalid code is accepted.
980        let mut header = reference_header;
981        header.prefix.code = 0xFF;
982        let mut buf = header.as_bytes();
983        assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
984
985        // Test that an invalid checksum is accepted. Instead of calculating the
986        // correct checksum, we just provide two different checksums. They can't
987        // both be valid.
988        let mut buf = reference_header.as_bytes();
989        assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
990        let mut header = reference_header;
991        header.prefix.checksum = [1, 1];
992        let mut buf = header.as_bytes();
993        assert!(buf.parse::<IcmpPacketRaw<Ipv4, _, IcmpEchoRequest>>().is_ok());
994    }
995
996    #[ip_test(I)]
997    #[test_case([0,0]; "zeroed_checksum")]
998    #[test_case([123, 234]; "garbage_checksum")]
999    fn test_try_write_checksum<I: IcmpIpExt>(corrupt_checksum: [u8; 2]) {
1000        // NB: The process of serializing an `IcmpPacketBuilder` will compute a
1001        // valid checksum.
1002        let icmp_message_with_checksum = IcmpPacketBuilder::<I, _>::new(
1003            *I::LOOPBACK_ADDRESS,
1004            *I::LOOPBACK_ADDRESS,
1005            IcmpZeroCode,
1006            IcmpEchoRequest::new(1, 1),
1007        )
1008        .wrap_body(EmptyBuf)
1009        .serialize_vec_outer(&mut NoOpSerializationContext)
1010        .unwrap()
1011        .as_ref()
1012        .to_vec();
1013
1014        // Clone the message and corrupt the checksum.
1015        let mut icmp_message_without_checksum = icmp_message_with_checksum.clone();
1016        {
1017            let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
1018            let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
1019                .expect("parse packet raw should succeed");
1020            message.header.prefix.checksum = corrupt_checksum;
1021        }
1022        assert_ne!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
1023
1024        // Write the checksum, and verify the message now matches the original.
1025        let buf = SliceBufViewMut::new(&mut icmp_message_without_checksum);
1026        let mut message = IcmpPacketRaw::<I, _, IcmpEchoRequest>::parse_mut(buf, ())
1027            .expect("parse packet raw should succeed");
1028        assert!(message.try_write_checksum(*I::LOOPBACK_ADDRESS, *I::LOOPBACK_ADDRESS));
1029        assert_eq!(&icmp_message_with_checksum[..], &icmp_message_without_checksum[..]);
1030    }
1031}