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