Skip to main content

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