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