netstack3_ip/
icmp.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//! The Internet Control Message Protocol (ICMP).
6
7use alloc::boxed::Box;
8use core::convert::TryInto as _;
9use core::num::{NonZeroU16, NonZeroU8};
10
11use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
12use log::{debug, error, trace};
13use net_types::ip::{
14    GenericOverIp, Ip, IpAddress, IpMarked, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr,
15    Ipv6SourceAddr, Mtu, SubnetError,
16};
17use net_types::{
18    LinkLocalAddress, LinkLocalUnicastAddr, MulticastAddress, NonMulticastAddr, SpecifiedAddr,
19    UnicastAddr, Witness,
20};
21use netstack3_base::socket::{AddrIsMappedError, SocketIpAddr, SocketIpAddrExt as _};
22use netstack3_base::sync::Mutex;
23use netstack3_base::{
24    AnyDevice, Counter, CounterContext, DeviceIdContext, EitherDeviceId, FrameDestination,
25    IcmpIpExt, Icmpv4ErrorCode, Icmpv6ErrorCode, InstantBindingsTypes, InstantContext,
26    IpDeviceAddr, IpExt, Marks, RngContext, TokenBucket, TxMetadataBindingsTypes,
27};
28use netstack3_filter::{FilterIpExt, TransportPacketSerializer};
29use packet::{
30    BufferMut, InnerPacketBuilder as _, ParsablePacket as _, ParseBuffer, Serializer,
31    TruncateDirection, TruncatingSerializer,
32};
33use packet_formats::icmp::ndp::options::{NdpOption, NdpOptionBuilder};
34use packet_formats::icmp::ndp::{
35    NdpPacket, NeighborAdvertisement, NeighborSolicitation, NonZeroNdpLifetime,
36    OptionSequenceBuilder, RouterSolicitation,
37};
38use packet_formats::icmp::{
39    peek_message_type, IcmpDestUnreachable, IcmpEchoRequest, IcmpMessage, IcmpMessageType,
40    IcmpPacket, IcmpPacketBuilder, IcmpPacketRaw, IcmpParseArgs, IcmpTimeExceeded, IcmpZeroCode,
41    Icmpv4DestUnreachableCode, Icmpv4Packet, Icmpv4ParameterProblem, Icmpv4ParameterProblemCode,
42    Icmpv4TimeExceededCode, Icmpv6DestUnreachableCode, Icmpv6Packet, Icmpv6PacketTooBig,
43    Icmpv6ParameterProblem, Icmpv6ParameterProblemCode, Icmpv6TimeExceededCode, MessageBody,
44    OriginalPacket,
45};
46use packet_formats::ip::{DscpAndEcn, IpPacket, Ipv4Proto, Ipv6Proto};
47use packet_formats::ipv4::{Ipv4FragmentType, Ipv4Header, Ipv4OnlyMeta};
48use packet_formats::ipv6::{ExtHdrParseError, Ipv6Header};
49use zerocopy::SplitByteSlice;
50
51use crate::internal::base::{
52    AddressStatus, IpDeviceIngressStateContext, IpLayerHandler, IpPacketDestination,
53    IpSendFrameError, IpTransportContext, Ipv6PresentAddressStatus, NdpBindingsContext,
54    RouterAdvertisementEvent, SendIpPacketMeta, TransportReceiveError, IPV6_DEFAULT_SUBNET,
55};
56use crate::internal::device::nud::{ConfirmationFlags, NudIpHandler};
57use crate::internal::device::route_discovery::Ipv6DiscoveredRoute;
58use crate::internal::device::{
59    IpAddressState, IpDeviceHandler, Ipv6DeviceHandler, Ipv6LinkLayerAddr,
60};
61use crate::internal::local_delivery::{IpHeaderInfo, LocalDeliveryPacketInfo, ReceiveIpPacketMeta};
62use crate::internal::path_mtu::PmtuHandler;
63use crate::internal::socket::{
64    DelegatedRouteResolutionOptions, DelegatedSendOptions, IpSocketHandler, OptionDelegationMarker,
65    RouteResolutionOptions, SendOptions,
66};
67
68/// The IP packet hop limit for all NDP packets.
69///
70/// See [RFC 4861 section 4.1], [RFC 4861 section 4.2], [RFC 4861 section 4.2],
71/// [RFC 4861 section 4.3], [RFC 4861 section 4.4], and [RFC 4861 section 4.5]
72/// for more information.
73///
74/// [RFC 4861 section 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
75/// [RFC 4861 section 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
76/// [RFC 4861 section 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
77/// [RFC 4861 section 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
78/// [RFC 4861 section 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
79pub const REQUIRED_NDP_IP_PACKET_HOP_LIMIT: u8 = 255;
80
81/// The default number of ICMP error messages to send per second.
82///
83/// Beyond this rate, error messages will be silently dropped.
84///
85/// The current value (1000) was inspired by Netstack2 (gVisor).
86// TODO(https://fxbug.dev/407541323): Consider tuning the ICMP rate limiting
87// behavior to conform more closely to Linux.
88pub const DEFAULT_ERRORS_PER_SECOND: u64 = 1000;
89/// The IP layer's ICMP state.
90#[derive(GenericOverIp)]
91#[generic_over_ip(I, Ip)]
92pub struct IcmpState<I: IpExt, BT: IcmpBindingsTypes> {
93    error_send_bucket: Mutex<IpMarked<I, TokenBucket<BT::Instant>>>,
94    /// ICMP transmit counters.
95    pub tx_counters: IcmpTxCounters<I>,
96    /// ICMP receive counters.
97    pub rx_counters: IcmpRxCounters<I>,
98}
99
100impl<I, BT> OrderedLockAccess<IpMarked<I, TokenBucket<BT::Instant>>> for IcmpState<I, BT>
101where
102    I: IpExt,
103    BT: IcmpBindingsTypes,
104{
105    type Lock = Mutex<IpMarked<I, TokenBucket<BT::Instant>>>;
106    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
107        OrderedLockRef::new(&self.error_send_bucket)
108    }
109}
110
111/// ICMP tx path counters.
112pub type IcmpTxCounters<I> = IpMarked<I, IcmpTxCountersInner>;
113
114/// ICMP tx path counters.
115#[derive(Default)]
116pub struct IcmpTxCountersInner {
117    /// Count of reply messages sent.
118    pub reply: Counter,
119    /// Count of protocol unreachable messages sent.
120    pub protocol_unreachable: Counter,
121    /// Count of host/address unreachable messages sent.
122    pub address_unreachable: Counter,
123    /// Count of port unreachable messages sent.
124    pub port_unreachable: Counter,
125    /// Count of net unreachable messages sent.
126    pub net_unreachable: Counter,
127    /// Count of ttl expired messages sent.
128    pub ttl_expired: Counter,
129    /// Count of packet too big messages sent.
130    pub packet_too_big: Counter,
131    /// Count of parameter problem messages sent.
132    pub parameter_problem: Counter,
133    /// Count of destination unreachable messages sent.
134    pub dest_unreachable: Counter,
135    /// Count of error messages sent.
136    pub error: Counter,
137}
138
139/// ICMP rx path counters.
140pub type IcmpRxCounters<I> = IpMarked<I, IcmpRxCountersInner>;
141
142/// ICMP rx path counters.
143#[derive(Default)]
144pub struct IcmpRxCountersInner {
145    /// Count of error messages received.
146    pub error: Counter,
147    /// Count of error messages delivered to the transport layer.
148    pub error_delivered_to_transport_layer: Counter,
149    /// Count of error messages delivered to a socket.
150    pub error_delivered_to_socket: Counter,
151    /// Count of echo request messages received.
152    pub echo_request: Counter,
153    /// Count of echo reply messages received.
154    pub echo_reply: Counter,
155    /// Count of timestamp request messages received.
156    pub timestamp_request: Counter,
157    /// Count of destination unreachable messages received.
158    pub dest_unreachable: Counter,
159    /// Count of time exceeded messages received.
160    pub time_exceeded: Counter,
161    /// Count of parameter problem messages received.
162    pub parameter_problem: Counter,
163    /// Count of packet too big messages received.
164    pub packet_too_big: Counter,
165}
166
167/// Receive NDP counters.
168#[derive(Default)]
169pub struct NdpRxCounters {
170    /// Count of neighbor solicitation messages received.
171    pub neighbor_solicitation: Counter,
172    /// Count of neighbor advertisement messages received.
173    pub neighbor_advertisement: Counter,
174    /// Count of router advertisement messages received.
175    pub router_advertisement: Counter,
176    /// Count of router solicitation messages received.
177    pub router_solicitation: Counter,
178}
179
180/// Transmit NDP counters.
181#[derive(Default)]
182pub struct NdpTxCounters {
183    /// Count of neighbor advertisement messages sent.
184    pub neighbor_advertisement: Counter,
185    /// Count of neighbor solicitation messages sent.
186    pub neighbor_solicitation: Counter,
187}
188
189/// Counters for NDP messages.
190#[derive(Default)]
191pub struct NdpCounters {
192    /// Receive counters.
193    pub rx: NdpRxCounters,
194    /// Transmit counters.
195    pub tx: NdpTxCounters,
196}
197
198/// A builder for ICMPv4 state.
199#[derive(Copy, Clone)]
200pub struct Icmpv4StateBuilder {
201    send_timestamp_reply: bool,
202    errors_per_second: u64,
203}
204
205impl Default for Icmpv4StateBuilder {
206    fn default() -> Icmpv4StateBuilder {
207        Icmpv4StateBuilder {
208            send_timestamp_reply: false,
209            errors_per_second: DEFAULT_ERRORS_PER_SECOND,
210        }
211    }
212}
213
214impl Icmpv4StateBuilder {
215    /// Enable or disable replying to ICMPv4 Timestamp Request messages with
216    /// Timestamp Reply messages (default: disabled).
217    ///
218    /// Enabling this can introduce a very minor vulnerability in which an
219    /// attacker can learn the system clock's time, which in turn can aid in
220    /// attacks against time-based authentication systems.
221    pub fn send_timestamp_reply(&mut self, send_timestamp_reply: bool) -> &mut Self {
222        self.send_timestamp_reply = send_timestamp_reply;
223        self
224    }
225
226    /// Builds an [`Icmpv4State`].
227    pub fn build<BT: IcmpBindingsTypes>(self) -> Icmpv4State<BT> {
228        Icmpv4State {
229            inner: IcmpState {
230                error_send_bucket: Mutex::new(IpMarked::new(TokenBucket::new(
231                    self.errors_per_second,
232                ))),
233                tx_counters: Default::default(),
234                rx_counters: Default::default(),
235            },
236            send_timestamp_reply: self.send_timestamp_reply,
237        }
238    }
239}
240
241/// The state associated with the ICMPv4 protocol.
242pub struct Icmpv4State<BT: IcmpBindingsTypes> {
243    /// The inner common ICMP state.
244    pub inner: IcmpState<Ipv4, BT>,
245    /// Whether the stack is configured to send ICMP timestamp replies.
246    pub send_timestamp_reply: bool,
247}
248
249impl<BT: IcmpBindingsTypes> AsRef<IcmpState<Ipv4, BT>> for Icmpv4State<BT> {
250    fn as_ref(&self) -> &IcmpState<Ipv4, BT> {
251        &self.inner
252    }
253}
254
255impl<BT: IcmpBindingsTypes> AsMut<IcmpState<Ipv4, BT>> for Icmpv4State<BT> {
256    fn as_mut(&mut self) -> &mut IcmpState<Ipv4, BT> {
257        &mut self.inner
258    }
259}
260
261/// A builder for ICMPv6 state.
262#[derive(Copy, Clone)]
263pub(crate) struct Icmpv6StateBuilder {
264    errors_per_second: u64,
265}
266
267impl Default for Icmpv6StateBuilder {
268    fn default() -> Icmpv6StateBuilder {
269        Icmpv6StateBuilder { errors_per_second: DEFAULT_ERRORS_PER_SECOND }
270    }
271}
272
273impl Icmpv6StateBuilder {
274    pub(crate) fn build<BT: IcmpBindingsTypes>(self) -> Icmpv6State<BT> {
275        Icmpv6State {
276            inner: IcmpState {
277                error_send_bucket: Mutex::new(IpMarked::new(TokenBucket::new(
278                    self.errors_per_second,
279                ))),
280                tx_counters: Default::default(),
281                rx_counters: Default::default(),
282            },
283            ndp_counters: Default::default(),
284        }
285    }
286}
287
288/// The state associated with the ICMPv6 protocol.
289pub struct Icmpv6State<BT: IcmpBindingsTypes> {
290    /// The inner common ICMP state.
291    pub inner: IcmpState<Ipv6, BT>,
292    /// Neighbor discovery protocol counters.
293    pub ndp_counters: NdpCounters,
294}
295
296impl<BT: IcmpBindingsTypes> AsRef<IcmpState<Ipv6, BT>> for Icmpv6State<BT> {
297    fn as_ref(&self) -> &IcmpState<Ipv6, BT> {
298        &self.inner
299    }
300}
301
302impl<BT: IcmpBindingsTypes> AsMut<IcmpState<Ipv6, BT>> for Icmpv6State<BT> {
303    fn as_mut(&mut self) -> &mut IcmpState<Ipv6, BT> {
304        &mut self.inner
305    }
306}
307
308/// An extension trait providing ICMP handler properties.
309pub trait IcmpHandlerIpExt: IpExt {
310    type SourceAddress: Witness<Self::Addr>;
311    type IcmpError;
312
313    /// A try-conversion from [`Self::RecvSrcAddr`] to [`Self::SourceAddress`].
314    fn received_source_as_icmp_source(src: Self::RecvSrcAddr) -> Option<Self::SourceAddress>;
315
316    /// An IP-specific constructor for TtlExpired ICMP errors.
317    fn new_ttl_expired<B: SplitByteSlice>(
318        proto: Self::Proto,
319        header_len: usize,
320        meta: <Self::Packet<B> as IpPacket<B, Self>>::VersionSpecificMeta,
321    ) -> Self::IcmpError;
322
323    /// An IP-specific optional-constructor for MTU Exceeded ICMP errors.
324    fn new_mtu_exceeded(proto: Self::Proto, header_len: usize, mtu: Mtu)
325        -> Option<Self::IcmpError>;
326}
327
328impl IcmpHandlerIpExt for Ipv4 {
329    type SourceAddress = NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>;
330    type IcmpError = Icmpv4Error;
331    fn received_source_as_icmp_source(
332        src: Ipv4SourceAddr,
333    ) -> Option<NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>> {
334        match src {
335            Ipv4SourceAddr::Specified(src) => Some(src),
336            Ipv4SourceAddr::Unspecified => None,
337        }
338    }
339    fn new_ttl_expired<B: SplitByteSlice>(
340        proto: Ipv4Proto,
341        header_len: usize,
342        Ipv4OnlyMeta { id: _, fragment_type }: Ipv4OnlyMeta,
343    ) -> Icmpv4Error {
344        Icmpv4Error { kind: Icmpv4ErrorKind::TtlExpired { proto, fragment_type }, header_len }
345    }
346    fn new_mtu_exceeded(_proto: Ipv4Proto, _header_len: usize, _mtu: Mtu) -> Option<Icmpv4Error> {
347        // NB: ICMPv4 has no representation of MTU exceeded errors.
348        None
349    }
350}
351
352impl IcmpHandlerIpExt for Ipv6 {
353    type SourceAddress = UnicastAddr<Ipv6Addr>;
354    type IcmpError = Icmpv6ErrorKind;
355    fn received_source_as_icmp_source(src: Ipv6SourceAddr) -> Option<UnicastAddr<Ipv6Addr>> {
356        match src {
357            Ipv6SourceAddr::Unicast(src) => Some(src.get()),
358            Ipv6SourceAddr::Unspecified => None,
359        }
360    }
361    fn new_ttl_expired<B: SplitByteSlice>(
362        proto: Ipv6Proto,
363        header_len: usize,
364        _meta: (),
365    ) -> Icmpv6ErrorKind {
366        Icmpv6ErrorKind::TtlExpired { proto, header_len }
367    }
368    fn new_mtu_exceeded(proto: Ipv6Proto, header_len: usize, mtu: Mtu) -> Option<Icmpv6ErrorKind> {
369        Some(Icmpv6ErrorKind::PacketTooBig { proto, header_len, mtu })
370    }
371}
372
373/// A kind of ICMPv4 error.
374pub(crate) enum Icmpv4ErrorKind {
375    ParameterProblem {
376        code: Icmpv4ParameterProblemCode,
377        pointer: u8,
378        fragment_type: Ipv4FragmentType,
379    },
380    TtlExpired {
381        proto: Ipv4Proto,
382        fragment_type: Ipv4FragmentType,
383    },
384    NetUnreachable {
385        proto: Ipv4Proto,
386        fragment_type: Ipv4FragmentType,
387    },
388    ProtocolUnreachable,
389    PortUnreachable,
390}
391
392/// An ICMPv4 error.
393pub struct Icmpv4Error {
394    pub(super) kind: Icmpv4ErrorKind,
395    pub(super) header_len: usize,
396}
397
398/// A kind of ICMPv6 error.
399pub enum Icmpv6ErrorKind {
400    ParameterProblem { code: Icmpv6ParameterProblemCode, pointer: u32, allow_dst_multicast: bool },
401    TtlExpired { proto: Ipv6Proto, header_len: usize },
402    NetUnreachable { proto: Ipv6Proto, header_len: usize },
403    PacketTooBig { proto: Ipv6Proto, header_len: usize, mtu: Mtu },
404    ProtocolUnreachable { header_len: usize },
405    PortUnreachable,
406}
407
408/// The handler exposed by ICMP.
409pub trait IcmpErrorHandler<I: IcmpHandlerIpExt, BC>: DeviceIdContext<AnyDevice> {
410    /// Sends an error message in response to an incoming packet.
411    ///
412    /// `src_ip` and `dst_ip` are the source and destination addresses of the
413    /// incoming packet.
414    fn send_icmp_error_message<B: BufferMut>(
415        &mut self,
416        bindings_ctx: &mut BC,
417        device: &Self::DeviceId,
418        frame_dst: Option<FrameDestination>,
419        src_ip: I::SourceAddress,
420        dst_ip: SpecifiedAddr<I::Addr>,
421        original_packet: B,
422        error: I::IcmpError,
423        marks: &Marks,
424    );
425}
426
427impl<
428        BC: IcmpBindingsContext,
429        CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
430    > IcmpErrorHandler<Ipv4, BC> for CC
431{
432    fn send_icmp_error_message<B: BufferMut>(
433        &mut self,
434        bindings_ctx: &mut BC,
435        device: &CC::DeviceId,
436        frame_dst: Option<FrameDestination>,
437        src_ip: NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>,
438        dst_ip: SpecifiedAddr<Ipv4Addr>,
439        original_packet: B,
440        Icmpv4Error { kind, header_len }: Icmpv4Error,
441        marks: &Marks,
442    ) {
443        let src_ip = SocketIpAddr::new_ipv4_specified(src_ip.get());
444        let dst_ip = SocketIpAddr::new_ipv4_specified(dst_ip);
445        match kind {
446            Icmpv4ErrorKind::ParameterProblem { code, pointer, fragment_type } => {
447                send_icmpv4_parameter_problem(
448                    self,
449                    bindings_ctx,
450                    device,
451                    frame_dst,
452                    src_ip,
453                    dst_ip,
454                    code,
455                    Icmpv4ParameterProblem::new(pointer),
456                    original_packet,
457                    header_len,
458                    fragment_type,
459                    marks,
460                )
461            }
462            Icmpv4ErrorKind::TtlExpired { proto, fragment_type } => send_icmpv4_ttl_expired(
463                self,
464                bindings_ctx,
465                device,
466                frame_dst,
467                src_ip,
468                dst_ip,
469                proto,
470                original_packet,
471                header_len,
472                fragment_type,
473                marks,
474            ),
475            Icmpv4ErrorKind::NetUnreachable { proto, fragment_type } => {
476                send_icmpv4_net_unreachable(
477                    self,
478                    bindings_ctx,
479                    device,
480                    frame_dst,
481                    src_ip,
482                    dst_ip,
483                    proto,
484                    original_packet,
485                    header_len,
486                    fragment_type,
487                    marks,
488                )
489            }
490            Icmpv4ErrorKind::ProtocolUnreachable => send_icmpv4_protocol_unreachable(
491                self,
492                bindings_ctx,
493                device,
494                frame_dst,
495                src_ip,
496                dst_ip,
497                original_packet,
498                header_len,
499                marks,
500            ),
501            Icmpv4ErrorKind::PortUnreachable => send_icmpv4_port_unreachable(
502                self,
503                bindings_ctx,
504                device,
505                frame_dst,
506                src_ip,
507                dst_ip,
508                original_packet,
509                header_len,
510                marks,
511            ),
512        }
513    }
514}
515
516impl<
517        BC: IcmpBindingsContext,
518        CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
519    > IcmpErrorHandler<Ipv6, BC> for CC
520{
521    fn send_icmp_error_message<B: BufferMut>(
522        &mut self,
523        bindings_ctx: &mut BC,
524        device: &CC::DeviceId,
525        frame_dst: Option<FrameDestination>,
526        src_ip: UnicastAddr<Ipv6Addr>,
527        dst_ip: SpecifiedAddr<Ipv6Addr>,
528        original_packet: B,
529        error: Icmpv6ErrorKind,
530        marks: &Marks,
531    ) {
532        let src_ip: SocketIpAddr<Ipv6Addr> = match src_ip.into_specified().try_into() {
533            Ok(addr) => addr,
534            Err(AddrIsMappedError {}) => {
535                trace!("send_icmpv6_error_message: src_ip is mapped");
536                return;
537            }
538        };
539        let dst_ip: SocketIpAddr<Ipv6Addr> = match dst_ip.try_into() {
540            Ok(addr) => addr,
541            Err(AddrIsMappedError {}) => {
542                trace!("send_icmpv6_error_message: dst_ip is mapped");
543                return;
544            }
545        };
546
547        match error {
548            Icmpv6ErrorKind::ParameterProblem { code, pointer, allow_dst_multicast } => {
549                send_icmpv6_parameter_problem(
550                    self,
551                    bindings_ctx,
552                    device,
553                    frame_dst,
554                    src_ip,
555                    dst_ip,
556                    code,
557                    Icmpv6ParameterProblem::new(pointer),
558                    original_packet,
559                    allow_dst_multicast,
560                    marks,
561                )
562            }
563            Icmpv6ErrorKind::TtlExpired { proto, header_len } => send_icmpv6_ttl_expired(
564                self,
565                bindings_ctx,
566                device,
567                frame_dst,
568                src_ip,
569                dst_ip,
570                proto,
571                original_packet,
572                header_len,
573                marks,
574            ),
575            Icmpv6ErrorKind::NetUnreachable { proto, header_len } => send_icmpv6_net_unreachable(
576                self,
577                bindings_ctx,
578                device,
579                frame_dst,
580                src_ip,
581                dst_ip,
582                proto,
583                original_packet,
584                header_len,
585                marks,
586            ),
587            Icmpv6ErrorKind::PacketTooBig { proto, header_len, mtu } => send_icmpv6_packet_too_big(
588                self,
589                bindings_ctx,
590                device,
591                frame_dst,
592                src_ip,
593                dst_ip,
594                proto,
595                mtu,
596                original_packet,
597                header_len,
598                marks,
599            ),
600            Icmpv6ErrorKind::ProtocolUnreachable { header_len } => {
601                send_icmpv6_protocol_unreachable(
602                    self,
603                    bindings_ctx,
604                    device,
605                    frame_dst,
606                    src_ip,
607                    dst_ip,
608                    original_packet,
609                    header_len,
610                    marks,
611                )
612            }
613            Icmpv6ErrorKind::PortUnreachable => send_icmpv6_port_unreachable(
614                self,
615                bindings_ctx,
616                device,
617                frame_dst,
618                src_ip,
619                dst_ip,
620                original_packet,
621                marks,
622            ),
623        }
624    }
625}
626
627/// A marker for all the contexts provided by bindings require by the ICMP
628/// module.
629pub trait IcmpBindingsContext: InstantContext + RngContext + IcmpBindingsTypes {}
630impl<BC> IcmpBindingsContext for BC where
631    BC: InstantContext + RngContext + IcmpBindingsTypes + IcmpBindingsTypes
632{
633}
634
635/// A marker trait for all bindings types required by the ICMP module.
636pub trait IcmpBindingsTypes: InstantBindingsTypes + TxMetadataBindingsTypes {}
637impl<BT: InstantBindingsTypes + TxMetadataBindingsTypes> IcmpBindingsTypes for BT {}
638
639/// Empty trait to work around coherence issues.
640///
641/// This serves only to convince the coherence checker that a particular blanket
642/// trait implementation could only possibly conflict with other blanket impls
643/// in this crate. It can be safely implemented for any type.
644/// TODO(https://github.com/rust-lang/rust/issues/97811): Remove this once the
645/// coherence checker doesn't require it.
646pub trait IcmpStateContext {}
647
648/// A marker trait to prevent integration from creating a recursive loop when
649/// handling Echo sockets.
650///
651/// This is a requirement for [`InnerIcmpContext::EchoTransportContext`] which
652/// disallows the integration layer from using [`IcmpIpTransportContext`] as the
653/// associated type, which would create a recursive loop.
654///
655/// By *not implementing* this trait for [`IcmpIpTransporContext`] we prevent
656/// the mistake.
657pub trait EchoTransportContextMarker {}
658
659/// The execution context shared by ICMP(v4) and ICMPv6 for the internal
660/// operations of the IP stack.
661pub trait InnerIcmpContext<I: IpExt + FilterIpExt, BC: IcmpBindingsTypes>:
662    IpSocketHandler<I, BC>
663{
664    /// A type implementing [`IpTransportContext`] that handles ICMP Echo
665    /// replies.
666    type EchoTransportContext: IpTransportContext<I, BC, Self> + EchoTransportContextMarker;
667
668    // TODO(joshlf): If we end up needing to respond to these messages with new
669    // outbound packets, then perhaps it'd be worth passing the original buffer
670    // so that it can be reused?
671    //
672    // NOTE(joshlf): We don't guarantee the packet body length here for two
673    // reasons:
674    // - It's possible that some IPv4 protocol does or will exist for which
675    //   valid packets are less than 8 bytes in length. If we were to reject all
676    //   packets with bodies of less than 8 bytes, we might silently discard
677    //   legitimate error messages for such protocols.
678    // - Even if we were to guarantee this, there's no good way to encode such a
679    //   guarantee in the type system, and so the caller would have no recourse
680    //   but to panic, and panics have a habit of becoming bugs or DoS
681    //   vulnerabilities when invariants change.
682
683    /// Receives an ICMP error message and demultiplexes it to a transport layer
684    /// protocol.
685    ///
686    /// All arguments beginning with `original_` are fields from the IP packet
687    /// that triggered the error. The `original_body` is provided here so that
688    /// the error can be associated with a transport-layer socket. `device`
689    /// identifies the device on which the packet was received.
690    ///
691    /// While ICMPv4 error messages are supposed to contain the first 8 bytes of
692    /// the body of the offending packet, and ICMPv6 error messages are supposed
693    /// to contain as much of the offending packet as possible without violating
694    /// the IPv6 minimum MTU, the caller does NOT guarantee that either of these
695    /// hold. It is `receive_icmp_error`'s responsibility to handle any length
696    /// of `original_body`, and to perform any necessary validation.
697    fn receive_icmp_error(
698        &mut self,
699        bindings_ctx: &mut BC,
700        device: &Self::DeviceId,
701        original_src_ip: Option<SpecifiedAddr<I::Addr>>,
702        original_dst_ip: SpecifiedAddr<I::Addr>,
703        original_proto: I::Proto,
704        original_body: &[u8],
705        err: I::ErrorCode,
706    );
707
708    /// Calls the function with a mutable reference to ICMP error send tocket
709    /// bucket.
710    fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<BC::Instant>) -> O>(
711        &mut self,
712        cb: F,
713    ) -> O;
714}
715
716/// The execution context for ICMPv4.
717///
718/// `InnerIcmpv4Context` is a shorthand for a larger collection of traits.
719pub trait InnerIcmpv4Context<BC: IcmpBindingsTypes>: InnerIcmpContext<Ipv4, BC> {
720    /// Returns true if a timestamp reply may be sent.
721    fn should_send_timestamp_reply(&self) -> bool;
722}
723
724/// The execution context for ICMPv6.
725///
726/// `InnerIcmpv6Context` is a shorthand for a larger collection of traits.
727pub trait InnerIcmpv6Context<BC: IcmpBindingsTypes>: InnerIcmpContext<Ipv6, BC> {}
728impl<BC: IcmpBindingsTypes, CC: InnerIcmpContext<Ipv6, BC>> InnerIcmpv6Context<BC> for CC {}
729
730/// Attempt to send an ICMP or ICMPv6 error message, applying a rate limit.
731///
732/// `try_send_error!($core_ctx, $bindings_ctx, $e)` attempts to consume a token from the
733/// token bucket at `$core_ctx.get_state_mut().error_send_bucket`. If it
734/// succeeds, it invokes the expression `$e`, and otherwise does nothing. It
735/// assumes that the type of `$e` is `Result<(), _>` and, in the case that the
736/// rate limit is exceeded and it does not invoke `$e`, returns `Ok(())`.
737///
738/// [RFC 4443 Section 2.4] (f) requires that we MUST limit the rate of outbound
739/// ICMPv6 error messages. To our knowledge, there is no similar requirement for
740/// ICMPv4, but the same rationale applies, so we do it for ICMPv4 as well.
741///
742/// [RFC 4443 Section 2.4]: https://tools.ietf.org/html/rfc4443#section-2.4
743macro_rules! try_send_error {
744    ($core_ctx:expr, $bindings_ctx:expr, $e:expr) => {{
745        let send = $core_ctx.with_error_send_bucket_mut(|error_send_bucket| {
746            error_send_bucket.try_take($bindings_ctx)
747        });
748
749        if send {
750            $core_ctx.counters().error.increment();
751            $e
752        } else {
753            trace!("ip::icmp::try_send_error!: dropping rate-limited ICMP error message");
754            Ok(())
755        }
756    }};
757}
758
759/// An implementation of [`IpTransportContext`] for ICMP.
760pub enum IcmpIpTransportContext {}
761
762fn receive_ip_transport_icmp_error<
763    I: IpExt + FilterIpExt,
764    CC: InnerIcmpContext<I, BC> + CounterContext<IcmpRxCounters<I>>,
765    BC: IcmpBindingsContext,
766>(
767    core_ctx: &mut CC,
768    bindings_ctx: &mut BC,
769    device: &CC::DeviceId,
770    original_src_ip: Option<SpecifiedAddr<I::Addr>>,
771    original_dst_ip: SpecifiedAddr<I::Addr>,
772    original_body: &[u8],
773    err: I::ErrorCode,
774) {
775    core_ctx.counters().error_delivered_to_transport_layer.increment();
776    trace!("IcmpIpTransportContext::receive_icmp_error({:?})", err);
777
778    let mut parse_body = original_body;
779    match parse_body.parse::<IcmpPacketRaw<I, _, IcmpEchoRequest>>() {
780        // Only pass things along to the Echo socket layer if this is an echo
781        // request.
782        Ok(_echo_request) => (),
783        Err(_) => {
784            // NOTE: This might just mean that the error message was in response
785            // to a packet that we sent that wasn't an echo request, so we just
786            // silently ignore it.
787            return;
788        }
789    }
790
791    <CC::EchoTransportContext as IpTransportContext<I, BC, CC>>::receive_icmp_error(
792        core_ctx,
793        bindings_ctx,
794        device,
795        original_src_ip,
796        original_dst_ip,
797        original_body,
798        err,
799    );
800}
801
802impl<
803        BC: IcmpBindingsContext,
804        CC: InnerIcmpv4Context<BC>
805            + PmtuHandler<Ipv4, BC>
806            + CounterContext<IcmpRxCounters<Ipv4>>
807            + CounterContext<IcmpTxCounters<Ipv4>>,
808    > IpTransportContext<Ipv4, BC, CC> for IcmpIpTransportContext
809{
810    fn receive_icmp_error(
811        core_ctx: &mut CC,
812        bindings_ctx: &mut BC,
813        device: &CC::DeviceId,
814        original_src_ip: Option<SpecifiedAddr<Ipv4Addr>>,
815        original_dst_ip: SpecifiedAddr<Ipv4Addr>,
816        original_body: &[u8],
817        err: Icmpv4ErrorCode,
818    ) {
819        receive_ip_transport_icmp_error(
820            core_ctx,
821            bindings_ctx,
822            device,
823            original_src_ip,
824            original_dst_ip,
825            original_body,
826            err,
827        )
828    }
829
830    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<Ipv4>>(
831        core_ctx: &mut CC,
832        bindings_ctx: &mut BC,
833        device: &CC::DeviceId,
834        src_ip: Ipv4SourceAddr,
835        dst_ip: SpecifiedAddr<Ipv4Addr>,
836        mut buffer: B,
837        info: &LocalDeliveryPacketInfo<Ipv4, H>,
838    ) -> Result<(), (B, TransportReceiveError)> {
839        let LocalDeliveryPacketInfo { meta, header_info: _, marks } = info;
840        let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
841        if let Some(delivery) = transparent_override {
842            unreachable!(
843                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
844                transparent proxy rules can only be configured for TCP and UDP packets"
845            );
846        }
847
848        trace!(
849            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet({}, {})",
850            src_ip,
851            dst_ip
852        );
853        let packet =
854            match buffer.parse_with::<_, Icmpv4Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
855                Ok(packet) => packet,
856                Err(_) => return Ok(()), // TODO(joshlf): Do something else here?
857            };
858
859        match packet {
860            Icmpv4Packet::EchoRequest(echo_request) => {
861                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx).echo_request.increment();
862
863                if let Ipv4SourceAddr::Specified(src_ip) = src_ip {
864                    let req = *echo_request.message();
865                    let code = echo_request.code();
866                    let (local_ip, remote_ip) = (dst_ip, src_ip);
867                    debug!(
868                        "replying to ICMP echo request from {remote_ip} to {local_ip}%{device:?}: \
869                        id={}, seq={}",
870                        req.id(),
871                        req.seq()
872                    );
873                    send_icmp_reply(
874                        core_ctx,
875                        bindings_ctx,
876                        device,
877                        SocketIpAddr::new_ipv4_specified(remote_ip.get()),
878                        SocketIpAddr::new_ipv4_specified(local_ip),
879                        |src_ip| {
880                            buffer.encapsulate(IcmpPacketBuilder::<Ipv4, _>::new(
881                                src_ip,
882                                *remote_ip,
883                                code,
884                                req.reply(),
885                            ))
886                        },
887                        &WithMarks(marks),
888                    );
889                } else {
890                    trace!(
891                        "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
892                        Received echo request with an unspecified source address"
893                    );
894                }
895            }
896            Icmpv4Packet::EchoReply(echo_reply) => {
897                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx).echo_reply.increment();
898                trace!(
899                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
900                    Received an EchoReply message"
901                );
902                let parse_metadata = echo_reply.parse_metadata();
903                buffer.undo_parse(parse_metadata);
904                return <CC::EchoTransportContext
905                            as IpTransportContext<Ipv4, BC, CC>>::receive_ip_packet(
906                        core_ctx,
907                        bindings_ctx,
908                        device,
909                        src_ip,
910                        dst_ip,
911                        buffer,
912                        info,
913                );
914            }
915            Icmpv4Packet::TimestampRequest(timestamp_request) => {
916                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
917                    .timestamp_request
918                    .increment();
919                if let Ipv4SourceAddr::Specified(src_ip) = src_ip {
920                    if core_ctx.should_send_timestamp_reply() {
921                        trace!(
922                            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
923                            receive_ip_packet: Responding to Timestamp Request message"
924                        );
925                        // We're supposed to respond with the time that we
926                        // processed this message as measured in milliseconds
927                        // since midnight UT. However, that would require that
928                        // we knew the local time zone and had a way to convert
929                        // `InstantContext::Instant` to a `u32` value. We can't
930                        // do that, and probably don't want to introduce all of
931                        // the machinery necessary just to support this one use
932                        // case. Luckily, RFC 792 page 17 provides us with an
933                        // out:
934                        //
935                        //   If the time is not available in miliseconds [sic]
936                        //   or cannot be provided with respect to midnight UT
937                        //   then any time can be inserted in a timestamp
938                        //   provided the high order bit of the timestamp is
939                        //   also set to indicate this non-standard value.
940                        //
941                        // Thus, we provide a zero timestamp with the high order
942                        // bit set.
943                        const NOW: u32 = 0x80000000;
944                        let reply = timestamp_request.message().reply(NOW, NOW);
945                        let (local_ip, remote_ip) = (dst_ip, src_ip);
946                        // We don't actually want to use any of the _contents_
947                        // of the buffer, but we would like to reuse it as
948                        // scratch space. Eventually, `IcmpPacketBuilder` will
949                        // implement `InnerPacketBuilder` for messages without
950                        // bodies, but until that happens, we need to give it an
951                        // empty buffer.
952                        buffer.shrink_front_to(0);
953                        send_icmp_reply(
954                            core_ctx,
955                            bindings_ctx,
956                            device,
957                            SocketIpAddr::new_ipv4_specified(remote_ip.get()),
958                            SocketIpAddr::new_ipv4_specified(local_ip),
959                            |src_ip| {
960                                buffer.encapsulate(IcmpPacketBuilder::<Ipv4, _>::new(
961                                    src_ip,
962                                    *remote_ip,
963                                    IcmpZeroCode,
964                                    reply,
965                                ))
966                            },
967                            &WithMarks(marks),
968                        );
969                    } else {
970                        trace!(
971                            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
972                            receive_ip_packet: Silently ignoring Timestamp Request message"
973                        );
974                    }
975                } else {
976                    trace!(
977                        "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
978                        receive_ip_packet: Received timestamp request with an unspecified source \
979                        address"
980                    );
981                }
982            }
983            Icmpv4Packet::TimestampReply(_) => {
984                // TODO(joshlf): Support sending Timestamp Requests and
985                // receiving Timestamp Replies?
986                debug!(
987                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
988                    Received unsolicited Timestamp Reply message"
989                );
990            }
991            Icmpv4Packet::DestUnreachable(dest_unreachable) => {
992                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
993                    .dest_unreachable
994                    .increment();
995                trace!(
996                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
997                    Received a Destination Unreachable message"
998                );
999
1000                let error = if dest_unreachable.code()
1001                    == Icmpv4DestUnreachableCode::FragmentationRequired
1002                {
1003                    let mtu = if let Some(next_hop_mtu) = dest_unreachable.message().next_hop_mtu()
1004                    {
1005                        // We are updating the path MTU from the destination
1006                        // address of this `packet` (which is an IP address on
1007                        // this node) to some remote (identified by the source
1008                        // address of this `packet`).
1009                        //
1010                        // `update_pmtu_if_less` will only update the PMTU if
1011                        // the Dest Unreachable message's MTU field had a value
1012                        // that was at least the IPv4 minimum MTU (which is
1013                        // required by IPv4 RFC 791).
1014                        core_ctx.update_pmtu_if_less(
1015                            bindings_ctx,
1016                            dst_ip.get(),
1017                            src_ip.get(),
1018                            Mtu::new(u32::from(next_hop_mtu.get())),
1019                        )
1020                    } else {
1021                        // If the Next-Hop MTU from an incoming ICMP message is
1022                        // `0`, then we assume the source node of the ICMP
1023                        // message does not implement RFC 1191 and therefore
1024                        // does not actually use the Next-Hop MTU field and
1025                        // still considers it as an unused field.
1026                        //
1027                        // In this case, the only information we have is the
1028                        // size of the original IP packet that was too big (the
1029                        // original packet header should be included in the ICMP
1030                        // response). Here we will simply reduce our PMTU
1031                        // estimate to a value less than the total length of the
1032                        // original packet. See RFC 1191 Section 5.
1033                        //
1034                        // `update_pmtu_next_lower` may return an error, but it
1035                        // will only happen if no valid lower value exists from
1036                        // the original packet's length. It is safe to silently
1037                        // ignore the error when we have no valid lower PMTU
1038                        // value as the node from `src_ip` would not be IP RFC
1039                        // compliant and we expect this to be very rare (for
1040                        // IPv4, the lowest MTU value for a link can be 68
1041                        // bytes).
1042                        let (original_packet_buf, inner_body) = dest_unreachable.body().bytes();
1043                        // Note: ICMP Dest Unreachable messages don't have a variable size body.
1044                        debug_assert!(inner_body.is_none());
1045                        if original_packet_buf.len() >= 4 {
1046                            // We need the first 4 bytes as the total length
1047                            // field is at bytes 2/3 of the original packet
1048                            // buffer.
1049                            let total_len =
1050                                u16::from_be_bytes(original_packet_buf[2..4].try_into().unwrap());
1051
1052                            trace!(
1053                                "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1054                                receive_ip_packet: Next-Hop MTU is 0 so using the next best PMTU \
1055                                value from {total_len}"
1056                            );
1057
1058                            core_ctx.update_pmtu_next_lower(
1059                                bindings_ctx,
1060                                dst_ip.get(),
1061                                src_ip.get(),
1062                                Mtu::new(u32::from(total_len)),
1063                            )
1064                        } else {
1065                            // Ok to silently ignore as RFC 792 requires nodes
1066                            // to send the original IP packet header + 64 bytes
1067                            // of the original IP packet's body so the node
1068                            // itself is already violating the RFC.
1069                            trace!(
1070                                "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1071                                receive_ip_packet: Original packet buf is too small to get \
1072                                original packet len so ignoring"
1073                            );
1074                            None
1075                        }
1076                    };
1077                    mtu.and_then(|mtu| {
1078                        let mtu = u16::try_from(mtu.get()).unwrap_or(u16::MAX);
1079                        let mtu = NonZeroU16::new(mtu)?;
1080                        Some(Icmpv4ErrorCode::DestUnreachable(
1081                            dest_unreachable.code(),
1082                            IcmpDestUnreachable::new_for_frag_req(mtu),
1083                        ))
1084                    })
1085                } else {
1086                    Some(Icmpv4ErrorCode::DestUnreachable(
1087                        dest_unreachable.code(),
1088                        *dest_unreachable.message(),
1089                    ))
1090                };
1091
1092                if let Some(error) = error {
1093                    receive_icmpv4_error(core_ctx, bindings_ctx, device, &dest_unreachable, error);
1094                }
1095            }
1096            Icmpv4Packet::TimeExceeded(time_exceeded) => {
1097                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
1098                    .time_exceeded
1099                    .increment();
1100                trace!(
1101                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
1102                    Received a Time Exceeded message"
1103                );
1104
1105                receive_icmpv4_error(
1106                    core_ctx,
1107                    bindings_ctx,
1108                    device,
1109                    &time_exceeded,
1110                    Icmpv4ErrorCode::TimeExceeded(time_exceeded.code()),
1111                );
1112            }
1113            // TODO(https://fxbug.dev/323400954): Support ICMP Redirect.
1114            Icmpv4Packet::Redirect(_) => {
1115                debug!(
1116                    "Unimplemented: <IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1117                    receive_ip_packet::redirect"
1118                )
1119            }
1120            Icmpv4Packet::ParameterProblem(parameter_problem) => {
1121                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
1122                    .parameter_problem
1123                    .increment();
1124                trace!(
1125                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
1126                    Received a Parameter Problem message"
1127                );
1128
1129                receive_icmpv4_error(
1130                    core_ctx,
1131                    bindings_ctx,
1132                    device,
1133                    &parameter_problem,
1134                    Icmpv4ErrorCode::ParameterProblem(parameter_problem.code()),
1135                );
1136            }
1137        }
1138
1139        Ok(())
1140    }
1141}
1142
1143/// A type to allow implementing the required filtering traits on a concrete
1144/// subset of message types.
1145#[allow(missing_docs)]
1146pub enum NdpMessage {
1147    NeighborSolicitation {
1148        message: NeighborSolicitation,
1149        code: <NeighborSolicitation as IcmpMessage<Ipv6>>::Code,
1150    },
1151
1152    RouterSolicitation {
1153        message: RouterSolicitation,
1154        code: <RouterSolicitation as IcmpMessage<Ipv6>>::Code,
1155    },
1156
1157    NeighborAdvertisement {
1158        message: NeighborAdvertisement,
1159        code: <NeighborAdvertisement as IcmpMessage<Ipv6>>::Code,
1160    },
1161}
1162
1163/// Sends an NDP packet from `device_id` with the provided parameters.
1164pub fn send_ndp_packet<BC, CC, S>(
1165    core_ctx: &mut CC,
1166    bindings_ctx: &mut BC,
1167    device_id: &CC::DeviceId,
1168    src_ip: Option<SpecifiedAddr<Ipv6Addr>>,
1169    dst_ip: SpecifiedAddr<Ipv6Addr>,
1170    body: S,
1171    message: NdpMessage,
1172) -> Result<(), IpSendFrameError<S>>
1173where
1174    CC: IpLayerHandler<Ipv6, BC>,
1175    S: Serializer,
1176    S::Buffer: BufferMut,
1177{
1178    macro_rules! send {
1179        ($message:expr, $code:expr) => {{
1180            // TODO(https://fxbug.dev/42177356): Send through ICMPv6 send path.
1181            IpLayerHandler::<Ipv6, _>::send_ip_packet_from_device(
1182                core_ctx,
1183                bindings_ctx,
1184                SendIpPacketMeta {
1185                    device: device_id,
1186                    src_ip,
1187                    dst_ip,
1188                    destination: IpPacketDestination::from_addr(dst_ip),
1189                    ttl: NonZeroU8::new(REQUIRED_NDP_IP_PACKET_HOP_LIMIT),
1190                    proto: Ipv6Proto::Icmpv6,
1191                    mtu: Mtu::no_limit(),
1192                    dscp_and_ecn: DscpAndEcn::default(),
1193                },
1194                body.encapsulate(IcmpPacketBuilder::<Ipv6, _>::new(
1195                    src_ip.map_or(Ipv6::UNSPECIFIED_ADDRESS, |a| a.get()),
1196                    dst_ip.get(),
1197                    $code,
1198                    $message,
1199                )),
1200            )
1201            .map_err(|s| s.into_inner())
1202        }};
1203    }
1204
1205    match message {
1206        NdpMessage::NeighborSolicitation { message, code } => send!(message, code),
1207        NdpMessage::RouterSolicitation { message, code } => send!(message, code),
1208        NdpMessage::NeighborAdvertisement { message, code } => send!(message, code),
1209    }
1210}
1211
1212fn send_neighbor_advertisement<
1213    BC,
1214    CC: Ipv6DeviceHandler<BC>
1215        + IpDeviceHandler<Ipv6, BC>
1216        + IpLayerHandler<Ipv6, BC>
1217        + CounterContext<NdpCounters>,
1218>(
1219    core_ctx: &mut CC,
1220    bindings_ctx: &mut BC,
1221    device_id: &CC::DeviceId,
1222    solicited: bool,
1223    device_addr: UnicastAddr<Ipv6Addr>,
1224    dst_ip: SpecifiedAddr<Ipv6Addr>,
1225) {
1226    core_ctx.counters().tx.neighbor_advertisement.increment();
1227    debug!("send_neighbor_advertisement from {:?} to {:?}", device_addr, dst_ip);
1228    // We currently only allow the destination address to be:
1229    // 1) a unicast address.
1230    // 2) a multicast destination but the message should be an unsolicited
1231    //    neighbor advertisement.
1232    // NOTE: this assertion may need change if more messages are to be allowed
1233    // in the future.
1234    debug_assert!(dst_ip.is_valid_unicast() || (!solicited && dst_ip.is_multicast()));
1235
1236    // We must call into the higher level send_ip_packet_from_device function
1237    // because it is not guaranteed that we actually know the link-layer
1238    // address of the destination IP. Typically, the solicitation request will
1239    // carry that information, but it is not necessary. So it is perfectly valid
1240    // that trying to send this advertisement will end up triggering a neighbor
1241    // solicitation to be sent.
1242    let src_ll = core_ctx.get_link_layer_addr(&device_id);
1243
1244    // Nothing reasonable to do with the error.
1245    let advertisement = NeighborAdvertisement::new(
1246        core_ctx.is_router_device(&device_id),
1247        solicited,
1248        false,
1249        device_addr.get(),
1250    );
1251    let _: Result<(), _> = send_ndp_packet(
1252        core_ctx,
1253        bindings_ctx,
1254        &device_id,
1255        Some(device_addr.into_specified()),
1256        dst_ip,
1257        OptionSequenceBuilder::new(
1258            src_ll
1259                .as_ref()
1260                .map(Ipv6LinkLayerAddr::as_bytes)
1261                .map(NdpOptionBuilder::TargetLinkLayerAddress)
1262                .iter(),
1263        )
1264        .into_serializer(),
1265        NdpMessage::NeighborAdvertisement { message: advertisement, code: IcmpZeroCode },
1266    );
1267}
1268
1269fn receive_ndp_packet<
1270    B: SplitByteSlice,
1271    BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1272    CC: InnerIcmpv6Context<BC>
1273        + Ipv6DeviceHandler<BC>
1274        + IpDeviceHandler<Ipv6, BC>
1275        + IpDeviceIngressStateContext<Ipv6>
1276        + NudIpHandler<Ipv6, BC>
1277        + IpLayerHandler<Ipv6, BC>
1278        + CounterContext<NdpCounters>,
1279    H: IpHeaderInfo<Ipv6>,
1280>(
1281    core_ctx: &mut CC,
1282    bindings_ctx: &mut BC,
1283    device_id: &CC::DeviceId,
1284    src_ip: Ipv6SourceAddr,
1285    packet: NdpPacket<B>,
1286    header_info: &H,
1287) {
1288    // All NDP messages should be dropped if the hop-limit != 255. See
1289    //   Router Solicitations: RFC 4861 section 6.1.1,
1290    //   Router Advertisements: RFC 4861 section 6.1.2,
1291    //   Neighbor Solicitations: RFC 4861 section 7.1.1,
1292    //   Neighbor Advertisements: RFC 4861 section 7.1.2, and
1293    //   Redirect: RFC 4861 section 8.1:
1294    //
1295    //       A node MUST silently discard any received [NDP Message Type]
1296    //       messages that do not satisfy all of the following validity
1297    //       checks:
1298    //
1299    //          ...
1300    //
1301    //          - The IP Hop Limit field has a value of 255, i.e., the packet
1302    //            could not possibly have been forwarded by a router.
1303    //
1304    //          ...
1305    if header_info.hop_limit() != REQUIRED_NDP_IP_PACKET_HOP_LIMIT {
1306        trace!("dropping NDP packet from {src_ip} with invalid hop limit");
1307        return;
1308    }
1309
1310    match packet {
1311        NdpPacket::RouterSolicitation(_) | NdpPacket::Redirect(_) => {}
1312        NdpPacket::NeighborSolicitation(ref p) => {
1313            let target_address = p.message().target_address();
1314            let target_address = match UnicastAddr::new(*target_address) {
1315                Some(a) => a,
1316                None => {
1317                    trace!(
1318                        "dropping NS from {} with non-unicast target={:?}",
1319                        src_ip,
1320                        target_address
1321                    );
1322                    return;
1323                }
1324            };
1325
1326            core_ctx.counters().rx.neighbor_solicitation.increment();
1327
1328            match src_ip {
1329                Ipv6SourceAddr::Unspecified => {
1330                    // The neighbor is performing Duplicate address detection.
1331                    //
1332                    // As per RFC 4861 section 4.3,
1333                    //
1334                    //   Source Address
1335                    //       Either an address assigned to the interface from
1336                    //       which this message is sent or (if Duplicate Address
1337                    //       Detection is in progress [ADDRCONF]) the
1338                    //       unspecified address.
1339                    match Ipv6DeviceHandler::handle_received_dad_neighbor_solicitation(
1340                        core_ctx,
1341                        bindings_ctx,
1342                        &device_id,
1343                        target_address,
1344                        p.body().iter().find_map(|option| option.nonce()),
1345                    ) {
1346                        IpAddressState::Assigned => {
1347                            // Address is assigned to us to we let the
1348                            // remote node performing DAD that we own the
1349                            // address.
1350                            send_neighbor_advertisement(
1351                                core_ctx,
1352                                bindings_ctx,
1353                                &device_id,
1354                                false,
1355                                target_address,
1356                                Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS.into_specified(),
1357                            );
1358                        }
1359                        IpAddressState::Tentative => {
1360                            // Nothing further to do in response to DAD
1361                            // messages.
1362                        }
1363                        IpAddressState::Unavailable => {
1364                            // Nothing further to do for unassigned target
1365                            // addresses.
1366                        }
1367                    }
1368
1369                    return;
1370                }
1371                Ipv6SourceAddr::Unicast(src_ip) => {
1372                    // Neighbor is performing link address resolution.
1373                    match core_ctx
1374                        .address_status_for_device(target_address.into_specified(), device_id)
1375                    {
1376                        AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned) => {}
1377                        AddressStatus::Present(
1378                            Ipv6PresentAddressStatus::UnicastTentative
1379                            | Ipv6PresentAddressStatus::Multicast,
1380                        )
1381                        | AddressStatus::Unassigned => {
1382                            // Address is not considered assigned to us as a
1383                            // unicast so don't send a neighbor advertisement
1384                            // reply.
1385                            return;
1386                        }
1387                    }
1388
1389                    let link_addr = p.body().iter().find_map(|o| o.source_link_layer_address());
1390
1391                    if let Some(link_addr) = link_addr {
1392                        NudIpHandler::handle_neighbor_probe(
1393                            core_ctx,
1394                            bindings_ctx,
1395                            &device_id,
1396                            src_ip.into_specified(),
1397                            link_addr,
1398                        );
1399                    }
1400
1401                    send_neighbor_advertisement(
1402                        core_ctx,
1403                        bindings_ctx,
1404                        &device_id,
1405                        true,
1406                        target_address,
1407                        src_ip.into_specified(),
1408                    );
1409                }
1410            }
1411        }
1412        NdpPacket::NeighborAdvertisement(ref p) => {
1413            // TODO(https://fxbug.dev/42179526): Invalidate discovered routers when
1414            // neighbor entry's IsRouter field transitions to false.
1415
1416            let target_address = p.message().target_address();
1417
1418            let src_ip = match src_ip {
1419                Ipv6SourceAddr::Unicast(src_ip) => src_ip,
1420                Ipv6SourceAddr::Unspecified => {
1421                    trace!("dropping NA with unspecified source and target = {:?}", target_address);
1422                    return;
1423                }
1424            };
1425
1426            let target_address = match UnicastAddr::new(*target_address) {
1427                Some(a) => a,
1428                None => {
1429                    trace!(
1430                        "dropping NA from {} with non-unicast target={:?}",
1431                        src_ip,
1432                        target_address
1433                    );
1434                    return;
1435                }
1436            };
1437
1438            core_ctx.counters().rx.neighbor_advertisement.increment();
1439
1440            match Ipv6DeviceHandler::handle_received_neighbor_advertisement(
1441                core_ctx,
1442                bindings_ctx,
1443                &device_id,
1444                target_address,
1445            ) {
1446                IpAddressState::Assigned => {
1447                    // A neighbor is advertising that it owns an address
1448                    // that we also have assigned. This is out of scope
1449                    // for DAD.
1450                    //
1451                    // As per RFC 4862 section 5.4.4,
1452                    //
1453                    //   2.  If the target address matches a unicast address
1454                    //       assigned to the receiving interface, it would
1455                    //       possibly indicate that the address is a
1456                    //       duplicate but it has not been detected by the
1457                    //       Duplicate Address Detection procedure (recall
1458                    //       that Duplicate Address Detection is not
1459                    //       completely reliable). How to handle such a case
1460                    //       is beyond the scope of this document.
1461                    //
1462                    // TODO(https://fxbug.dev/42111744): Signal to bindings
1463                    // that a duplicate address is detected.
1464                    error!(
1465                        "NA from {src_ip} with target address {target_address} that is also \
1466                        assigned on device {device_id:?}",
1467                    );
1468                }
1469                IpAddressState::Tentative => {
1470                    // Nothing further to do for an NA from a neighbor that
1471                    // targets an address we also have assigned.
1472                    return;
1473                }
1474                IpAddressState::Unavailable => {
1475                    // Address not targeting us so we know its for a neighbor.
1476                    //
1477                    // TODO(https://fxbug.dev/42182317): Move NUD to IP.
1478                }
1479            }
1480
1481            let link_addr = p.body().iter().find_map(|o| o.target_link_layer_address());
1482            let link_addr = match link_addr {
1483                Some(a) => a,
1484                None => {
1485                    trace!(
1486                        "dropping NA from {} targetting {} with no TLL option",
1487                        src_ip,
1488                        target_address
1489                    );
1490                    return;
1491                }
1492            };
1493
1494            NudIpHandler::handle_neighbor_confirmation(
1495                core_ctx,
1496                bindings_ctx,
1497                &device_id,
1498                target_address.into_specified(),
1499                link_addr,
1500                ConfirmationFlags {
1501                    solicited_flag: p.message().solicited_flag(),
1502                    override_flag: p.message().override_flag(),
1503                },
1504            );
1505        }
1506        NdpPacket::RouterAdvertisement(ref p) => {
1507            // As per RFC 4861 section 6.1.2,
1508            //
1509            //   A node MUST silently discard any received Router Advertisement
1510            //   messages that do not satisfy all of the following validity
1511            //   checks:
1512            //
1513            //      - IP Source Address is a link-local address.  Routers must
1514            //        use their link-local address as the source for Router
1515            //        Advertisement and Redirect messages so that hosts can
1516            //        uniquely identify routers.
1517            //
1518            //        ...
1519            let src_ip = match src_ip {
1520                Ipv6SourceAddr::Unicast(ip) => match LinkLocalUnicastAddr::new(*ip) {
1521                    Some(ip) => ip,
1522                    None => return,
1523                },
1524                Ipv6SourceAddr::Unspecified => return,
1525            };
1526
1527            let ra = p.message();
1528            debug!("received router advertisement from {:?}: {:?}", src_ip, ra);
1529            core_ctx.counters().rx.router_advertisement.increment();
1530
1531            // As per RFC 4861 section 6.3.4,
1532            //   The RetransTimer variable SHOULD be copied from the Retrans
1533            //   Timer field, if it is specified.
1534            //
1535            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1536            // update the retransmit timer.
1537            if let Some(retransmit_timer) = ra.retransmit_timer() {
1538                Ipv6DeviceHandler::set_discovered_retrans_timer(
1539                    core_ctx,
1540                    bindings_ctx,
1541                    &device_id,
1542                    retransmit_timer,
1543                );
1544            }
1545
1546            // As per RFC 4861 section 6.3.4:
1547            //   If the received Cur Hop Limit value is specified, the host
1548            //   SHOULD set its CurHopLimit variable to the received value.
1549            //
1550            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1551            // update the default hop limit.
1552            if let Some(hop_limit) = ra.current_hop_limit() {
1553                trace!("receive_ndp_packet: NDP RA: updating device's hop limit to {:?} for router: {:?}", ra.current_hop_limit(), src_ip);
1554                IpDeviceHandler::set_default_hop_limit(core_ctx, &device_id, hop_limit);
1555            }
1556
1557            // TODO(https://fxbug.dev/42077316): Support default router preference.
1558            Ipv6DeviceHandler::update_discovered_ipv6_route(
1559                core_ctx,
1560                bindings_ctx,
1561                &device_id,
1562                Ipv6DiscoveredRoute { subnet: IPV6_DEFAULT_SUBNET, gateway: Some(src_ip) },
1563                p.message().router_lifetime().map(NonZeroNdpLifetime::Finite),
1564            );
1565
1566            for option in p.body().iter() {
1567                match option {
1568                    NdpOption::TargetLinkLayerAddress(_)
1569                    | NdpOption::RedirectedHeader { .. }
1570                    | NdpOption::RecursiveDnsServer(_)
1571                    | NdpOption::Nonce(_) => {}
1572                    NdpOption::SourceLinkLayerAddress(addr) => {
1573                        debug!("processing SourceLinkLayerAddress option in RA: {:?}", addr);
1574                        // As per RFC 4861 section 6.3.4,
1575                        //
1576                        //   If the advertisement contains a Source Link-Layer
1577                        //   Address option, the link-layer address SHOULD be
1578                        //   recorded in the Neighbor Cache entry for the router
1579                        //   (creating an entry if necessary) and the IsRouter
1580                        //   flag in the Neighbor Cache entry MUST be set to
1581                        //   TRUE. If no Source Link-Layer Address is included,
1582                        //   but a corresponding Neighbor Cache entry exists,
1583                        //   its IsRouter flag MUST be set to TRUE. The IsRouter
1584                        //   flag is used by Neighbor Unreachability Detection
1585                        //   to determine when a router changes to being a host
1586                        //   (i.e., no longer capable of forwarding packets).
1587                        //   If a Neighbor Cache entry is created for the
1588                        //   router, its reachability state MUST be set to STALE
1589                        //   as specified in Section 7.3.3.  If a cache entry
1590                        //   already exists and is updated with a different
1591                        //   link-layer address, the reachability state MUST
1592                        //   also be set to STALE.if a Neighbor Cache entry
1593                        //
1594                        // We do not yet support NUD as described in RFC 4861
1595                        // so for now we just record the link-layer address in
1596                        // our neighbor table.
1597                        //
1598                        // TODO(https://fxbug.dev/42083367): Add support for routers in NUD.
1599                        NudIpHandler::handle_neighbor_probe(
1600                            core_ctx,
1601                            bindings_ctx,
1602                            &device_id,
1603                            {
1604                                let src_ip: UnicastAddr<_> = src_ip.into_addr();
1605                                src_ip.into_specified()
1606                            },
1607                            addr,
1608                        );
1609                    }
1610                    NdpOption::PrefixInformation(prefix_info) => {
1611                        debug!("processing Prefix Information option in RA: {:?}", prefix_info);
1612                        // As per RFC 4861 section 6.3.4,
1613                        //
1614                        //   For each Prefix Information option with the on-link
1615                        //   flag set, a host does the following:
1616                        //
1617                        //      - If the prefix is the link-local prefix,
1618                        //        silently ignore the Prefix Information option.
1619                        //
1620                        // Also as per RFC 4862 section 5.5.3,
1621                        //
1622                        //   For each Prefix-Information option in the Router
1623                        //   Advertisement:
1624                        //
1625                        //    ..
1626                        //
1627                        //    b)  If the prefix is the link-local prefix,
1628                        //        silently ignore the Prefix Information option.
1629                        if prefix_info.prefix().is_link_local() {
1630                            continue;
1631                        }
1632
1633                        let subnet = match prefix_info.subnet() {
1634                            Ok(subnet) => subnet,
1635                            Err(err) => match err {
1636                                SubnetError::PrefixTooLong | SubnetError::HostBitsSet => continue,
1637                            },
1638                        };
1639
1640                        match UnicastAddr::new(subnet.network()) {
1641                            Some(UnicastAddr { .. }) => {}
1642                            None => continue,
1643                        }
1644
1645                        let valid_lifetime = prefix_info.valid_lifetime();
1646
1647                        if prefix_info.on_link_flag() {
1648                            // TODO(https://fxbug.dev/42077316): Support route preference.
1649                            Ipv6DeviceHandler::update_discovered_ipv6_route(
1650                                core_ctx,
1651                                bindings_ctx,
1652                                &device_id,
1653                                Ipv6DiscoveredRoute { subnet, gateway: None },
1654                                valid_lifetime,
1655                            )
1656                        }
1657
1658                        if prefix_info.autonomous_address_configuration_flag() {
1659                            Ipv6DeviceHandler::apply_slaac_update(
1660                                core_ctx,
1661                                bindings_ctx,
1662                                &device_id,
1663                                subnet,
1664                                prefix_info.preferred_lifetime(),
1665                                valid_lifetime,
1666                            );
1667                        }
1668                    }
1669                    NdpOption::RouteInformation(rio) => {
1670                        debug!("processing Route Information option in RA: {:?}", rio);
1671                        // TODO(https://fxbug.dev/42077316): Support route preference.
1672                        Ipv6DeviceHandler::update_discovered_ipv6_route(
1673                            core_ctx,
1674                            bindings_ctx,
1675                            &device_id,
1676                            Ipv6DiscoveredRoute {
1677                                subnet: rio.prefix().clone(),
1678                                gateway: Some(src_ip),
1679                            },
1680                            rio.route_lifetime(),
1681                        )
1682                    }
1683                    NdpOption::Mtu(mtu) => {
1684                        debug!("processing MTU option in RA: {:?}", mtu);
1685                        // TODO(https://fxbug.dev/42052173): Control whether or
1686                        // not we should update the link's MTU in response to
1687                        // RAs.
1688                        Ipv6DeviceHandler::set_link_mtu(core_ctx, &device_id, Mtu::new(mtu));
1689                    }
1690                }
1691            }
1692
1693            bindings_ctx.on_event(RouterAdvertisementEvent {
1694                options_bytes: Box::from(p.body().bytes()),
1695                source: **src_ip,
1696                device: device_id.clone(),
1697            });
1698        }
1699    }
1700}
1701
1702impl<
1703        BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1704        CC: InnerIcmpv6Context<BC>
1705            + InnerIcmpContext<Ipv6, BC>
1706            + Ipv6DeviceHandler<BC>
1707            + IpDeviceHandler<Ipv6, BC>
1708            + IpDeviceIngressStateContext<Ipv6>
1709            + PmtuHandler<Ipv6, BC>
1710            + NudIpHandler<Ipv6, BC>
1711            + IpLayerHandler<Ipv6, BC>
1712            + CounterContext<IcmpRxCounters<Ipv6>>
1713            + CounterContext<IcmpTxCounters<Ipv6>>
1714            + CounterContext<NdpCounters>,
1715    > IpTransportContext<Ipv6, BC, CC> for IcmpIpTransportContext
1716{
1717    fn receive_icmp_error(
1718        core_ctx: &mut CC,
1719        bindings_ctx: &mut BC,
1720        device: &CC::DeviceId,
1721        original_src_ip: Option<SpecifiedAddr<Ipv6Addr>>,
1722        original_dst_ip: SpecifiedAddr<Ipv6Addr>,
1723        original_body: &[u8],
1724        err: Icmpv6ErrorCode,
1725    ) {
1726        receive_ip_transport_icmp_error(
1727            core_ctx,
1728            bindings_ctx,
1729            device,
1730            original_src_ip,
1731            original_dst_ip,
1732            original_body,
1733            err,
1734        )
1735    }
1736
1737    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<Ipv6>>(
1738        core_ctx: &mut CC,
1739        bindings_ctx: &mut BC,
1740        device: &CC::DeviceId,
1741        src_ip: Ipv6SourceAddr,
1742        dst_ip: SpecifiedAddr<Ipv6Addr>,
1743        mut buffer: B,
1744        info: &LocalDeliveryPacketInfo<Ipv6, H>,
1745    ) -> Result<(), (B, TransportReceiveError)> {
1746        let LocalDeliveryPacketInfo { meta, header_info, marks } = info;
1747        let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
1748        if let Some(delivery) = transparent_override {
1749            unreachable!(
1750                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1751                transparent proxy rules can only be configured for TCP and UDP packets"
1752            );
1753        }
1754
1755        trace!(
1756            "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet({:?}, {})",
1757            src_ip,
1758            dst_ip
1759        );
1760
1761        let packet = match buffer
1762            .parse_with::<_, Icmpv6Packet<_>>(IcmpParseArgs::new(src_ip.get(), dst_ip))
1763        {
1764            Ok(packet) => packet,
1765            Err(_) => return Ok(()), // TODO(joshlf): Do something else here?
1766        };
1767
1768        match packet {
1769            Icmpv6Packet::EchoRequest(echo_request) => {
1770                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_request.increment();
1771
1772                if let Some(src_ip) = SocketIpAddr::new_from_ipv6_source(src_ip) {
1773                    match SocketIpAddr::try_from(dst_ip) {
1774                        Ok(dst_ip) => {
1775                            let req = *echo_request.message();
1776                            let code = echo_request.code();
1777                            let (local_ip, remote_ip) = (dst_ip, src_ip);
1778                            debug!(
1779                                "replying to ICMP echo request from {remote_ip}: id={}, seq={}",
1780                                req.id(),
1781                                req.seq()
1782                            );
1783                            send_icmp_reply(
1784                                core_ctx,
1785                                bindings_ctx,
1786                                device,
1787                                remote_ip,
1788                                local_ip,
1789                                |src_ip| {
1790                                    buffer.encapsulate(IcmpPacketBuilder::<Ipv6, _>::new(
1791                                        src_ip,
1792                                        remote_ip.addr(),
1793                                        code,
1794                                        req.reply(),
1795                                    ))
1796                                },
1797                                &WithMarks(marks),
1798                            );
1799                        }
1800                        Err(AddrIsMappedError {}) => {
1801                            trace!("IpTransportContext<Ipv6>::receive_ip_packet: Received echo request with an ipv4-mapped-ipv6 destination address");
1802                        }
1803                    }
1804                } else {
1805                    trace!("<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received echo request with an unspecified source address");
1806                }
1807            }
1808            Icmpv6Packet::EchoReply(echo_reply) => {
1809                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_reply.increment();
1810                trace!("<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received an EchoReply message");
1811                let parse_metadata = echo_reply.parse_metadata();
1812                buffer.undo_parse(parse_metadata);
1813                return <CC::EchoTransportContext
1814                            as IpTransportContext<Ipv6, BC, CC>>::receive_ip_packet(
1815                        core_ctx,
1816                        bindings_ctx,
1817                        device,
1818                        src_ip,
1819                        dst_ip,
1820                        buffer,
1821                        info
1822                );
1823            }
1824            Icmpv6Packet::Ndp(packet) => {
1825                receive_ndp_packet(core_ctx, bindings_ctx, device, src_ip, packet, header_info)
1826            }
1827            Icmpv6Packet::PacketTooBig(packet_too_big) => {
1828                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx)
1829                    .packet_too_big
1830                    .increment();
1831                trace!("<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received a Packet Too Big message");
1832                let new_mtu = if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
1833                    // We are updating the path MTU from the destination address
1834                    // of this `packet` (which is an IP address on this node) to
1835                    // some remote (identified by the source address of this
1836                    // `packet`).
1837                    //
1838                    // `update_pmtu_if_less` will only update the PMTU if the
1839                    // Packet Too Big message's MTU field had a value that was
1840                    // at least the IPv6 minimum MTU (which is required by IPv6
1841                    // RFC 8200).
1842                    core_ctx.update_pmtu_if_less(
1843                        bindings_ctx,
1844                        dst_ip.get(),
1845                        src_ip.get(),
1846                        Mtu::new(packet_too_big.message().mtu()),
1847                    )
1848                } else {
1849                    None
1850                };
1851                if let Some(mtu) = new_mtu {
1852                    receive_icmpv6_error(
1853                        core_ctx,
1854                        bindings_ctx,
1855                        device,
1856                        &packet_too_big,
1857                        Icmpv6ErrorCode::PacketTooBig(mtu),
1858                    );
1859                }
1860            }
1861            Icmpv6Packet::Mld(packet) => {
1862                core_ctx.receive_mld_packet(
1863                    bindings_ctx,
1864                    &device,
1865                    src_ip,
1866                    dst_ip,
1867                    packet,
1868                    header_info,
1869                );
1870            }
1871            Icmpv6Packet::DestUnreachable(dest_unreachable) => receive_icmpv6_error(
1872                core_ctx,
1873                bindings_ctx,
1874                device,
1875                &dest_unreachable,
1876                Icmpv6ErrorCode::DestUnreachable(dest_unreachable.code()),
1877            ),
1878            Icmpv6Packet::TimeExceeded(time_exceeded) => receive_icmpv6_error(
1879                core_ctx,
1880                bindings_ctx,
1881                device,
1882                &time_exceeded,
1883                Icmpv6ErrorCode::TimeExceeded(time_exceeded.code()),
1884            ),
1885            Icmpv6Packet::ParameterProblem(parameter_problem) => receive_icmpv6_error(
1886                core_ctx,
1887                bindings_ctx,
1888                device,
1889                &parameter_problem,
1890                Icmpv6ErrorCode::ParameterProblem(parameter_problem.code()),
1891            ),
1892        }
1893
1894        Ok(())
1895    }
1896}
1897
1898#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1899struct WithMarks<'a>(&'a Marks);
1900
1901impl<'a> OptionDelegationMarker for WithMarks<'a> {}
1902
1903impl<'a, I: IpExt> DelegatedRouteResolutionOptions<I> for WithMarks<'a> {
1904    fn marks(&self) -> &Marks {
1905        let Self(marks) = self;
1906        marks
1907    }
1908}
1909
1910impl<'a, I: IpExt> DelegatedSendOptions<I> for WithMarks<'a> {}
1911
1912/// Sends an ICMP reply to a remote host.
1913///
1914/// `send_icmp_reply` sends a reply to a non-error message (e.g., "echo request"
1915/// or "timestamp request" messages).
1916///
1917/// `get_body_from_src_ip` returns a `Serializer` with the bytes of the ICMP
1918/// packet, and, when called, is given the source IP address chosen for the
1919/// outbound packet. This allows `get_body_from_src_ip` to properly compute the
1920/// ICMP checksum, which relies on both the source and destination IP addresses
1921/// of the IP packet it's encapsulated in.
1922fn send_icmp_reply<I, BC, CC, S, F, O>(
1923    core_ctx: &mut CC,
1924    bindings_ctx: &mut BC,
1925    device: &CC::DeviceId,
1926    original_src_ip: SocketIpAddr<I::Addr>,
1927    original_dst_ip: SocketIpAddr<I::Addr>,
1928    get_body_from_src_ip: F,
1929    ip_options: &O,
1930) where
1931    I: IpExt + FilterIpExt,
1932    CC: IpSocketHandler<I, BC> + DeviceIdContext<AnyDevice> + CounterContext<IcmpTxCounters<I>>,
1933    BC: TxMetadataBindingsTypes,
1934    S: TransportPacketSerializer<I>,
1935    S::Buffer: BufferMut,
1936    F: FnOnce(SpecifiedAddr<I::Addr>) -> S,
1937    O: SendOptions<I> + RouteResolutionOptions<I>,
1938{
1939    trace!("send_icmp_reply({:?}, {}, {})", device, original_src_ip, original_dst_ip);
1940    core_ctx.counters().reply.increment();
1941    let tx_metadata: BC::TxMetadata = Default::default();
1942
1943    // Force the egress device if the original destination is multicast or
1944    // requires a zone (i.e. link-local non-loopback), ensuring we pick the
1945    // correct return route.
1946    let egress_device = (original_dst_ip.as_ref().is_multicast()
1947        || original_dst_ip.as_ref().must_have_zone())
1948    .then_some(EitherDeviceId::Strong(device));
1949
1950    core_ctx
1951        .send_oneshot_ip_packet(
1952            bindings_ctx,
1953            egress_device,
1954            IpDeviceAddr::new_from_socket_ip_addr(original_dst_ip),
1955            original_src_ip,
1956            I::ICMP_IP_PROTO,
1957            ip_options,
1958            tx_metadata,
1959            |src_ip| get_body_from_src_ip(src_ip.into()),
1960        )
1961        .unwrap_or_else(|err| {
1962            debug!("failed to send ICMP reply: {}", err);
1963        })
1964}
1965
1966/// Receive an ICMP(v4) error message.
1967///
1968/// `receive_icmpv4_error` handles an incoming ICMP error message by parsing the
1969/// original IPv4 packet and then delegating to the context.
1970fn receive_icmpv4_error<
1971    BC: IcmpBindingsContext,
1972    CC: InnerIcmpv4Context<BC>,
1973    B: SplitByteSlice,
1974    M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>,
1975>(
1976    core_ctx: &mut CC,
1977    bindings_ctx: &mut BC,
1978    device: &CC::DeviceId,
1979    packet: &IcmpPacket<Ipv4, B, M>,
1980    err: Icmpv4ErrorCode,
1981) {
1982    packet.with_original_packet(|res| match res {
1983        Ok(original_packet) => {
1984            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
1985                Some(ip) => ip,
1986                None => {
1987                    trace!("receive_icmpv4_error: Got ICMP error message whose original IPv4 packet contains an unspecified destination address; discarding");
1988                    return;
1989                },
1990            };
1991            InnerIcmpContext::receive_icmp_error(
1992                core_ctx,
1993                bindings_ctx,
1994                device,
1995                SpecifiedAddr::new(original_packet.src_ip()),
1996                dst_ip,
1997                original_packet.proto(),
1998                original_packet.body().into_inner(),
1999                err,
2000            );
2001        }
2002        Err(_) => debug!(
2003            "receive_icmpv4_error: Got ICMP error message with unparsable original IPv4 packet"
2004        ),
2005    })
2006}
2007
2008/// Receive an ICMPv6 error message.
2009///
2010/// `receive_icmpv6_error` handles an incoming ICMPv6 error message by parsing
2011/// the original IPv6 packet and then delegating to the context.
2012fn receive_icmpv6_error<
2013    BC: IcmpBindingsContext,
2014    CC: InnerIcmpv6Context<BC>,
2015    B: SplitByteSlice,
2016    M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>,
2017>(
2018    core_ctx: &mut CC,
2019    bindings_ctx: &mut BC,
2020    device: &CC::DeviceId,
2021    packet: &IcmpPacket<Ipv6, B, M>,
2022    err: Icmpv6ErrorCode,
2023) {
2024    packet.with_original_packet(|res| match res {
2025        Ok(original_packet) => {
2026            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
2027                Some(ip)=>ip,
2028                None => {
2029                    trace!("receive_icmpv6_error: Got ICMP error message whose original IPv6 packet contains an unspecified destination address; discarding");
2030                    return;
2031                },
2032            };
2033            match original_packet.body_proto() {
2034                Ok((body, proto)) => {
2035                    InnerIcmpContext::receive_icmp_error(
2036                        core_ctx,
2037                        bindings_ctx,
2038                        device,
2039                        SpecifiedAddr::new(original_packet.src_ip()),
2040                        dst_ip,
2041                        proto,
2042                        body.into_inner(),
2043                        err,
2044                    );
2045                }
2046                Err(ExtHdrParseError) => {
2047                    trace!("receive_icmpv6_error: We could not parse the original packet's extension headers, and so we don't know where the original packet's body begins; discarding");
2048                    // There's nothing we can do in this case, so we just
2049                    // return.
2050                    return;
2051                }
2052            }
2053        }
2054        Err(_body) => debug!(
2055            "receive_icmpv6_error: Got ICMPv6 error message with unparsable original IPv6 packet"
2056        ),
2057    })
2058}
2059
2060/// Send an ICMP(v4) message in response to receiving a packet destined for an
2061/// unreachable address.
2062///
2063/// `send_icmpv4_host_unreachable` sends the appropriate ICMP message in
2064/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2065/// `dst_ip` is unreachable. In particular, this is an ICMP
2066/// "destination unreachable" message with a "host unreachable" code.
2067///
2068/// `original_packet` must be an initial fragment or a complete IP
2069/// packet, per [RFC 792 Introduction]:
2070///
2071///   Also ICMP messages are only sent about errors in handling fragment zero of
2072///   fragemented [sic] datagrams.
2073///
2074/// `header_len` is the length of the header including all options.
2075///
2076/// [RFC 792 Introduction]: https://datatracker.ietf.org/doc/html/rfc792
2077pub fn send_icmpv4_host_unreachable<
2078    B: BufferMut,
2079    BC: IcmpBindingsContext,
2080    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2081>(
2082    core_ctx: &mut CC,
2083    bindings_ctx: &mut BC,
2084    device: Option<&CC::DeviceId>,
2085    frame_dst: Option<FrameDestination>,
2086    src_ip: SocketIpAddr<Ipv4Addr>,
2087    dst_ip: SocketIpAddr<Ipv4Addr>,
2088    original_packet: B,
2089    header_len: usize,
2090    fragment_type: Ipv4FragmentType,
2091    marks: &Marks,
2092) {
2093    core_ctx.counters().address_unreachable.increment();
2094
2095    send_icmpv4_dest_unreachable(
2096        core_ctx,
2097        bindings_ctx,
2098        device,
2099        frame_dst,
2100        src_ip,
2101        dst_ip,
2102        Icmpv4DestUnreachableCode::DestHostUnreachable,
2103        original_packet,
2104        header_len,
2105        fragment_type,
2106        marks,
2107    );
2108}
2109
2110/// Send an ICMPv6 message in response to receiving a packet destined for an
2111/// unreachable address.
2112///
2113/// `send_icmpv6_address_unreachable` sends the appropriate ICMP message in
2114/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2115/// `dst_ip` is unreachable. In particular, this is an ICMP
2116/// "destination unreachable" message with an "address unreachable" code.
2117///
2118/// `original_packet` contains the contents of the entire original packet,
2119/// including extension headers.
2120pub fn send_icmpv6_address_unreachable<
2121    B: BufferMut,
2122    BC: IcmpBindingsContext,
2123    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2124>(
2125    core_ctx: &mut CC,
2126    bindings_ctx: &mut BC,
2127    device: Option<&CC::DeviceId>,
2128    frame_dst: Option<FrameDestination>,
2129    src_ip: SocketIpAddr<Ipv6Addr>,
2130    dst_ip: SocketIpAddr<Ipv6Addr>,
2131    original_packet: B,
2132    marks: &Marks,
2133) {
2134    core_ctx.counters().address_unreachable.increment();
2135
2136    send_icmpv6_dest_unreachable(
2137        core_ctx,
2138        bindings_ctx,
2139        device,
2140        frame_dst,
2141        src_ip,
2142        dst_ip,
2143        Icmpv6DestUnreachableCode::AddrUnreachable,
2144        original_packet,
2145        marks,
2146    );
2147}
2148
2149/// Send an ICMP(v4) message in response to receiving a packet destined for an
2150/// unsupported IPv4 protocol.
2151///
2152/// `send_icmpv4_protocol_unreachable` sends the appropriate ICMP message in
2153/// response to receiving an IP packet from `src_ip` to `dst_ip` identifying an
2154/// unsupported protocol - in particular, a "destination unreachable" message
2155/// with a "protocol unreachable" code.
2156///
2157/// `original_packet` contains the contents of the entire original packet,
2158/// including the IP header. This must be a whole packet, not a packet fragment.
2159/// `header_len` is the length of the header including all options.
2160pub(crate) fn send_icmpv4_protocol_unreachable<
2161    B: BufferMut,
2162    BC: IcmpBindingsContext,
2163    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2164>(
2165    core_ctx: &mut CC,
2166    bindings_ctx: &mut BC,
2167    device: &CC::DeviceId,
2168    frame_dst: Option<FrameDestination>,
2169    src_ip: SocketIpAddr<Ipv4Addr>,
2170    dst_ip: SocketIpAddr<Ipv4Addr>,
2171    original_packet: B,
2172    header_len: usize,
2173    marks: &Marks,
2174) {
2175    core_ctx.counters().protocol_unreachable.increment();
2176
2177    send_icmpv4_dest_unreachable(
2178        core_ctx,
2179        bindings_ctx,
2180        Some(device),
2181        frame_dst,
2182        src_ip,
2183        dst_ip,
2184        Icmpv4DestUnreachableCode::DestProtocolUnreachable,
2185        original_packet,
2186        header_len,
2187        // If we are sending a protocol unreachable error it is correct to assume that, if the
2188        // packet was initially fragmented, it has been successfully reassembled by now. It
2189        // guarantees that we won't send more than one ICMP Destination Unreachable message for
2190        // different fragments of the same original packet, so we should behave as if we are
2191        // handling an initial fragment.
2192        Ipv4FragmentType::InitialFragment,
2193        marks,
2194    );
2195}
2196
2197/// Send an ICMPv6 message in response to receiving a packet destined for an
2198/// unsupported Next Header.
2199///
2200/// `send_icmpv6_protocol_unreachable` is like
2201/// [`send_icmpv4_protocol_unreachable`], but for ICMPv6. It sends an ICMPv6
2202/// "parameter problem" message with an "unrecognized next header type" code.
2203///
2204/// `header_len` is the length of all IPv6 headers (including extension headers)
2205/// *before* the payload with the problematic Next Header type.
2206pub(crate) fn send_icmpv6_protocol_unreachable<
2207    B: BufferMut,
2208    BC: IcmpBindingsContext,
2209    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2210>(
2211    core_ctx: &mut CC,
2212    bindings_ctx: &mut BC,
2213    device: &CC::DeviceId,
2214    frame_dst: Option<FrameDestination>,
2215    src_ip: SocketIpAddr<Ipv6Addr>,
2216    dst_ip: SocketIpAddr<Ipv6Addr>,
2217    original_packet: B,
2218    header_len: usize,
2219    marks: &Marks,
2220) {
2221    core_ctx.counters().protocol_unreachable.increment();
2222
2223    send_icmpv6_parameter_problem(
2224        core_ctx,
2225        bindings_ctx,
2226        device,
2227        frame_dst,
2228        src_ip,
2229        dst_ip,
2230        Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
2231        // Per RFC 4443, the pointer refers to the first byte of the packet
2232        // whose Next Header field was unrecognized. It is measured as an offset
2233        // from the beginning of the first IPv6 header. E.g., a pointer of 40
2234        // (the length of a single IPv6 header) would indicate that the Next
2235        // Header field from that header - and hence of the first encapsulated
2236        // packet - was unrecognized.
2237        //
2238        // NOTE: Since header_len is a usize, this could theoretically be a
2239        // lossy conversion. However, all that means in practice is that, if a
2240        // remote host somehow managed to get us to process a frame with a 4GB
2241        // IP header and send an ICMP response, the pointer value would be
2242        // wrong. It's not worth wasting special logic to avoid generating a
2243        // malformed packet in a case that will almost certainly never happen.
2244        Icmpv6ParameterProblem::new(header_len as u32),
2245        original_packet,
2246        false,
2247        marks,
2248    );
2249}
2250
2251/// Send an ICMP(v4) message in response to receiving a packet destined for an
2252/// unreachable local transport-layer port.
2253///
2254/// `send_icmpv4_port_unreachable` sends the appropriate ICMP message in
2255/// response to receiving an IP packet from `src_ip` to `dst_ip` with an
2256/// unreachable local transport-layer port. In particular, this is an ICMP
2257/// "destination unreachable" message with a "port unreachable" code.
2258///
2259/// `original_packet` contains the contents of the entire original packet,
2260/// including the IP header. This must be a whole packet, not a packet fragment.
2261/// `header_len` is the length of the header including all options.
2262pub(crate) fn send_icmpv4_port_unreachable<
2263    B: BufferMut,
2264    BC: IcmpBindingsContext,
2265    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2266>(
2267    core_ctx: &mut CC,
2268    bindings_ctx: &mut BC,
2269    device: &CC::DeviceId,
2270    frame_dst: Option<FrameDestination>,
2271    src_ip: SocketIpAddr<Ipv4Addr>,
2272    dst_ip: SocketIpAddr<Ipv4Addr>,
2273    original_packet: B,
2274    header_len: usize,
2275    marks: &Marks,
2276) {
2277    core_ctx.counters().port_unreachable.increment();
2278
2279    send_icmpv4_dest_unreachable(
2280        core_ctx,
2281        bindings_ctx,
2282        Some(device),
2283        frame_dst,
2284        src_ip,
2285        dst_ip,
2286        Icmpv4DestUnreachableCode::DestPortUnreachable,
2287        original_packet,
2288        header_len,
2289        // If we are sending a port unreachable error it is correct to assume that, if the packet
2290        // was initially fragmented, it has been successfully reassembled by now. It guarantees that
2291        // we won't send more than one ICMP Destination Unreachable message for different fragments
2292        // of the same original packet, so we should behave as if we are handling an initial
2293        // fragment.
2294        Ipv4FragmentType::InitialFragment,
2295        marks,
2296    );
2297}
2298
2299/// Send an ICMPv6 message in response to receiving a packet destined for an
2300/// unreachable local transport-layer port.
2301///
2302/// `send_icmpv6_port_unreachable` is like [`send_icmpv4_port_unreachable`], but
2303/// for ICMPv6.
2304///
2305/// `original_packet` contains the contents of the entire original packet,
2306/// including extension headers.
2307pub(crate) fn send_icmpv6_port_unreachable<
2308    B: BufferMut,
2309    BC: IcmpBindingsContext,
2310    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2311>(
2312    core_ctx: &mut CC,
2313    bindings_ctx: &mut BC,
2314    device: &CC::DeviceId,
2315    frame_dst: Option<FrameDestination>,
2316    src_ip: SocketIpAddr<Ipv6Addr>,
2317    dst_ip: SocketIpAddr<Ipv6Addr>,
2318    original_packet: B,
2319    marks: &Marks,
2320) {
2321    core_ctx.counters().port_unreachable.increment();
2322
2323    send_icmpv6_dest_unreachable(
2324        core_ctx,
2325        bindings_ctx,
2326        Some(device),
2327        frame_dst,
2328        src_ip,
2329        dst_ip,
2330        Icmpv6DestUnreachableCode::PortUnreachable,
2331        original_packet,
2332        marks,
2333    );
2334}
2335
2336/// Send an ICMP(v4) message in response to receiving a packet destined for an
2337/// unreachable network.
2338///
2339/// `send_icmpv4_net_unreachable` sends the appropriate ICMP message in response
2340/// to receiving an IP packet from `src_ip` to an unreachable `dst_ip`. In
2341/// particular, this is an ICMP "destination unreachable" message with a "net
2342/// unreachable" code.
2343///
2344/// `original_packet` contains the contents of the entire original packet -
2345/// including all IP headers. `header_len` is the length of the IPv4 header. It
2346/// is ignored for IPv6.
2347pub(crate) fn send_icmpv4_net_unreachable<
2348    B: BufferMut,
2349    BC: IcmpBindingsContext,
2350    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2351>(
2352    core_ctx: &mut CC,
2353    bindings_ctx: &mut BC,
2354    device: &CC::DeviceId,
2355    frame_dst: Option<FrameDestination>,
2356    src_ip: SocketIpAddr<Ipv4Addr>,
2357    dst_ip: SocketIpAddr<Ipv4Addr>,
2358    proto: Ipv4Proto,
2359    original_packet: B,
2360    header_len: usize,
2361    fragment_type: Ipv4FragmentType,
2362    marks: &Marks,
2363) {
2364    core_ctx.counters().net_unreachable.increment();
2365
2366    // Check whether we MUST NOT send an ICMP error message
2367    // because the original packet was itself an ICMP error message.
2368    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2369        return;
2370    }
2371
2372    send_icmpv4_dest_unreachable(
2373        core_ctx,
2374        bindings_ctx,
2375        Some(device),
2376        frame_dst,
2377        src_ip,
2378        dst_ip,
2379        Icmpv4DestUnreachableCode::DestNetworkUnreachable,
2380        original_packet,
2381        header_len,
2382        fragment_type,
2383        marks,
2384    );
2385}
2386
2387/// Send an ICMPv6 message in response to receiving a packet destined for an
2388/// unreachable network.
2389///
2390/// `send_icmpv6_net_unreachable` is like [`send_icmpv4_net_unreachable`], but
2391/// for ICMPv6. It sends an ICMPv6 "destination unreachable" message with a "no
2392/// route to destination" code.
2393///
2394/// `original_packet` contains the contents of the entire original packet
2395/// including extension headers. `header_len` is the length of the IP header and
2396/// all extension headers.
2397pub(crate) fn send_icmpv6_net_unreachable<
2398    B: BufferMut,
2399    BC: IcmpBindingsContext,
2400    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2401>(
2402    core_ctx: &mut CC,
2403    bindings_ctx: &mut BC,
2404    device: &CC::DeviceId,
2405    frame_dst: Option<FrameDestination>,
2406    src_ip: SocketIpAddr<Ipv6Addr>,
2407    dst_ip: SocketIpAddr<Ipv6Addr>,
2408    proto: Ipv6Proto,
2409    original_packet: B,
2410    header_len: usize,
2411    marks: &Marks,
2412) {
2413    core_ctx.counters().net_unreachable.increment();
2414
2415    // Check whether we MUST NOT send an ICMP error message
2416    // because the original packet was itself an ICMP error message.
2417    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2418        return;
2419    }
2420
2421    send_icmpv6_dest_unreachable(
2422        core_ctx,
2423        bindings_ctx,
2424        Some(device),
2425        frame_dst,
2426        src_ip,
2427        dst_ip,
2428        Icmpv6DestUnreachableCode::NoRoute,
2429        original_packet,
2430        marks,
2431    );
2432}
2433
2434/// Send an ICMP(v4) message in response to receiving a packet whose TTL has
2435/// expired.
2436///
2437/// `send_icmpv4_ttl_expired` sends the appropriate ICMP in response to
2438/// receiving an IP packet from `src_ip` to `dst_ip` whose TTL has expired. In
2439/// particular, this is an ICMP "time exceeded" message with a "time to live
2440/// exceeded in transit" code.
2441///
2442/// `original_packet` contains the contents of the entire original packet,
2443/// including the header. `header_len` is the length of the IP header including
2444/// options.
2445pub(crate) fn send_icmpv4_ttl_expired<
2446    B: BufferMut,
2447    BC: IcmpBindingsContext,
2448    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2449>(
2450    core_ctx: &mut CC,
2451    bindings_ctx: &mut BC,
2452    device: &CC::DeviceId,
2453    frame_dst: Option<FrameDestination>,
2454    src_ip: SocketIpAddr<Ipv4Addr>,
2455    dst_ip: SocketIpAddr<Ipv4Addr>,
2456    proto: Ipv4Proto,
2457    original_packet: B,
2458    header_len: usize,
2459    fragment_type: Ipv4FragmentType,
2460    marks: &Marks,
2461) {
2462    core_ctx.counters().ttl_expired.increment();
2463
2464    // Check whether we MUST NOT send an ICMP error message because the original
2465    // packet was itself an ICMP error message.
2466    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2467        return;
2468    }
2469
2470    send_icmpv4_error_message(
2471        core_ctx,
2472        bindings_ctx,
2473        Some(device),
2474        frame_dst,
2475        src_ip,
2476        dst_ip,
2477        Icmpv4ErrorMessage::TimeExceeded {
2478            message: IcmpTimeExceeded::default(),
2479            code: Icmpv4TimeExceededCode::TtlExpired,
2480        },
2481        original_packet,
2482        header_len,
2483        fragment_type,
2484        marks,
2485    )
2486}
2487
2488/// Send an ICMPv6 message in response to receiving a packet whose hop limit has
2489/// expired.
2490///
2491/// `send_icmpv6_ttl_expired` is like [`send_icmpv4_ttl_expired`], but for
2492/// ICMPv6. It sends an ICMPv6 "time exceeded" message with a "hop limit
2493/// exceeded in transit" code.
2494///
2495/// `original_packet` contains the contents of the entire original packet
2496/// including extension headers. `header_len` is the length of the IP header and
2497/// all extension headers.
2498pub(crate) fn send_icmpv6_ttl_expired<
2499    B: BufferMut,
2500    BC: IcmpBindingsContext,
2501    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2502>(
2503    core_ctx: &mut CC,
2504    bindings_ctx: &mut BC,
2505    device: &CC::DeviceId,
2506    frame_dst: Option<FrameDestination>,
2507    src_ip: SocketIpAddr<Ipv6Addr>,
2508    dst_ip: SocketIpAddr<Ipv6Addr>,
2509    proto: Ipv6Proto,
2510    original_packet: B,
2511    header_len: usize,
2512    marks: &Marks,
2513) {
2514    core_ctx.counters().ttl_expired.increment();
2515
2516    // Check whether we MUST NOT send an ICMP error message because the
2517    // original packet was itself an ICMP error message.
2518    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2519        return;
2520    }
2521
2522    send_icmpv6_error_message(
2523        core_ctx,
2524        bindings_ctx,
2525        Some(device),
2526        frame_dst,
2527        src_ip,
2528        dst_ip,
2529        Icmpv6ErrorMessage::TimeExceeded {
2530            message: IcmpTimeExceeded::default(),
2531            code: Icmpv6TimeExceededCode::HopLimitExceeded,
2532        },
2533        original_packet,
2534        false, /* allow_dst_multicast */
2535        marks,
2536    )
2537}
2538
2539// TODO(joshlf): Test send_icmpv6_packet_too_big once we support fake IPv6 test
2540// setups.
2541
2542/// Send an ICMPv6 message in response to receiving a packet whose size exceeds
2543/// the MTU of the next hop interface.
2544///
2545/// `send_icmpv6_packet_too_big` sends an ICMPv6 "packet too big" message in
2546/// response to receiving an IP packet from `src_ip` to `dst_ip` whose size
2547/// exceeds the `mtu` of the next hop interface.
2548pub(crate) fn send_icmpv6_packet_too_big<
2549    B: BufferMut,
2550    BC: IcmpBindingsContext,
2551    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2552>(
2553    core_ctx: &mut CC,
2554    bindings_ctx: &mut BC,
2555    device: &CC::DeviceId,
2556    frame_dst: Option<FrameDestination>,
2557    src_ip: SocketIpAddr<Ipv6Addr>,
2558    dst_ip: SocketIpAddr<Ipv6Addr>,
2559    proto: Ipv6Proto,
2560    mtu: Mtu,
2561    original_packet: B,
2562    header_len: usize,
2563    marks: &Marks,
2564) {
2565    core_ctx.counters().packet_too_big.increment();
2566    // Check whether we MUST NOT send an ICMP error message because the
2567    // original packet was itself an ICMP error message.
2568    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2569        return;
2570    }
2571
2572    send_icmpv6_error_message(
2573        core_ctx,
2574        bindings_ctx,
2575        Some(device),
2576        frame_dst,
2577        src_ip,
2578        dst_ip,
2579        Icmpv6ErrorMessage::PacketTooBig {
2580            message: Icmpv6PacketTooBig::new(mtu.into()),
2581            code: IcmpZeroCode,
2582        },
2583        original_packet,
2584        // As per RFC 4443 section 2.4.e,
2585        //
2586        //   An ICMPv6 error message MUST NOT be originated as a result of
2587        //   receiving the following:
2588        //
2589        //     (e.3) A packet destined to an IPv6 multicast address.  (There are
2590        //           two exceptions to this rule: (1) the Packet Too Big Message
2591        //           (Section 3.2) to allow Path MTU discovery to work for IPv6
2592        //           multicast, and (2) the Parameter Problem Message, Code 2
2593        //           (Section 3.4) reporting an unrecognized IPv6 option (see
2594        //           Section 4.2 of [IPv6]) that has the Option Type highest-
2595        //           order two bits set to 10).
2596        //
2597        //     (e.4) A packet sent as a link-layer multicast (the exceptions
2598        //           from e.3 apply to this case, too).
2599        //
2600        // Thus, we explicitly allow sending a Packet Too Big error if the
2601        // destination was a multicast packet.
2602        true, /* allow_dst_multicast */
2603        marks,
2604    )
2605}
2606
2607pub(crate) fn send_icmpv4_parameter_problem<
2608    B: BufferMut,
2609    BC: IcmpBindingsContext,
2610    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2611>(
2612    core_ctx: &mut CC,
2613    bindings_ctx: &mut BC,
2614    device: &CC::DeviceId,
2615    frame_dst: Option<FrameDestination>,
2616    src_ip: SocketIpAddr<Ipv4Addr>,
2617    dst_ip: SocketIpAddr<Ipv4Addr>,
2618    code: Icmpv4ParameterProblemCode,
2619    parameter_problem: Icmpv4ParameterProblem,
2620    original_packet: B,
2621    header_len: usize,
2622    fragment_type: Ipv4FragmentType,
2623    marks: &Marks,
2624) {
2625    core_ctx.counters().parameter_problem.increment();
2626
2627    send_icmpv4_error_message(
2628        core_ctx,
2629        bindings_ctx,
2630        Some(device),
2631        frame_dst,
2632        src_ip,
2633        dst_ip,
2634        Icmpv4ErrorMessage::ParameterProblem { message: parameter_problem, code },
2635        original_packet,
2636        header_len,
2637        fragment_type,
2638        marks,
2639    )
2640}
2641
2642/// Send an ICMPv6 Parameter Problem error message.
2643///
2644/// If the error message is Code 2 reporting an unrecognized IPv6 option that
2645/// has the Option Type highest-order two bits set to 10, `allow_dst_multicast`
2646/// must be set to `true`. See [`should_send_icmpv6_error`] for more details.
2647///
2648/// # Panics
2649///
2650/// Panics if `allow_multicast_addr` is set to `true`, but this Parameter
2651/// Problem's code is not 2 (Unrecognized IPv6 Option).
2652pub(crate) fn send_icmpv6_parameter_problem<
2653    B: BufferMut,
2654    BC: IcmpBindingsContext,
2655    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2656>(
2657    core_ctx: &mut CC,
2658    bindings_ctx: &mut BC,
2659    device: &CC::DeviceId,
2660    frame_dst: Option<FrameDestination>,
2661    src_ip: SocketIpAddr<Ipv6Addr>,
2662    dst_ip: SocketIpAddr<Ipv6Addr>,
2663    code: Icmpv6ParameterProblemCode,
2664    parameter_problem: Icmpv6ParameterProblem,
2665    original_packet: B,
2666    allow_dst_multicast: bool,
2667    marks: &Marks,
2668) {
2669    // Only allow the `allow_dst_multicast` parameter to be set if the code is
2670    // the unrecognized IPv6 option as that is one of the few exceptions where
2671    // we can send an ICMP packet in response to a packet that was destined for
2672    // a multicast address.
2673    assert!(!allow_dst_multicast || code == Icmpv6ParameterProblemCode::UnrecognizedIpv6Option);
2674
2675    core_ctx.counters().parameter_problem.increment();
2676
2677    send_icmpv6_error_message(
2678        core_ctx,
2679        bindings_ctx,
2680        Some(device),
2681        frame_dst,
2682        src_ip,
2683        dst_ip,
2684        Icmpv6ErrorMessage::ParameterProblem { message: parameter_problem, code },
2685        original_packet,
2686        allow_dst_multicast,
2687        marks,
2688    )
2689}
2690
2691fn send_icmpv4_dest_unreachable<
2692    B: BufferMut,
2693    BC: IcmpBindingsContext,
2694    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2695>(
2696    core_ctx: &mut CC,
2697    bindings_ctx: &mut BC,
2698    device: Option<&CC::DeviceId>,
2699    frame_dst: Option<FrameDestination>,
2700    src_ip: SocketIpAddr<Ipv4Addr>,
2701    dst_ip: SocketIpAddr<Ipv4Addr>,
2702    code: Icmpv4DestUnreachableCode,
2703    original_packet: B,
2704    header_len: usize,
2705    fragment_type: Ipv4FragmentType,
2706    marks: &Marks,
2707) {
2708    core_ctx.counters().dest_unreachable.increment();
2709    send_icmpv4_error_message(
2710        core_ctx,
2711        bindings_ctx,
2712        device,
2713        frame_dst,
2714        src_ip,
2715        dst_ip,
2716        Icmpv4ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2717        original_packet,
2718        header_len,
2719        fragment_type,
2720        marks,
2721    )
2722}
2723
2724fn send_icmpv6_dest_unreachable<
2725    B: BufferMut,
2726    BC: IcmpBindingsContext,
2727    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2728>(
2729    core_ctx: &mut CC,
2730    bindings_ctx: &mut BC,
2731    device: Option<&CC::DeviceId>,
2732    frame_dst: Option<FrameDestination>,
2733    src_ip: SocketIpAddr<Ipv6Addr>,
2734    dst_ip: SocketIpAddr<Ipv6Addr>,
2735    code: Icmpv6DestUnreachableCode,
2736    original_packet: B,
2737    marks: &Marks,
2738) {
2739    send_icmpv6_error_message(
2740        core_ctx,
2741        bindings_ctx,
2742        device,
2743        frame_dst,
2744        src_ip,
2745        dst_ip,
2746        Icmpv6ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2747        original_packet,
2748        false, /* allow_dst_multicast */
2749        marks,
2750    )
2751}
2752
2753/// A type to allow implementing the required filtering traits on a concrete
2754/// subset of message types.
2755#[allow(missing_docs)]
2756enum Icmpv4ErrorMessage {
2757    TimeExceeded {
2758        message: IcmpTimeExceeded,
2759        code: <IcmpTimeExceeded as IcmpMessage<Ipv4>>::Code,
2760    },
2761    ParameterProblem {
2762        message: Icmpv4ParameterProblem,
2763        code: <Icmpv4ParameterProblem as IcmpMessage<Ipv4>>::Code,
2764    },
2765    DestUnreachable {
2766        message: IcmpDestUnreachable,
2767        code: <IcmpDestUnreachable as IcmpMessage<Ipv4>>::Code,
2768    },
2769}
2770
2771fn send_icmpv4_error_message<
2772    B: BufferMut,
2773    BC: IcmpBindingsContext,
2774    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2775>(
2776    core_ctx: &mut CC,
2777    bindings_ctx: &mut BC,
2778    device: Option<&CC::DeviceId>,
2779    frame_dst: Option<FrameDestination>,
2780    original_src_ip: SocketIpAddr<Ipv4Addr>,
2781    original_dst_ip: SocketIpAddr<Ipv4Addr>,
2782    message: Icmpv4ErrorMessage,
2783    mut original_packet: B,
2784    header_len: usize,
2785    fragment_type: Ipv4FragmentType,
2786    marks: &Marks,
2787) {
2788    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2789    // error messages.
2790
2791    if !should_send_icmpv4_error(
2792        frame_dst,
2793        original_src_ip.into(),
2794        original_dst_ip.into(),
2795        fragment_type,
2796    ) {
2797        return;
2798    }
2799
2800    // Per RFC 792, body contains entire IPv4 header + 64 bytes of original
2801    // body.
2802    original_packet.shrink_back_to(header_len + 64);
2803
2804    let tx_metadata: BC::TxMetadata = Default::default();
2805
2806    macro_rules! send {
2807        ($message:expr, $code:expr) => {{
2808            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2809            // errors sent from unnumbered/router interfaces.
2810            let _ = try_send_error!(
2811                core_ctx,
2812                bindings_ctx,
2813                core_ctx.send_oneshot_ip_packet(
2814                    bindings_ctx,
2815                    device.map(EitherDeviceId::Strong),
2816                    None,
2817                    original_src_ip,
2818                    Ipv4Proto::Icmp,
2819                    &WithMarks(marks),
2820                    tx_metadata,
2821                    |local_ip| {
2822                        original_packet.encapsulate(IcmpPacketBuilder::<Ipv4, _>::new(
2823                            local_ip.addr(),
2824                            original_src_ip.addr(),
2825                            $code,
2826                            $message,
2827                        ))
2828                    },
2829                )
2830            );
2831        }};
2832    }
2833
2834    match message {
2835        Icmpv4ErrorMessage::TimeExceeded { message, code } => send!(message, code),
2836        Icmpv4ErrorMessage::ParameterProblem { message, code } => send!(message, code),
2837        Icmpv4ErrorMessage::DestUnreachable { message, code } => send!(message, code),
2838    }
2839}
2840
2841/// A type to allow implementing the required filtering traits on a concrete
2842/// subset of message types.
2843#[allow(missing_docs)]
2844enum Icmpv6ErrorMessage {
2845    TimeExceeded {
2846        message: IcmpTimeExceeded,
2847        code: <IcmpTimeExceeded as IcmpMessage<Ipv6>>::Code,
2848    },
2849    PacketTooBig {
2850        message: Icmpv6PacketTooBig,
2851        code: <Icmpv6PacketTooBig as IcmpMessage<Ipv6>>::Code,
2852    },
2853    ParameterProblem {
2854        message: Icmpv6ParameterProblem,
2855        code: <Icmpv6ParameterProblem as IcmpMessage<Ipv6>>::Code,
2856    },
2857    DestUnreachable {
2858        message: IcmpDestUnreachable,
2859        code: <IcmpDestUnreachable as IcmpMessage<Ipv6>>::Code,
2860    },
2861}
2862
2863fn send_icmpv6_error_message<
2864    B: BufferMut,
2865    BC: IcmpBindingsContext,
2866    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2867>(
2868    core_ctx: &mut CC,
2869    bindings_ctx: &mut BC,
2870    device: Option<&CC::DeviceId>,
2871    frame_dst: Option<FrameDestination>,
2872    original_src_ip: SocketIpAddr<Ipv6Addr>,
2873    original_dst_ip: SocketIpAddr<Ipv6Addr>,
2874    message: Icmpv6ErrorMessage,
2875    original_packet: B,
2876    allow_dst_multicast: bool,
2877    marks: &Marks,
2878) {
2879    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2880    // error messages.
2881
2882    if !should_send_icmpv6_error(
2883        frame_dst,
2884        original_src_ip.into(),
2885        original_dst_ip.into(),
2886        allow_dst_multicast,
2887    ) {
2888        return;
2889    }
2890
2891    struct Icmpv6ErrorOptions<'a>(&'a Marks);
2892    impl<'a> OptionDelegationMarker for Icmpv6ErrorOptions<'a> {}
2893    impl<'a> DelegatedSendOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2894        fn mtu(&self) -> Mtu {
2895            Ipv6::MINIMUM_LINK_MTU
2896        }
2897    }
2898    impl<'a> DelegatedRouteResolutionOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2899        fn marks(&self) -> &Marks {
2900            let Self(marks) = self;
2901            marks
2902        }
2903    }
2904
2905    let tx_metadata: BC::TxMetadata = Default::default();
2906
2907    macro_rules! send {
2908        ($message:expr, $code:expr) => {{
2909            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2910            // errors sent from unnumbered/router interfaces.
2911            let _ = try_send_error!(
2912                core_ctx,
2913                bindings_ctx,
2914                core_ctx.send_oneshot_ip_packet(
2915                    bindings_ctx,
2916                    device.map(EitherDeviceId::Strong),
2917                    None,
2918                    original_src_ip,
2919                    Ipv6Proto::Icmpv6,
2920                    &Icmpv6ErrorOptions(marks),
2921                    tx_metadata,
2922                    |local_ip| {
2923                        let icmp_builder = IcmpPacketBuilder::<Ipv6, _>::new(
2924                            local_ip.addr(),
2925                            original_src_ip.addr(),
2926                            $code,
2927                            $message,
2928                        );
2929
2930                        // Per RFC 4443, body contains as much of the original body as
2931                        // possible without exceeding IPv6 minimum MTU.
2932                        TruncatingSerializer::new(original_packet, TruncateDirection::DiscardBack)
2933                            .encapsulate(icmp_builder)
2934                    },
2935                )
2936            );
2937        }};
2938    }
2939
2940    match message {
2941        Icmpv6ErrorMessage::TimeExceeded { message, code } => send!(message, code),
2942        Icmpv6ErrorMessage::PacketTooBig { message, code } => send!(message, code),
2943        Icmpv6ErrorMessage::ParameterProblem { message, code } => send!(message, code),
2944        Icmpv6ErrorMessage::DestUnreachable { message, code } => send!(message, code),
2945    }
2946}
2947
2948/// Should we send an ICMP(v4) response?
2949///
2950/// `should_send_icmpv4_error` implements the logic described in RFC 1122
2951/// Section 3.2.2. It decides whether, upon receiving an incoming packet with
2952/// the given parameters, we should send an ICMP response or not. In particular,
2953/// we do not send an ICMP response if we've received:
2954/// - a packet destined to a broadcast or multicast address
2955/// - a packet sent in a link-layer broadcast
2956/// - a non-initial fragment
2957/// - a packet whose source address does not define a single host (a
2958///   zero/unspecified address, a loopback address, a broadcast address, a
2959///   multicast address, or a Class E address)
2960///
2961/// Note that `should_send_icmpv4_error` does NOT check whether the incoming
2962/// packet contained an ICMP error message. This is because that check is
2963/// unnecessary for some ICMP error conditions. The ICMP error message check can
2964/// be performed separately with `is_icmp_error_message`.
2965fn should_send_icmpv4_error(
2966    frame_dst: Option<FrameDestination>,
2967    src_ip: SpecifiedAddr<Ipv4Addr>,
2968    dst_ip: SpecifiedAddr<Ipv4Addr>,
2969    fragment_type: Ipv4FragmentType,
2970) -> bool {
2971    // NOTE: We do not explicitly implement the "unspecified address" check, as
2972    // it is enforced by the types of the arguments.
2973
2974    // TODO(joshlf): Implement the rest of the rules:
2975    // - a packet destined to a subnet broadcast address
2976    // - a packet whose source address is a subnet broadcast address
2977
2978    // NOTE: The FrameDestination type has variants for unicast, multicast, and
2979    // broadcast. One implication of the fact that we only check for broadcast
2980    // here (in compliance with the RFC) is that we could, in one very unlikely
2981    // edge case, respond with an ICMP error message to an IP packet which was
2982    // sent in a link-layer multicast frame. In particular, that can happen if
2983    // we subscribe to a multicast IP group and, as a result, subscribe to the
2984    // corresponding multicast MAC address, and we receive a unicast IP packet
2985    // in a multicast link-layer frame destined to that MAC address.
2986    //
2987    // TODO(joshlf): Should we filter incoming multicast IP traffic to make sure
2988    // that it matches the multicast MAC address of the frame it was
2989    // encapsulated in?
2990    fragment_type == Ipv4FragmentType::InitialFragment
2991        && !(dst_ip.is_multicast()
2992            || dst_ip.is_limited_broadcast()
2993            || frame_dst.is_some_and(|dst| dst.is_broadcast())
2994            || src_ip.is_loopback()
2995            || src_ip.is_limited_broadcast()
2996            || src_ip.is_multicast()
2997            || src_ip.is_class_e())
2998}
2999
3000/// Should we send an ICMPv6 response?
3001///
3002/// `should_send_icmpv6_error` implements the logic described in RFC 4443
3003/// Section 2.4.e. It decides whether, upon receiving an incoming packet with
3004/// the given parameters, we should send an ICMP response or not. In particular,
3005/// we do not send an ICMP response if we've received:
3006/// - a packet destined to a multicast address
3007///   - Two exceptions to this rules:
3008///     1) the Packet Too Big Message to allow Path MTU discovery to work for
3009///        IPv6 multicast
3010///     2) the Parameter Problem Message, Code 2 reporting an unrecognized IPv6
3011///        option that has the Option Type highest-order two bits set to 10
3012/// - a packet sent as a link-layer multicast or broadcast
3013///   - same exceptions apply here as well.
3014/// - a packet whose source address does not define a single host (a
3015///   zero/unspecified address, a loopback address, or a multicast address)
3016///
3017/// If an ICMP response will be a Packet Too Big Message or a Parameter Problem
3018/// Message, Code 2 reporting an unrecognized IPv6 option that has the Option
3019/// Type highest-order two bits set to 10, `info.allow_dst_multicast` must be
3020/// set to `true` so this function will allow the exception mentioned above.
3021///
3022/// Note that `should_send_icmpv6_error` does NOT check whether the incoming
3023/// packet contained an ICMP error message. This is because that check is
3024/// unnecessary for some ICMP error conditions. The ICMP error message check can
3025/// be performed separately with `is_icmp_error_message`.
3026fn should_send_icmpv6_error(
3027    frame_dst: Option<FrameDestination>,
3028    src_ip: SpecifiedAddr<Ipv6Addr>,
3029    dst_ip: SpecifiedAddr<Ipv6Addr>,
3030    allow_dst_multicast: bool,
3031) -> bool {
3032    // NOTE: We do not explicitly implement the "unspecified address" check, as
3033    // it is enforced by the types of the arguments.
3034    let multicast_frame_dst = match frame_dst {
3035        Some(FrameDestination::Individual { local: _ }) | None => false,
3036        Some(FrameDestination::Broadcast) | Some(FrameDestination::Multicast) => true,
3037    };
3038    if (dst_ip.is_multicast() || multicast_frame_dst) && !allow_dst_multicast {
3039        return false;
3040    }
3041    if src_ip.is_loopback() || src_ip.is_multicast() {
3042        return false;
3043    }
3044    true
3045}
3046
3047/// Determine whether or not an IP packet body contains an ICMP error message
3048/// for the purposes of determining whether or not to send an ICMP response.
3049///
3050/// `is_icmp_error_message` checks whether `proto` is ICMP(v4) for IPv4 or
3051/// ICMPv6 for IPv6 and, if so, attempts to parse `buf` as an ICMP packet in
3052/// order to determine whether it is an error message or not. If parsing fails,
3053/// it conservatively assumes that it is an error packet in order to avoid
3054/// violating the MUST NOT directives of RFC 1122 Section 3.2.2 and [RFC 4443
3055/// Section 2.4.e].
3056///
3057/// [RFC 4443 Section 2.4.e]: https://tools.ietf.org/html/rfc4443#section-2.4
3058fn is_icmp_error_message<I: IcmpIpExt>(proto: I::Proto, buf: &[u8]) -> bool {
3059    proto == I::ICMP_IP_PROTO
3060        && peek_message_type::<I::IcmpMessageType>(buf).map(IcmpMessageType::is_err).unwrap_or(true)
3061}
3062
3063/// Test utilities for ICMP.
3064#[cfg(any(test, feature = "testutils"))]
3065pub(crate) mod testutil {
3066    use alloc::vec::Vec;
3067    use net_types::ethernet::Mac;
3068    use net_types::ip::{Ipv6, Ipv6Addr};
3069    use packet::{Buf, InnerPacketBuilder as _, Serializer as _};
3070    use packet_formats::icmp::ndp::options::NdpOptionBuilder;
3071    use packet_formats::icmp::ndp::{
3072        NeighborAdvertisement, NeighborSolicitation, OptionSequenceBuilder,
3073    };
3074    use packet_formats::icmp::{IcmpPacketBuilder, IcmpZeroCode};
3075    use packet_formats::ip::Ipv6Proto;
3076    use packet_formats::ipv6::Ipv6PacketBuilder;
3077
3078    use super::REQUIRED_NDP_IP_PACKET_HOP_LIMIT;
3079
3080    /// Serialize an IP packet containing a neighbor advertisement with the
3081    /// provided parameters.
3082    pub fn neighbor_advertisement_ip_packet(
3083        src_ip: Ipv6Addr,
3084        dst_ip: Ipv6Addr,
3085        router_flag: bool,
3086        solicited_flag: bool,
3087        override_flag: bool,
3088        mac: Mac,
3089    ) -> Buf<Vec<u8>> {
3090        OptionSequenceBuilder::new([NdpOptionBuilder::TargetLinkLayerAddress(&mac.bytes())].iter())
3091            .into_serializer()
3092            .encapsulate(IcmpPacketBuilder::<Ipv6, _>::new(
3093                src_ip,
3094                dst_ip,
3095                IcmpZeroCode,
3096                NeighborAdvertisement::new(router_flag, solicited_flag, override_flag, src_ip),
3097            ))
3098            .encapsulate(Ipv6PacketBuilder::new(
3099                src_ip,
3100                dst_ip,
3101                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3102                Ipv6Proto::Icmpv6,
3103            ))
3104            .serialize_vec_outer()
3105            .unwrap()
3106            .unwrap_b()
3107    }
3108
3109    /// Serialize an IP packet containing a neighbor solicitation with the
3110    /// provided parameters.
3111    pub fn neighbor_solicitation_ip_packet(
3112        src_ip: Ipv6Addr,
3113        dst_ip: Ipv6Addr,
3114        target_addr: Ipv6Addr,
3115        mac: Mac,
3116    ) -> Buf<Vec<u8>> {
3117        OptionSequenceBuilder::new([NdpOptionBuilder::SourceLinkLayerAddress(&mac.bytes())].iter())
3118            .into_serializer()
3119            .encapsulate(IcmpPacketBuilder::<Ipv6, _>::new(
3120                src_ip,
3121                dst_ip,
3122                IcmpZeroCode,
3123                NeighborSolicitation::new(target_addr),
3124            ))
3125            .encapsulate(Ipv6PacketBuilder::new(
3126                src_ip,
3127                dst_ip,
3128                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3129                Ipv6Proto::Icmpv6,
3130            ))
3131            .serialize_vec_outer()
3132            .unwrap()
3133            .unwrap_b()
3134    }
3135}
3136
3137#[cfg(test)]
3138mod tests {
3139    use alloc::vec;
3140    use alloc::vec::Vec;
3141    use packet_formats::icmp::ndp::options::NdpNonce;
3142
3143    use core::fmt::Debug;
3144    use core::time::Duration;
3145
3146    use net_types::ip::Subnet;
3147    use netstack3_base::testutil::{
3148        set_logger_for_test, FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant,
3149        FakeTxMetadata, FakeWeakDeviceId, TestIpExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
3150    };
3151    use netstack3_base::{CtxPair, Uninstantiable};
3152    use packet::Buf;
3153    use packet_formats::icmp::mld::MldPacket;
3154    use packet_formats::ip::IpProto;
3155    use packet_formats::utils::NonZeroDuration;
3156
3157    use super::*;
3158    use crate::internal::base::{IpDeviceEgressStateContext, RouterAdvertisementEvent};
3159    use crate::internal::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx};
3160    use crate::internal::socket::{
3161        IpSock, IpSockCreationError, IpSockSendError, IpSocketHandler, SendOptions,
3162    };
3163    use crate::socket::RouteResolutionOptions;
3164
3165    /// The FakeCoreCtx held as the inner state of [`FakeIcmpCoreCtx`].
3166    type InnerIpSocketCtx<I> = FakeCoreCtx<
3167        FakeIpSocketCtx<I, FakeDeviceId>,
3168        SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
3169        FakeDeviceId,
3170    >;
3171
3172    /// `FakeCoreCtx` specialized for ICMP.
3173    pub(super) struct FakeIcmpCoreCtx<I: IpExt> {
3174        ip_socket_ctx: InnerIpSocketCtx<I>,
3175        icmp: FakeIcmpCoreCtxState<I>,
3176    }
3177
3178    /// `FakeBindingsCtx` specialized for ICMP.
3179    type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<
3180        (),
3181        RouterAdvertisementEvent<FakeDeviceId>,
3182        FakeIcmpBindingsCtxState<I>,
3183        (),
3184    >;
3185
3186    /// A fake ICMP bindings and core contexts.
3187    ///
3188    /// This is exposed to super so it can be shared with the socket tests.
3189    pub(super) type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
3190
3191    pub(super) struct FakeIcmpCoreCtxState<I: IpExt> {
3192        error_send_bucket: TokenBucket<FakeInstant>,
3193        receive_icmp_error: Vec<I::ErrorCode>,
3194        rx_counters: IcmpRxCounters<I>,
3195        tx_counters: IcmpTxCounters<I>,
3196        ndp_counters: NdpCounters,
3197    }
3198
3199    impl<I: TestIpExt + IpExt> FakeIcmpCoreCtx<I> {
3200        fn with_errors_per_second(errors_per_second: u64) -> Self {
3201            Self {
3202                icmp: FakeIcmpCoreCtxState {
3203                    error_send_bucket: TokenBucket::new(errors_per_second),
3204                    receive_icmp_error: Default::default(),
3205                    rx_counters: Default::default(),
3206                    tx_counters: Default::default(),
3207                    ndp_counters: Default::default(),
3208                },
3209                ip_socket_ctx: InnerIpSocketCtx::with_state(FakeIpSocketCtx::new(
3210                    core::iter::once(FakeDeviceConfig {
3211                        device: FakeDeviceId,
3212                        local_ips: vec![I::TEST_ADDRS.local_ip],
3213                        remote_ips: vec![I::TEST_ADDRS.remote_ip],
3214                    }),
3215                )),
3216            }
3217        }
3218    }
3219
3220    impl<I: TestIpExt + IpExt> Default for FakeIcmpCoreCtx<I> {
3221        fn default() -> Self {
3222            Self::with_errors_per_second(DEFAULT_ERRORS_PER_SECOND)
3223        }
3224    }
3225
3226    impl<I: IpExt> DeviceIdContext<AnyDevice> for FakeIcmpCoreCtx<I> {
3227        type DeviceId = FakeDeviceId;
3228        type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
3229    }
3230
3231    impl<I: IpExt> IcmpStateContext for FakeIcmpCoreCtx<I> {}
3232    impl<I: IpExt> IcmpStateContext for InnerIpSocketCtx<I> {}
3233
3234    impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtx<I> {
3235        fn counters(&self) -> &IcmpRxCounters<I> {
3236            &self.icmp.rx_counters
3237        }
3238    }
3239
3240    impl<I: IpExt> CounterContext<IcmpTxCounters<I>> for FakeIcmpCoreCtx<I> {
3241        fn counters(&self) -> &IcmpTxCounters<I> {
3242            &self.icmp.tx_counters
3243        }
3244    }
3245
3246    impl<I: IpExt> CounterContext<NdpCounters> for FakeIcmpCoreCtx<I> {
3247        fn counters(&self) -> &NdpCounters {
3248            &self.icmp.ndp_counters
3249        }
3250    }
3251
3252    pub enum FakeEchoIpTransportContext {}
3253
3254    impl EchoTransportContextMarker for FakeEchoIpTransportContext {}
3255
3256    impl<I: IpExt> IpTransportContext<I, FakeIcmpBindingsCtx<I>, FakeIcmpCoreCtx<I>>
3257        for FakeEchoIpTransportContext
3258    {
3259        fn receive_icmp_error(
3260            core_ctx: &mut FakeIcmpCoreCtx<I>,
3261            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3262            _device: &FakeDeviceId,
3263            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3264            _original_dst_ip: SpecifiedAddr<I::Addr>,
3265            _original_body: &[u8],
3266            _err: I::ErrorCode,
3267        ) {
3268            core_ctx.icmp.rx_counters.error_delivered_to_socket.increment()
3269        }
3270
3271        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3272            _core_ctx: &mut FakeIcmpCoreCtx<I>,
3273            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3274            _device: &FakeDeviceId,
3275            _src_ip: I::RecvSrcAddr,
3276            _dst_ip: SpecifiedAddr<I::Addr>,
3277            _buffer: B,
3278            _info: &LocalDeliveryPacketInfo<I, H>,
3279        ) -> Result<(), (B, TransportReceiveError)> {
3280            unimplemented!()
3281        }
3282    }
3283
3284    impl<I: IpExt + FilterIpExt> InnerIcmpContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3285        type EchoTransportContext = FakeEchoIpTransportContext;
3286
3287        fn receive_icmp_error(
3288            &mut self,
3289            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3290            device: &Self::DeviceId,
3291            original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3292            original_dst_ip: SpecifiedAddr<I::Addr>,
3293            original_proto: I::Proto,
3294            original_body: &[u8],
3295            err: I::ErrorCode,
3296        ) {
3297            CounterContext::<IcmpRxCounters<I>>::counters(self).error.increment();
3298            self.icmp.receive_icmp_error.push(err);
3299            if original_proto == I::ICMP_IP_PROTO {
3300                receive_ip_transport_icmp_error(
3301                    self,
3302                    bindings_ctx,
3303                    device,
3304                    original_src_ip,
3305                    original_dst_ip,
3306                    original_body,
3307                    err,
3308                )
3309            }
3310        }
3311
3312        fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<FakeInstant>) -> O>(
3313            &mut self,
3314            cb: F,
3315        ) -> O {
3316            cb(&mut self.icmp.error_send_bucket)
3317        }
3318    }
3319
3320    #[test]
3321    fn test_should_send_icmpv4_error() {
3322        let src_ip = TEST_ADDRS_V4.local_ip;
3323        let dst_ip = TEST_ADDRS_V4.remote_ip;
3324        let frame_dst = FrameDestination::Individual { local: true };
3325        let multicast_ip_1 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 1])).unwrap();
3326        let multicast_ip_2 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 2])).unwrap();
3327
3328        // Should Send, unless non initial fragment.
3329        assert!(should_send_icmpv4_error(
3330            Some(frame_dst),
3331            src_ip,
3332            dst_ip,
3333            Ipv4FragmentType::InitialFragment
3334        ));
3335        assert!(should_send_icmpv4_error(None, src_ip, dst_ip, Ipv4FragmentType::InitialFragment));
3336        assert!(!should_send_icmpv4_error(
3337            Some(frame_dst),
3338            src_ip,
3339            dst_ip,
3340            Ipv4FragmentType::NonInitialFragment
3341        ));
3342
3343        // Should not send because destined for IP broadcast addr
3344        assert!(!should_send_icmpv4_error(
3345            Some(frame_dst),
3346            src_ip,
3347            Ipv4::LIMITED_BROADCAST_ADDRESS,
3348            Ipv4FragmentType::InitialFragment
3349        ));
3350        assert!(!should_send_icmpv4_error(
3351            Some(frame_dst),
3352            src_ip,
3353            Ipv4::LIMITED_BROADCAST_ADDRESS,
3354            Ipv4FragmentType::NonInitialFragment
3355        ));
3356
3357        // Should not send because destined for multicast addr
3358        assert!(!should_send_icmpv4_error(
3359            Some(frame_dst),
3360            src_ip,
3361            multicast_ip_1,
3362            Ipv4FragmentType::InitialFragment
3363        ));
3364        assert!(!should_send_icmpv4_error(
3365            Some(frame_dst),
3366            src_ip,
3367            multicast_ip_1,
3368            Ipv4FragmentType::NonInitialFragment
3369        ));
3370
3371        // Should not send because Link Layer Broadcast.
3372        assert!(!should_send_icmpv4_error(
3373            Some(FrameDestination::Broadcast),
3374            src_ip,
3375            dst_ip,
3376            Ipv4FragmentType::InitialFragment
3377        ));
3378        assert!(!should_send_icmpv4_error(
3379            Some(FrameDestination::Broadcast),
3380            src_ip,
3381            dst_ip,
3382            Ipv4FragmentType::NonInitialFragment
3383        ));
3384
3385        // Should not send because from loopback addr
3386        assert!(!should_send_icmpv4_error(
3387            Some(frame_dst),
3388            Ipv4::LOOPBACK_ADDRESS,
3389            dst_ip,
3390            Ipv4FragmentType::InitialFragment
3391        ));
3392        assert!(!should_send_icmpv4_error(
3393            Some(frame_dst),
3394            Ipv4::LOOPBACK_ADDRESS,
3395            dst_ip,
3396            Ipv4FragmentType::NonInitialFragment
3397        ));
3398
3399        // Should not send because from limited broadcast addr
3400        assert!(!should_send_icmpv4_error(
3401            Some(frame_dst),
3402            Ipv4::LIMITED_BROADCAST_ADDRESS,
3403            dst_ip,
3404            Ipv4FragmentType::InitialFragment
3405        ));
3406        assert!(!should_send_icmpv4_error(
3407            Some(frame_dst),
3408            Ipv4::LIMITED_BROADCAST_ADDRESS,
3409            dst_ip,
3410            Ipv4FragmentType::NonInitialFragment
3411        ));
3412
3413        // Should not send because from multicast addr
3414        assert!(!should_send_icmpv4_error(
3415            Some(frame_dst),
3416            multicast_ip_2,
3417            dst_ip,
3418            Ipv4FragmentType::InitialFragment
3419        ));
3420        assert!(!should_send_icmpv4_error(
3421            Some(frame_dst),
3422            multicast_ip_2,
3423            dst_ip,
3424            Ipv4FragmentType::NonInitialFragment
3425        ));
3426
3427        // Should not send because from class E addr
3428        assert!(!should_send_icmpv4_error(
3429            Some(frame_dst),
3430            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3431            dst_ip,
3432            Ipv4FragmentType::InitialFragment
3433        ));
3434        assert!(!should_send_icmpv4_error(
3435            Some(frame_dst),
3436            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3437            dst_ip,
3438            Ipv4FragmentType::NonInitialFragment
3439        ));
3440    }
3441
3442    #[test]
3443    fn test_should_send_icmpv6_error() {
3444        let src_ip = TEST_ADDRS_V6.local_ip;
3445        let dst_ip = TEST_ADDRS_V6.remote_ip;
3446        let frame_dst = FrameDestination::Individual { local: true };
3447        let multicast_ip_1 =
3448            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 1])).unwrap();
3449        let multicast_ip_2 =
3450            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 2])).unwrap();
3451
3452        // Should Send.
3453        assert!(should_send_icmpv6_error(
3454            Some(frame_dst),
3455            src_ip,
3456            dst_ip,
3457            false /* allow_dst_multicast */
3458        ));
3459        assert!(should_send_icmpv6_error(None, src_ip, dst_ip, false /* allow_dst_multicast */));
3460        assert!(should_send_icmpv6_error(
3461            Some(frame_dst),
3462            src_ip,
3463            dst_ip,
3464            true /* allow_dst_multicast */
3465        ));
3466
3467        // Should not send because destined for multicast addr, unless exception
3468        // applies.
3469        assert!(!should_send_icmpv6_error(
3470            Some(frame_dst),
3471            src_ip,
3472            multicast_ip_1,
3473            false /* allow_dst_multicast */
3474        ));
3475        assert!(should_send_icmpv6_error(
3476            Some(frame_dst),
3477            src_ip,
3478            multicast_ip_1,
3479            true /* allow_dst_multicast */
3480        ));
3481
3482        // Should not send because Link Layer Broadcast, unless exception
3483        // applies.
3484        assert!(!should_send_icmpv6_error(
3485            Some(FrameDestination::Broadcast),
3486            src_ip,
3487            dst_ip,
3488            false /* allow_dst_multicast */
3489        ));
3490        assert!(should_send_icmpv6_error(
3491            Some(FrameDestination::Broadcast),
3492            src_ip,
3493            dst_ip,
3494            true /* allow_dst_multicast */
3495        ));
3496
3497        // Should not send because from loopback addr.
3498        assert!(!should_send_icmpv6_error(
3499            Some(frame_dst),
3500            Ipv6::LOOPBACK_ADDRESS,
3501            dst_ip,
3502            false /* allow_dst_multicast */
3503        ));
3504        assert!(!should_send_icmpv6_error(
3505            Some(frame_dst),
3506            Ipv6::LOOPBACK_ADDRESS,
3507            dst_ip,
3508            true /* allow_dst_multicast */
3509        ));
3510
3511        // Should not send because from multicast addr.
3512        assert!(!should_send_icmpv6_error(
3513            Some(frame_dst),
3514            multicast_ip_2,
3515            dst_ip,
3516            false /* allow_dst_multicast */
3517        ));
3518        assert!(!should_send_icmpv6_error(
3519            Some(frame_dst),
3520            multicast_ip_2,
3521            dst_ip,
3522            true /* allow_dst_multicast */
3523        ));
3524
3525        // Should not send because from multicast addr, even though dest
3526        // multicast exception applies.
3527        assert!(!should_send_icmpv6_error(
3528            Some(FrameDestination::Broadcast),
3529            multicast_ip_2,
3530            dst_ip,
3531            false /* allow_dst_multicast */
3532        ));
3533        assert!(!should_send_icmpv6_error(
3534            Some(FrameDestination::Broadcast),
3535            multicast_ip_2,
3536            dst_ip,
3537            true /* allow_dst_multicast */
3538        ));
3539        assert!(!should_send_icmpv6_error(
3540            Some(frame_dst),
3541            multicast_ip_2,
3542            multicast_ip_1,
3543            false /* allow_dst_multicast */
3544        ));
3545        assert!(!should_send_icmpv6_error(
3546            Some(frame_dst),
3547            multicast_ip_2,
3548            multicast_ip_1,
3549            true /* allow_dst_multicast */
3550        ));
3551    }
3552
3553    // Tests that only require an ICMP stack. Unlike the preceding tests, these
3554    // only test the ICMP stack and state, and fake everything else. We define
3555    // the `FakeIcmpv4Ctx` and `FakeIcmpv6Ctx` types, which we wrap in a
3556    // `FakeCtx` to provide automatic implementations of a number of required
3557    // traits. The rest we implement manually.
3558
3559    #[derive(Default)]
3560    pub(super) struct FakeIcmpBindingsCtxState<I: IpExt> {
3561        _marker: core::marker::PhantomData<I>,
3562    }
3563
3564    impl InnerIcmpv4Context<FakeIcmpBindingsCtx<Ipv4>> for FakeIcmpCoreCtx<Ipv4> {
3565        fn should_send_timestamp_reply(&self) -> bool {
3566            false
3567        }
3568    }
3569    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv4>, FakeIcmpBindingsCtx<Ipv4>, Ipv4);
3570    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv6>, FakeIcmpBindingsCtx<Ipv6>, Ipv6);
3571
3572    impl<I: IpExt + FilterIpExt> IpSocketHandler<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3573        fn new_ip_socket<O>(
3574            &mut self,
3575            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3576            device: Option<EitherDeviceId<&Self::DeviceId, &Self::WeakDeviceId>>,
3577            local_ip: Option<IpDeviceAddr<I::Addr>>,
3578            remote_ip: SocketIpAddr<I::Addr>,
3579            proto: I::Proto,
3580            options: &O,
3581        ) -> Result<IpSock<I, Self::WeakDeviceId>, IpSockCreationError>
3582        where
3583            O: RouteResolutionOptions<I>,
3584        {
3585            self.ip_socket_ctx.new_ip_socket(
3586                bindings_ctx,
3587                device,
3588                local_ip,
3589                remote_ip,
3590                proto,
3591                options,
3592            )
3593        }
3594
3595        fn send_ip_packet<S, O>(
3596            &mut self,
3597            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3598            socket: &IpSock<I, Self::WeakDeviceId>,
3599            body: S,
3600            options: &O,
3601            tx_meta: FakeTxMetadata,
3602        ) -> Result<(), IpSockSendError>
3603        where
3604            S: TransportPacketSerializer<I>,
3605            S::Buffer: BufferMut,
3606            O: SendOptions<I> + RouteResolutionOptions<I>,
3607        {
3608            self.ip_socket_ctx.send_ip_packet(bindings_ctx, socket, body, options, tx_meta)
3609        }
3610
3611        fn confirm_reachable<O>(
3612            &mut self,
3613            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3614            socket: &IpSock<I, Self::WeakDeviceId>,
3615            options: &O,
3616        ) where
3617            O: RouteResolutionOptions<I>,
3618        {
3619            self.ip_socket_ctx.confirm_reachable(bindings_ctx, socket, options)
3620        }
3621    }
3622
3623    impl IpDeviceHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3624        fn is_router_device(&mut self, _device_id: &Self::DeviceId) -> bool {
3625            unimplemented!()
3626        }
3627
3628        fn set_default_hop_limit(&mut self, _device_id: &Self::DeviceId, _hop_limit: NonZeroU8) {
3629            unreachable!()
3630        }
3631    }
3632
3633    impl IpDeviceEgressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3634        fn with_next_packet_id<O, F: FnOnce(&()) -> O>(&self, cb: F) -> O {
3635            cb(&())
3636        }
3637
3638        fn get_local_addr_for_remote(
3639            &mut self,
3640            _device_id: &Self::DeviceId,
3641            _remote: Option<SpecifiedAddr<Ipv6Addr>>,
3642        ) -> Option<IpDeviceAddr<Ipv6Addr>> {
3643            unimplemented!()
3644        }
3645
3646        fn get_hop_limit(&mut self, _device_id: &Self::DeviceId) -> NonZeroU8 {
3647            unimplemented!()
3648        }
3649    }
3650
3651    impl IpDeviceIngressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3652        fn address_status_for_device(
3653            &mut self,
3654            _addr: SpecifiedAddr<Ipv6Addr>,
3655            _device_id: &Self::DeviceId,
3656        ) -> AddressStatus<Ipv6PresentAddressStatus> {
3657            unimplemented!()
3658        }
3659    }
3660
3661    impl Ipv6DeviceHandler<FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3662        type LinkLayerAddr = Uninstantiable;
3663
3664        fn get_link_layer_addr(&mut self, _device_id: &Self::DeviceId) -> Option<Uninstantiable> {
3665            unimplemented!()
3666        }
3667
3668        fn set_discovered_retrans_timer(
3669            &mut self,
3670            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3671            _device_id: &Self::DeviceId,
3672            _retrans_timer: NonZeroDuration,
3673        ) {
3674            unimplemented!()
3675        }
3676
3677        fn handle_received_dad_neighbor_solicitation(
3678            &mut self,
3679            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3680            _device_id: &Self::DeviceId,
3681            _addr: UnicastAddr<Ipv6Addr>,
3682            _nonce: Option<NdpNonce<&'_ [u8]>>,
3683        ) -> IpAddressState {
3684            unimplemented!()
3685        }
3686
3687        fn handle_received_neighbor_advertisement(
3688            &mut self,
3689            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3690            _device_id: &Self::DeviceId,
3691            _addr: UnicastAddr<Ipv6Addr>,
3692        ) -> IpAddressState {
3693            unimplemented!()
3694        }
3695
3696        fn set_link_mtu(&mut self, _device_id: &Self::DeviceId, _mtu: Mtu) {
3697            unimplemented!()
3698        }
3699
3700        fn update_discovered_ipv6_route(
3701            &mut self,
3702            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3703            _device_id: &Self::DeviceId,
3704            _route: Ipv6DiscoveredRoute,
3705            _lifetime: Option<NonZeroNdpLifetime>,
3706        ) {
3707            unimplemented!()
3708        }
3709
3710        fn apply_slaac_update(
3711            &mut self,
3712            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3713            _device_id: &Self::DeviceId,
3714            _subnet: Subnet<Ipv6Addr>,
3715            _preferred_lifetime: Option<NonZeroNdpLifetime>,
3716            _valid_lifetime: Option<NonZeroNdpLifetime>,
3717        ) {
3718            unimplemented!()
3719        }
3720
3721        fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
3722            &mut self,
3723            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3724            _device: &FakeDeviceId,
3725            _src_ip: Ipv6SourceAddr,
3726            _dst_ip: SpecifiedAddr<Ipv6Addr>,
3727            _packet: MldPacket<B>,
3728            _header_info: &H,
3729        ) {
3730            unimplemented!()
3731        }
3732    }
3733
3734    impl IpLayerHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3735        fn send_ip_packet_from_device<S>(
3736            &mut self,
3737            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3738            _meta: SendIpPacketMeta<Ipv6, &Self::DeviceId, Option<SpecifiedAddr<Ipv6Addr>>>,
3739            _body: S,
3740        ) -> Result<(), IpSendFrameError<S>> {
3741            unimplemented!()
3742        }
3743
3744        fn send_ip_frame<S>(
3745            &mut self,
3746            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3747            _device: &Self::DeviceId,
3748            _destination: IpPacketDestination<Ipv6, &Self::DeviceId>,
3749            _body: S,
3750        ) -> Result<(), IpSendFrameError<S>>
3751        where
3752            S: Serializer,
3753            S::Buffer: BufferMut,
3754        {
3755            unimplemented!()
3756        }
3757    }
3758
3759    impl NudIpHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3760        fn handle_neighbor_probe(
3761            &mut self,
3762            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3763            _device_id: &Self::DeviceId,
3764            _neighbor: SpecifiedAddr<Ipv6Addr>,
3765            _link_addr: &[u8],
3766        ) {
3767            unimplemented!()
3768        }
3769
3770        fn handle_neighbor_confirmation(
3771            &mut self,
3772            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3773            _device_id: &Self::DeviceId,
3774            _neighbor: SpecifiedAddr<Ipv6Addr>,
3775            _link_addr: &[u8],
3776            _flags: ConfirmationFlags,
3777        ) {
3778            unimplemented!()
3779        }
3780
3781        fn flush_neighbor_table(
3782            &mut self,
3783            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3784            _device_id: &Self::DeviceId,
3785        ) {
3786            unimplemented!()
3787        }
3788    }
3789
3790    #[test]
3791    fn test_receive_icmpv4_error() {
3792        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
3793        // the value 0) and, b) different from each other.
3794        const ICMP_ID: u16 = 0x0F;
3795        const SEQ_NUM: u16 = 0xF0;
3796
3797        /// Test receiving an ICMP error message.
3798        ///
3799        /// Test that receiving an ICMP error message with the given code and
3800        /// message contents, and containing the given original IPv4 packet,
3801        /// results in the counter values in `assert_counters`. After that
3802        /// assertion passes, `f` is called on the context so that the caller
3803        /// can perform whatever extra validation they want.
3804        ///
3805        /// The error message will be sent from `TEST_ADDRS_V4.remote_ip` to
3806        /// `TEST_ADDRS_V4.local_ip`. Before the message is sent, an ICMP
3807        /// socket will be established with the ID `ICMP_ID`, and
3808        /// `test_receive_icmpv4_error_helper` will assert that its `SocketId`
3809        /// is 0. This allows the caller to craft the `original_packet` so that
3810        /// it should be delivered to this socket.
3811        fn test_receive_icmpv4_error_helper<
3812            C: Debug,
3813            M: IcmpMessage<Ipv4, Code = C> + Debug,
3814            F: Fn(&FakeIcmpCtx<Ipv4>),
3815        >(
3816            original_packet: &mut [u8],
3817            code: C,
3818            msg: M,
3819            f: F,
3820        ) {
3821            set_logger_for_test();
3822
3823            let mut ctx: FakeIcmpCtx<Ipv4> = FakeIcmpCtx::default();
3824
3825            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
3826            <IcmpIpTransportContext as IpTransportContext<Ipv4, _, _>>::receive_ip_packet(
3827                core_ctx,
3828                bindings_ctx,
3829                &FakeDeviceId,
3830                Ipv4SourceAddr::new(*TEST_ADDRS_V4.remote_ip).unwrap(),
3831                TEST_ADDRS_V4.local_ip,
3832                Buf::new(original_packet, ..)
3833                    .encapsulate(IcmpPacketBuilder::new(
3834                        TEST_ADDRS_V4.remote_ip,
3835                        TEST_ADDRS_V4.local_ip,
3836                        code,
3837                        msg,
3838                    ))
3839                    .serialize_vec_outer()
3840                    .unwrap(),
3841                &LocalDeliveryPacketInfo::default(),
3842            )
3843            .unwrap();
3844            f(&ctx);
3845        }
3846        // Test that, when we receive various ICMPv4 error messages, we properly
3847        // pass them up to the IP layer and, sometimes, to the transport layer.
3848
3849        // First, test with an original packet containing an ICMP message. Since
3850        // this test fake supports ICMP sockets, this error can be delivered all
3851        // the way up the stack.
3852
3853        // A buffer containing an ICMP echo request with ID `ICMP_ID` and
3854        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
3855        // ICMP error message which contains this as its original packet should
3856        // be delivered to the socket created in
3857        // `test_receive_icmpv4_error_helper`.
3858        let mut buffer = Buf::new(&mut [], ..)
3859            .encapsulate(IcmpPacketBuilder::<Ipv4, _>::new(
3860                TEST_ADDRS_V4.local_ip,
3861                TEST_ADDRS_V4.remote_ip,
3862                IcmpZeroCode,
3863                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
3864            ))
3865            .encapsulate(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3866                TEST_ADDRS_V4.local_ip,
3867                TEST_ADDRS_V4.remote_ip,
3868                64,
3869                Ipv4Proto::Icmp,
3870            ))
3871            .serialize_vec_outer()
3872            .unwrap();
3873
3874        test_receive_icmpv4_error_helper(
3875            buffer.as_mut(),
3876            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3877            IcmpDestUnreachable::default(),
3878            |CtxPair { core_ctx, bindings_ctx: _ }| {
3879                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3880                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3881                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3882                let err = Icmpv4ErrorCode::DestUnreachable(
3883                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3884                    IcmpDestUnreachable::default(),
3885                );
3886                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3887            },
3888        );
3889
3890        test_receive_icmpv4_error_helper(
3891            buffer.as_mut(),
3892            Icmpv4TimeExceededCode::TtlExpired,
3893            IcmpTimeExceeded::default(),
3894            |CtxPair { core_ctx, bindings_ctx: _ }| {
3895                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3896                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3897                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3898                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3899                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3900            },
3901        );
3902
3903        test_receive_icmpv4_error_helper(
3904            buffer.as_mut(),
3905            Icmpv4ParameterProblemCode::PointerIndicatesError,
3906            Icmpv4ParameterProblem::new(0),
3907            |CtxPair { core_ctx, bindings_ctx: _ }| {
3908                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3909                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3910                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3911                let err = Icmpv4ErrorCode::ParameterProblem(
3912                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3913                );
3914                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3915            },
3916        );
3917
3918        // Second, test with an original packet containing a malformed ICMP
3919        // packet (we accomplish this by leaving the IP packet's body empty). We
3920        // should process this packet in
3921        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
3922        // further - in particular, we should not dispatch to the Echo sockets.
3923
3924        let mut buffer = Buf::new(&mut [], ..)
3925            .encapsulate(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3926                TEST_ADDRS_V4.local_ip,
3927                TEST_ADDRS_V4.remote_ip,
3928                64,
3929                Ipv4Proto::Icmp,
3930            ))
3931            .serialize_vec_outer()
3932            .unwrap();
3933
3934        test_receive_icmpv4_error_helper(
3935            buffer.as_mut(),
3936            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3937            IcmpDestUnreachable::default(),
3938            |CtxPair { core_ctx, bindings_ctx: _ }| {
3939                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3940                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3941                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3942                let err = Icmpv4ErrorCode::DestUnreachable(
3943                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3944                    IcmpDestUnreachable::default(),
3945                );
3946                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3947            },
3948        );
3949
3950        test_receive_icmpv4_error_helper(
3951            buffer.as_mut(),
3952            Icmpv4TimeExceededCode::TtlExpired,
3953            IcmpTimeExceeded::default(),
3954            |CtxPair { core_ctx, bindings_ctx: _ }| {
3955                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3956                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3957                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3958                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3959                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3960            },
3961        );
3962
3963        test_receive_icmpv4_error_helper(
3964            buffer.as_mut(),
3965            Icmpv4ParameterProblemCode::PointerIndicatesError,
3966            Icmpv4ParameterProblem::new(0),
3967            |CtxPair { core_ctx, bindings_ctx: _ }| {
3968                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3969                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3970                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3971                let err = Icmpv4ErrorCode::ParameterProblem(
3972                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3973                );
3974                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3975            },
3976        );
3977
3978        // Third, test with an original packet containing a UDP packet. This
3979        // allows us to verify that protocol numbers are handled properly by
3980        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
3981        // called.
3982
3983        let mut buffer = Buf::new(&mut [], ..)
3984            .encapsulate(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3985                TEST_ADDRS_V4.local_ip,
3986                TEST_ADDRS_V4.remote_ip,
3987                64,
3988                IpProto::Udp.into(),
3989            ))
3990            .serialize_vec_outer()
3991            .unwrap();
3992
3993        test_receive_icmpv4_error_helper(
3994            buffer.as_mut(),
3995            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3996            IcmpDestUnreachable::default(),
3997            |CtxPair { core_ctx, bindings_ctx: _ }| {
3998                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3999                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4000                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4001                let err = Icmpv4ErrorCode::DestUnreachable(
4002                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4003                    IcmpDestUnreachable::default(),
4004                );
4005                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4006            },
4007        );
4008
4009        test_receive_icmpv4_error_helper(
4010            buffer.as_mut(),
4011            Icmpv4TimeExceededCode::TtlExpired,
4012            IcmpTimeExceeded::default(),
4013            |CtxPair { core_ctx, bindings_ctx: _ }| {
4014                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4015                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4016                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4017                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
4018                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4019            },
4020        );
4021
4022        test_receive_icmpv4_error_helper(
4023            buffer.as_mut(),
4024            Icmpv4ParameterProblemCode::PointerIndicatesError,
4025            Icmpv4ParameterProblem::new(0),
4026            |CtxPair { core_ctx, bindings_ctx: _ }| {
4027                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4028                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4029                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4030                let err = Icmpv4ErrorCode::ParameterProblem(
4031                    Icmpv4ParameterProblemCode::PointerIndicatesError,
4032                );
4033                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4034            },
4035        );
4036    }
4037
4038    #[test]
4039    fn test_receive_icmpv6_error() {
4040        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
4041        // the value 0) and, b) different from each other.
4042        const ICMP_ID: u16 = 0x0F;
4043        const SEQ_NUM: u16 = 0xF0;
4044
4045        /// Test receiving an ICMPv6 error message.
4046        ///
4047        /// Test that receiving an ICMP error message with the given code and
4048        /// message contents, and containing the given original IPv4 packet,
4049        /// results in the counter values in `assert_counters`. After that
4050        /// assertion passes, `f` is called on the context so that the caller
4051        /// can perform whatever extra validation they want.
4052        ///
4053        /// The error message will be sent from `TEST_ADDRS_V6.remote_ip` to
4054        /// `TEST_ADDRS_V6.local_ip`. Before the message is sent, an ICMP
4055        /// socket will be established with the ID `ICMP_ID`, and
4056        /// `test_receive_icmpv6_error_helper` will assert that its `SocketId`
4057        /// is 0. This allows the caller to craft the `original_packet` so that
4058        /// it should be delivered to this socket.
4059        fn test_receive_icmpv6_error_helper<
4060            C: Debug,
4061            M: IcmpMessage<Ipv6, Code = C> + Debug,
4062            F: Fn(&FakeIcmpCtx<Ipv6>),
4063        >(
4064            original_packet: &mut [u8],
4065            code: C,
4066            msg: M,
4067            f: F,
4068        ) {
4069            set_logger_for_test();
4070
4071            let mut ctx = FakeIcmpCtx::<Ipv6>::default();
4072            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
4073            <IcmpIpTransportContext as IpTransportContext<Ipv6, _, _>>::receive_ip_packet(
4074                core_ctx,
4075                bindings_ctx,
4076                &FakeDeviceId,
4077                TEST_ADDRS_V6.remote_ip.get().try_into().unwrap(),
4078                TEST_ADDRS_V6.local_ip,
4079                Buf::new(original_packet, ..)
4080                    .encapsulate(IcmpPacketBuilder::new(
4081                        TEST_ADDRS_V6.remote_ip,
4082                        TEST_ADDRS_V6.local_ip,
4083                        code,
4084                        msg,
4085                    ))
4086                    .serialize_vec_outer()
4087                    .unwrap(),
4088                &LocalDeliveryPacketInfo::default(),
4089            )
4090            .unwrap();
4091            f(&ctx);
4092        }
4093        // Test that, when we receive various ICMPv6 error messages, we properly
4094        // pass them up to the IP layer and, sometimes, to the transport layer.
4095
4096        // First, test with an original packet containing an ICMPv6 message.
4097        // Since this test fake supports ICMPv6 sockets, this error can be
4098        // delivered all the way up the stack.
4099
4100        // A buffer containing an ICMPv6 echo request with ID `ICMP_ID` and
4101        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
4102        // ICMPv6 error message which contains this as its original packet
4103        // should be delivered to the socket created in
4104        // `test_receive_icmpv6_error_helper`.
4105        let mut buffer = Buf::new(&mut [], ..)
4106            .encapsulate(IcmpPacketBuilder::<Ipv6, _>::new(
4107                TEST_ADDRS_V6.local_ip,
4108                TEST_ADDRS_V6.remote_ip,
4109                IcmpZeroCode,
4110                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
4111            ))
4112            .encapsulate(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4113                TEST_ADDRS_V6.local_ip,
4114                TEST_ADDRS_V6.remote_ip,
4115                64,
4116                Ipv6Proto::Icmpv6,
4117            ))
4118            .serialize_vec_outer()
4119            .unwrap();
4120
4121        test_receive_icmpv6_error_helper(
4122            buffer.as_mut(),
4123            Icmpv6DestUnreachableCode::NoRoute,
4124            IcmpDestUnreachable::default(),
4125            |CtxPair { core_ctx, bindings_ctx: _ }| {
4126                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4127                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4128                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4129                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4130                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4131            },
4132        );
4133
4134        test_receive_icmpv6_error_helper(
4135            buffer.as_mut(),
4136            Icmpv6TimeExceededCode::HopLimitExceeded,
4137            IcmpTimeExceeded::default(),
4138            |CtxPair { core_ctx, bindings_ctx: _ }| {
4139                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4140                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4141                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4142                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4143                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4144            },
4145        );
4146
4147        test_receive_icmpv6_error_helper(
4148            buffer.as_mut(),
4149            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4150            Icmpv6ParameterProblem::new(0),
4151            |CtxPair { core_ctx, bindings_ctx: _ }| {
4152                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4153                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4154                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4155                let err = Icmpv6ErrorCode::ParameterProblem(
4156                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4157                );
4158                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4159            },
4160        );
4161
4162        // Second, test with an original packet containing a malformed ICMPv6
4163        // packet (we accomplish this by leaving the IP packet's body empty). We
4164        // should process this packet in
4165        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
4166        // further - in particular, we should not call into Echo sockets.
4167
4168        let mut buffer = Buf::new(&mut [], ..)
4169            .encapsulate(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4170                TEST_ADDRS_V6.local_ip,
4171                TEST_ADDRS_V6.remote_ip,
4172                64,
4173                Ipv6Proto::Icmpv6,
4174            ))
4175            .serialize_vec_outer()
4176            .unwrap();
4177
4178        test_receive_icmpv6_error_helper(
4179            buffer.as_mut(),
4180            Icmpv6DestUnreachableCode::NoRoute,
4181            IcmpDestUnreachable::default(),
4182            |CtxPair { core_ctx, bindings_ctx: _ }| {
4183                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4184                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4185                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4186                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4187                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4188            },
4189        );
4190
4191        test_receive_icmpv6_error_helper(
4192            buffer.as_mut(),
4193            Icmpv6TimeExceededCode::HopLimitExceeded,
4194            IcmpTimeExceeded::default(),
4195            |CtxPair { core_ctx, bindings_ctx: _ }| {
4196                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4197                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4198                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4199                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4200                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4201            },
4202        );
4203
4204        test_receive_icmpv6_error_helper(
4205            buffer.as_mut(),
4206            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4207            Icmpv6ParameterProblem::new(0),
4208            |CtxPair { core_ctx, bindings_ctx: _ }| {
4209                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4210                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4211                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4212                let err = Icmpv6ErrorCode::ParameterProblem(
4213                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4214                );
4215                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4216            },
4217        );
4218
4219        // Third, test with an original packet containing a UDP packet. This
4220        // allows us to verify that protocol numbers are handled properly by
4221        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
4222        // called.
4223
4224        let mut buffer = Buf::new(&mut [], ..)
4225            .encapsulate(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4226                TEST_ADDRS_V6.local_ip,
4227                TEST_ADDRS_V6.remote_ip,
4228                64,
4229                IpProto::Udp.into(),
4230            ))
4231            .serialize_vec_outer()
4232            .unwrap();
4233
4234        test_receive_icmpv6_error_helper(
4235            buffer.as_mut(),
4236            Icmpv6DestUnreachableCode::NoRoute,
4237            IcmpDestUnreachable::default(),
4238            |CtxPair { core_ctx, bindings_ctx: _ }| {
4239                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4240                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4241                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4242                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4243                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4244            },
4245        );
4246
4247        test_receive_icmpv6_error_helper(
4248            buffer.as_mut(),
4249            Icmpv6TimeExceededCode::HopLimitExceeded,
4250            IcmpTimeExceeded::default(),
4251            |CtxPair { core_ctx, bindings_ctx: _ }| {
4252                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4253                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4254                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4255                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4256                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4257            },
4258        );
4259
4260        test_receive_icmpv6_error_helper(
4261            buffer.as_mut(),
4262            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4263            Icmpv6ParameterProblem::new(0),
4264            |CtxPair { core_ctx, bindings_ctx: _ }| {
4265                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4266                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4267                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4268                let err = Icmpv6ErrorCode::ParameterProblem(
4269                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4270                );
4271                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4272            },
4273        );
4274    }
4275
4276    #[test]
4277    fn test_error_rate_limit() {
4278        set_logger_for_test();
4279
4280        /// Call `send_icmpv4_ttl_expired` with fake values.
4281        fn send_icmpv4_ttl_expired_helper(
4282            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4283        ) {
4284            send_icmpv4_ttl_expired(
4285                core_ctx,
4286                bindings_ctx,
4287                &FakeDeviceId,
4288                Some(FrameDestination::Individual { local: true }),
4289                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4290                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4291                IpProto::Udp.into(),
4292                Buf::new(&mut [], ..),
4293                0,
4294                Ipv4FragmentType::InitialFragment,
4295                &Default::default(),
4296            );
4297        }
4298
4299        /// Call `send_icmpv4_parameter_problem` with fake values.
4300        fn send_icmpv4_parameter_problem_helper(
4301            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4302        ) {
4303            send_icmpv4_parameter_problem(
4304                core_ctx,
4305                bindings_ctx,
4306                &FakeDeviceId,
4307                Some(FrameDestination::Individual { local: true }),
4308                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4309                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4310                Icmpv4ParameterProblemCode::PointerIndicatesError,
4311                Icmpv4ParameterProblem::new(0),
4312                Buf::new(&mut [], ..),
4313                0,
4314                Ipv4FragmentType::InitialFragment,
4315                &Default::default(),
4316            );
4317        }
4318
4319        /// Call `send_icmpv4_dest_unreachable` with fake values.
4320        fn send_icmpv4_dest_unreachable_helper(
4321            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4322        ) {
4323            send_icmpv4_dest_unreachable(
4324                core_ctx,
4325                bindings_ctx,
4326                Some(&FakeDeviceId),
4327                Some(FrameDestination::Individual { local: true }),
4328                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4329                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4330                Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4331                Buf::new(&mut [], ..),
4332                0,
4333                Ipv4FragmentType::InitialFragment,
4334                &Default::default(),
4335            );
4336        }
4337
4338        /// Call `send_icmpv6_ttl_expired` with fake values.
4339        fn send_icmpv6_ttl_expired_helper(
4340            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4341        ) {
4342            send_icmpv6_ttl_expired(
4343                core_ctx,
4344                bindings_ctx,
4345                &FakeDeviceId,
4346                Some(FrameDestination::Individual { local: true }),
4347                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4348                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4349                IpProto::Udp.into(),
4350                Buf::new(&mut [], ..),
4351                0,
4352                &Default::default(),
4353            );
4354        }
4355
4356        /// Call `send_icmpv6_packet_too_big` with fake values.
4357        fn send_icmpv6_packet_too_big_helper(
4358            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4359        ) {
4360            send_icmpv6_packet_too_big(
4361                core_ctx,
4362                bindings_ctx,
4363                &FakeDeviceId,
4364                Some(FrameDestination::Individual { local: true }),
4365                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4366                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4367                IpProto::Udp.into(),
4368                Mtu::new(0),
4369                Buf::new(&mut [], ..),
4370                0,
4371                &Default::default(),
4372            );
4373        }
4374
4375        /// Call `send_icmpv6_parameter_problem` with fake values.
4376        fn send_icmpv6_parameter_problem_helper(
4377            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4378        ) {
4379            send_icmpv6_parameter_problem(
4380                core_ctx,
4381                bindings_ctx,
4382                &FakeDeviceId,
4383                Some(FrameDestination::Individual { local: true }),
4384                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4385                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4386                Icmpv6ParameterProblemCode::ErroneousHeaderField,
4387                Icmpv6ParameterProblem::new(0),
4388                Buf::new(&mut [], ..),
4389                false,
4390                &Default::default(),
4391            );
4392        }
4393
4394        /// Call `send_icmpv6_dest_unreachable` with fake values.
4395        fn send_icmpv6_dest_unreachable_helper(
4396            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4397        ) {
4398            send_icmpv6_dest_unreachable(
4399                core_ctx,
4400                bindings_ctx,
4401                Some(&FakeDeviceId),
4402                Some(FrameDestination::Individual { local: true }),
4403                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4404                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4405                Icmpv6DestUnreachableCode::NoRoute,
4406                Buf::new(&mut [], ..),
4407                &Default::default(),
4408            );
4409        }
4410
4411        // Run tests for each function that sends error messages to make sure
4412        // they're all properly rate limited.
4413
4414        fn run_test<I: IpExt, W: Fn(u64) -> FakeIcmpCtx<I>, S: Fn(&mut FakeIcmpCtx<I>)>(
4415            with_errors_per_second: W,
4416            send: S,
4417        ) {
4418            // Note that we could theoretically have more precise tests here
4419            // (e.g., a test that we send at the correct rate over the long
4420            // term), but those would amount to testing the `TokenBucket`
4421            // implementation, which has its own exhaustive tests. Instead, we
4422            // just have a few sanity checks to make sure that we're actually
4423            // invoking it when we expect to (as opposed to bypassing it
4424            // entirely or something).
4425
4426            // Test that, if no time has elapsed, we can successfully send up to
4427            // `ERRORS_PER_SECOND` error messages, but no more.
4428
4429            // Don't use `DEFAULT_ERRORS_PER_SECOND` because it's 2^16 and it
4430            // makes this test take a long time.
4431            const ERRORS_PER_SECOND: u64 = 64;
4432
4433            let mut ctx = with_errors_per_second(ERRORS_PER_SECOND);
4434
4435            for i in 0..ERRORS_PER_SECOND {
4436                send(&mut ctx);
4437                assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), i + 1);
4438            }
4439
4440            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4441            send(&mut ctx);
4442            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4443
4444            // Test that, if we set a rate of 0, we are not able to send any
4445            // error messages regardless of how much time has elapsed.
4446
4447            let mut ctx = with_errors_per_second(0);
4448            send(&mut ctx);
4449            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4450            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4451            send(&mut ctx);
4452            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4453            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4454            send(&mut ctx);
4455            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4456        }
4457
4458        fn with_errors_per_second_v4(errors_per_second: u64) -> FakeIcmpCtx<Ipv4> {
4459            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4460        }
4461        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_ttl_expired_helper);
4462        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_parameter_problem_helper);
4463        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_dest_unreachable_helper);
4464
4465        fn with_errors_per_second_v6(errors_per_second: u64) -> FakeIcmpCtx<Ipv6> {
4466            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4467        }
4468
4469        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_ttl_expired_helper);
4470        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_packet_too_big_helper);
4471        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_parameter_problem_helper);
4472        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_dest_unreachable_helper);
4473    }
4474}