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, IpMarked, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr, Ipv6SourceAddr,
18    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 broadcast address, a multicast address, or a
2500///   Class E address)
2501///
2502/// RFC Non-Compliance: RFC 1122 Section 3.2.2 also considers the loopback
2503/// address to be one that "does not define a single host". However, that breaks
2504/// error delivery for loopback sockets. This deviation matches the behavior of
2505/// Netstack2 and Linux.
2506///
2507/// Note that `should_send_icmpv4_error` does NOT check whether the incoming
2508/// packet contained an ICMP error message. This is because that check is
2509/// unnecessary for some ICMP error conditions. The ICMP error message check can
2510/// be performed separately with `is_icmp_error_message`.
2511fn should_send_icmpv4_error(
2512    frame_dst: Option<FrameDestination>,
2513    src_ip: SpecifiedAddr<Ipv4Addr>,
2514    dst_ip: SpecifiedAddr<Ipv4Addr>,
2515) -> bool {
2516    // NOTE: We do not explicitly implement the "unspecified address" check, as
2517    // it is enforced by the types of the arguments.
2518
2519    // TODO(joshlf): Implement the rest of the rules:
2520    // - a packet destined to a subnet broadcast address
2521    // - a packet whose source address is a subnet broadcast address
2522
2523    // NOTE: The FrameDestination type has variants for unicast, multicast, and
2524    // broadcast. One implication of the fact that we only check for broadcast
2525    // here (in compliance with the RFC) is that we could, in one very unlikely
2526    // edge case, respond with an ICMP error message to an IP packet which was
2527    // sent in a link-layer multicast frame. In particular, that can happen if
2528    // we subscribe to a multicast IP group and, as a result, subscribe to the
2529    // corresponding multicast MAC address, and we receive a unicast IP packet
2530    // in a multicast link-layer frame destined to that MAC address.
2531    //
2532    // TODO(joshlf): Should we filter incoming multicast IP traffic to make sure
2533    // that it matches the multicast MAC address of the frame it was
2534    // encapsulated in?
2535    !(dst_ip.is_multicast()
2536        || dst_ip.is_limited_broadcast()
2537        || frame_dst.is_some_and(|dst| dst.is_broadcast())
2538        || src_ip.is_limited_broadcast()
2539        || src_ip.is_multicast()
2540        || src_ip.is_class_e())
2541}
2542
2543/// Should we send an ICMPv6 response?
2544///
2545/// `should_send_icmpv6_error` implements the logic described in RFC 4443
2546/// Section 2.4.e. It decides whether, upon receiving an incoming packet with
2547/// the given parameters, we should send an ICMP response or not. In particular,
2548/// we do not send an ICMP response if we've received:
2549/// - a packet destined to a multicast address
2550///   - Two exceptions to this rules:
2551///     1) the Packet Too Big Message to allow Path MTU discovery to work for
2552///        IPv6 multicast
2553///     2) the Parameter Problem Message, Code 2 reporting an unrecognized IPv6
2554///        option that has the Option Type highest-order two bits set to 10
2555/// - a packet sent as a link-layer multicast or broadcast
2556///   - same exceptions apply here as well.
2557/// - a packet whose source address does not define a single host (a
2558///   zero/unspecified address, or a multicast address)
2559///
2560/// RFC Non-Compliance: We send ICMP errors over loopback. See the comment on
2561/// [`should_send_icmpv4_error`] for more information.
2562///
2563/// If an ICMP response will be a Packet Too Big Message or a Parameter Problem
2564/// Message, Code 2 reporting an unrecognized IPv6 option that has the Option
2565/// Type highest-order two bits set to 10, `info.allow_dst_multicast` must be
2566/// set to `true` so this function will allow the exception mentioned above.
2567///
2568/// Note that `should_send_icmpv6_error` does NOT check whether the incoming
2569/// packet contained an ICMP error message. This is because that check is
2570/// unnecessary for some ICMP error conditions. The ICMP error message check can
2571/// be performed separately with `is_icmp_error_message`.
2572fn should_send_icmpv6_error(
2573    frame_dst: Option<FrameDestination>,
2574    src_ip: SpecifiedAddr<Ipv6Addr>,
2575    dst_ip: SpecifiedAddr<Ipv6Addr>,
2576    allow_dst_multicast: bool,
2577) -> bool {
2578    // NOTE: We do not explicitly implement the "unspecified address" check, as
2579    // it is enforced by the types of the arguments.
2580    let multicast_frame_dst = match frame_dst {
2581        Some(FrameDestination::Individual { local: _ }) | None => false,
2582        Some(FrameDestination::Broadcast) | Some(FrameDestination::Multicast) => true,
2583    };
2584    if (dst_ip.is_multicast() || multicast_frame_dst) && !allow_dst_multicast {
2585        return false;
2586    }
2587    if src_ip.is_multicast() {
2588        return false;
2589    }
2590    true
2591}
2592
2593/// Determine whether or not an IP packet body contains an ICMP error message
2594/// for the purposes of determining whether or not to send an ICMP response.
2595///
2596/// `is_icmp_error_or_redirect_message` checks whether `proto` is ICMP(v4) for
2597/// IPv4 or ICMPv6 for IPv6 and, if so, attempts to parse `buf` as an ICMP
2598/// packet in order to determine whether it is an error message or not. If
2599/// parsing fails, it conservatively assumes that it is an error packet in
2600/// order to avoid violating the MUST NOT directives of RFC 1122 Section 3.2.2
2601/// and [RFC 4443 Section 2.4.e].
2602///
2603/// [RFC 4443 Section 2.4.e]: https://tools.ietf.org/html/rfc4443#section-2.4
2604fn is_icmp_error_or_redirect_message<I: IcmpIpExt>(proto: I::Proto, buf: &[u8]) -> bool {
2605    proto == I::ICMP_IP_PROTO
2606        && peek_message_type::<I::IcmpMessageType>(buf)
2607            .map(IcmpMessageType::is_error_or_redirect)
2608            .unwrap_or(true)
2609}
2610
2611/// Test utilities for ICMP.
2612#[cfg(any(test, feature = "testutils"))]
2613pub(crate) mod testutil {
2614    use alloc::vec::Vec;
2615    use net_types::ethernet::Mac;
2616    use net_types::ip::{Ipv6, Ipv6Addr};
2617    use packet::{Buf, InnerPacketBuilder as _, Serializer as _};
2618    use packet_formats::icmp::ndp::options::NdpOptionBuilder;
2619    use packet_formats::icmp::ndp::{
2620        NeighborAdvertisement, NeighborSolicitation, OptionSequenceBuilder,
2621    };
2622    use packet_formats::icmp::{IcmpPacketBuilder, IcmpZeroCode};
2623    use packet_formats::ip::Ipv6Proto;
2624    use packet_formats::ipv6::Ipv6PacketBuilder;
2625
2626    use super::REQUIRED_NDP_IP_PACKET_HOP_LIMIT;
2627
2628    /// Serialize an IP packet containing a neighbor advertisement with the
2629    /// provided parameters.
2630    pub fn neighbor_advertisement_ip_packet(
2631        src_ip: Ipv6Addr,
2632        dst_ip: Ipv6Addr,
2633        router_flag: bool,
2634        solicited_flag: bool,
2635        override_flag: bool,
2636        mac: Mac,
2637    ) -> Buf<Vec<u8>> {
2638        OptionSequenceBuilder::new([NdpOptionBuilder::TargetLinkLayerAddress(&mac.bytes())].iter())
2639            .into_serializer()
2640            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
2641                src_ip,
2642                dst_ip,
2643                IcmpZeroCode,
2644                NeighborAdvertisement::new(router_flag, solicited_flag, override_flag, src_ip),
2645            ))
2646            .wrap_in(Ipv6PacketBuilder::new(
2647                src_ip,
2648                dst_ip,
2649                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
2650                Ipv6Proto::Icmpv6,
2651            ))
2652            .serialize_vec_outer()
2653            .unwrap()
2654            .unwrap_b()
2655    }
2656
2657    /// Serialize an IP packet containing a neighbor solicitation with the
2658    /// provided parameters.
2659    pub fn neighbor_solicitation_ip_packet(
2660        src_ip: Ipv6Addr,
2661        dst_ip: Ipv6Addr,
2662        target_addr: Ipv6Addr,
2663        mac: Mac,
2664    ) -> Buf<Vec<u8>> {
2665        OptionSequenceBuilder::new([NdpOptionBuilder::SourceLinkLayerAddress(&mac.bytes())].iter())
2666            .into_serializer()
2667            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
2668                src_ip,
2669                dst_ip,
2670                IcmpZeroCode,
2671                NeighborSolicitation::new(target_addr),
2672            ))
2673            .wrap_in(Ipv6PacketBuilder::new(
2674                src_ip,
2675                dst_ip,
2676                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
2677                Ipv6Proto::Icmpv6,
2678            ))
2679            .serialize_vec_outer()
2680            .unwrap()
2681            .unwrap_b()
2682    }
2683}
2684
2685#[cfg(test)]
2686mod tests {
2687    use alloc::vec;
2688    use alloc::vec::Vec;
2689    use packet_formats::icmp::ndp::options::NdpNonce;
2690
2691    use core::fmt::Debug;
2692    use core::time::Duration;
2693
2694    use net_types::ip::Subnet;
2695    use netstack3_base::testutil::{
2696        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant, FakeTxMetadata, FakeWeakDeviceId,
2697        TEST_ADDRS_V4, TEST_ADDRS_V6, TestIpExt, set_logger_for_test,
2698    };
2699    use netstack3_base::{CtxPair, Uninstantiable};
2700    use netstack3_filter::TransportPacketSerializer;
2701    use packet::{Buf, EmptyBuf};
2702    use packet_formats::icmp::mld::MldPacket;
2703    use packet_formats::ip::IpProto;
2704    use packet_formats::utils::NonZeroDuration;
2705
2706    use super::*;
2707    use crate::internal::base::{IpDeviceEgressStateContext, RouterAdvertisementEvent};
2708    use crate::internal::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx};
2709    use crate::internal::socket::{
2710        IpSock, IpSockCreationError, IpSockSendError, IpSocketHandler, SendOptions,
2711    };
2712    use crate::socket::RouteResolutionOptions;
2713
2714    use test_util::assert_geq;
2715
2716    pub(super) trait IcmpTestIpExt:
2717        TestIpExt + IpExt + FilterIpExt + IcmpCountersIpExt
2718    {
2719    }
2720    impl<I: TestIpExt + IpExt + FilterIpExt + IcmpCountersIpExt> IcmpTestIpExt for I {}
2721
2722    /// The FakeCoreCtx held as the inner state of [`FakeIcmpCoreCtx`].
2723    type InnerIpSocketCtx<I> = FakeCoreCtx<
2724        FakeIpSocketCtx<I, FakeDeviceId>,
2725        SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
2726        FakeDeviceId,
2727    >;
2728
2729    /// `FakeCoreCtx` specialized for ICMP.
2730    pub(super) struct FakeIcmpCoreCtx<I: IcmpTestIpExt> {
2731        ip_socket_ctx: InnerIpSocketCtx<I>,
2732        icmp: FakeIcmpCoreCtxState<I>,
2733    }
2734
2735    /// `FakeBindingsCtx` specialized for ICMP.
2736    type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<
2737        (),
2738        RouterAdvertisementEvent<FakeDeviceId>,
2739        FakeIcmpBindingsCtxState<I>,
2740        (),
2741    >;
2742
2743    /// A fake ICMP bindings and core contexts.
2744    ///
2745    /// This is exposed to super so it can be shared with the socket tests.
2746    pub(super) type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
2747
2748    pub(super) struct FakeIcmpCoreCtxState<I: IcmpTestIpExt> {
2749        error_send_bucket: TokenBucket<FakeInstant>,
2750        receive_icmp_error: Vec<I::ErrorCode>,
2751        rx_counters: IcmpRxCounters<I>,
2752        tx_counters: IcmpTxCounters<I>,
2753        ndp_counters: NdpCounters,
2754    }
2755
2756    impl<I: IcmpTestIpExt> FakeIcmpCoreCtx<I> {
2757        fn with_errors_per_second(errors_per_second: u64) -> Self {
2758            Self {
2759                icmp: FakeIcmpCoreCtxState {
2760                    error_send_bucket: TokenBucket::new(errors_per_second),
2761                    receive_icmp_error: Default::default(),
2762                    rx_counters: Default::default(),
2763                    tx_counters: Default::default(),
2764                    ndp_counters: Default::default(),
2765                },
2766                ip_socket_ctx: InnerIpSocketCtx::with_state(FakeIpSocketCtx::new(
2767                    core::iter::once(FakeDeviceConfig {
2768                        device: FakeDeviceId,
2769                        local_ips: vec![I::TEST_ADDRS.local_ip],
2770                        remote_ips: vec![I::TEST_ADDRS.remote_ip],
2771                    }),
2772                )),
2773            }
2774        }
2775    }
2776
2777    impl<I: IcmpTestIpExt> Default for FakeIcmpCoreCtx<I> {
2778        fn default() -> Self {
2779            Self::with_errors_per_second(DEFAULT_ERRORS_PER_SECOND)
2780        }
2781    }
2782
2783    impl<I: IcmpTestIpExt> DeviceIdContext<AnyDevice> for FakeIcmpCoreCtx<I> {
2784        type DeviceId = FakeDeviceId;
2785        type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
2786    }
2787
2788    impl<I: IcmpTestIpExt> IcmpStateContext for FakeIcmpCoreCtx<I> {}
2789    impl<I: IcmpTestIpExt> IcmpStateContext for InnerIpSocketCtx<I> {}
2790
2791    impl<I: IcmpTestIpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtx<I> {
2792        fn counters(&self) -> &IcmpRxCounters<I> {
2793            &self.icmp.rx_counters
2794        }
2795    }
2796
2797    impl<I: IcmpTestIpExt> CounterContext<IcmpTxCounters<I>> for FakeIcmpCoreCtx<I> {
2798        fn counters(&self) -> &IcmpTxCounters<I> {
2799            &self.icmp.tx_counters
2800        }
2801    }
2802
2803    impl<I: IcmpTestIpExt> CounterContext<NdpCounters> for FakeIcmpCoreCtx<I> {
2804        fn counters(&self) -> &NdpCounters {
2805            &self.icmp.ndp_counters
2806        }
2807    }
2808
2809    pub enum FakeEchoIpTransportContext {}
2810
2811    impl EchoTransportContextMarker for FakeEchoIpTransportContext {}
2812
2813    impl<I> IpTransportContext<I, FakeIcmpBindingsCtx<I>, FakeIcmpCoreCtx<I>>
2814        for FakeEchoIpTransportContext
2815    where
2816        I: IcmpTestIpExt + IpLayerIpExt,
2817    {
2818        type EarlyDemuxSocket = Never;
2819
2820        fn early_demux<B: ParseBuffer>(
2821            _core_ctx: &mut FakeIcmpCoreCtx<I>,
2822            _device: &FakeDeviceId,
2823            _src_ip: I::Addr,
2824            _dst_ip: I::Addr,
2825            _buffer: B,
2826        ) -> Option<Self::EarlyDemuxSocket> {
2827            None
2828        }
2829
2830        fn receive_icmp_error(
2831            core_ctx: &mut FakeIcmpCoreCtx<I>,
2832            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
2833            _device: &FakeDeviceId,
2834            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
2835            _original_dst_ip: SpecifiedAddr<I::Addr>,
2836            _original_body: &[u8],
2837            _err: I::ErrorCode,
2838        ) {
2839            core_ctx.icmp.rx_counters.error_delivered_to_socket.increment()
2840        }
2841
2842        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
2843            _core_ctx: &mut FakeIcmpCoreCtx<I>,
2844            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
2845            _device: &FakeDeviceId,
2846            _src_ip: I::RecvSrcAddr,
2847            _dst_ip: SpecifiedAddr<I::Addr>,
2848            _buffer: B,
2849            _info: &LocalDeliveryPacketInfo<I, H>,
2850            _early_demux_socket: Option<Never>,
2851        ) -> Result<(), (B, I::IcmpError)> {
2852            unimplemented!()
2853        }
2854    }
2855
2856    impl<I: IpLayerIpExt + IcmpTestIpExt> InnerIcmpContext<I, FakeIcmpBindingsCtx<I>>
2857        for FakeIcmpCoreCtx<I>
2858    {
2859        type EchoTransportContext = FakeEchoIpTransportContext;
2860
2861        fn receive_icmp_error(
2862            &mut self,
2863            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
2864            device: &Self::DeviceId,
2865            original_src_ip: Option<SpecifiedAddr<I::Addr>>,
2866            original_dst_ip: SpecifiedAddr<I::Addr>,
2867            original_proto: I::Proto,
2868            original_body: &[u8],
2869            err: I::ErrorCode,
2870        ) {
2871            CounterContext::<IcmpRxCounters<I>>::counters(self).error.increment();
2872            self.icmp.receive_icmp_error.push(err);
2873            if original_proto == I::ICMP_IP_PROTO {
2874                receive_ip_transport_icmp_error(
2875                    self,
2876                    bindings_ctx,
2877                    device,
2878                    original_src_ip,
2879                    original_dst_ip,
2880                    original_body,
2881                    err,
2882                )
2883            }
2884        }
2885    }
2886
2887    impl<I: IpLayerIpExt + IcmpTestIpExt> IcmpSendContext<I, FakeIcmpBindingsCtx<I>>
2888        for FakeIcmpCoreCtx<I>
2889    {
2890        fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<FakeInstant>) -> O>(
2891            &mut self,
2892            cb: F,
2893        ) -> O {
2894            cb(&mut self.icmp.error_send_bucket)
2895        }
2896    }
2897
2898    #[test]
2899    fn test_should_send_icmpv4_error() {
2900        let src_ip = TEST_ADDRS_V4.local_ip;
2901        let dst_ip = TEST_ADDRS_V4.remote_ip;
2902        let frame_dst = FrameDestination::Individual { local: true };
2903        let multicast_ip_1 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 1])).unwrap();
2904        let multicast_ip_2 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 2])).unwrap();
2905
2906        // Should send to unicast addresses.
2907        assert!(should_send_icmpv4_error(Some(frame_dst), src_ip, dst_ip));
2908        assert!(should_send_icmpv4_error(None, src_ip, dst_ip));
2909
2910        // Should send because loopback addresses are allowed
2911        assert!(should_send_icmpv4_error(
2912            Some(frame_dst),
2913            Ipv4::LOOPBACK_ADDRESS,
2914            Ipv4::LOOPBACK_ADDRESS,
2915        ));
2916
2917        // Should not send because destined for IP broadcast addr
2918        assert!(!should_send_icmpv4_error(
2919            Some(frame_dst),
2920            src_ip,
2921            Ipv4::LIMITED_BROADCAST_ADDRESS,
2922        ));
2923
2924        // Should not send because destined for multicast addr
2925        assert!(!should_send_icmpv4_error(Some(frame_dst), src_ip, multicast_ip_1,));
2926
2927        // Should not send because Link Layer Broadcast.
2928        assert!(!should_send_icmpv4_error(Some(FrameDestination::Broadcast), src_ip, dst_ip,));
2929
2930        // Should not send because from limited broadcast addr
2931        assert!(!should_send_icmpv4_error(
2932            Some(frame_dst),
2933            Ipv4::LIMITED_BROADCAST_ADDRESS,
2934            dst_ip,
2935        ));
2936
2937        // Should not send because from multicast addr
2938        assert!(!should_send_icmpv4_error(Some(frame_dst), multicast_ip_2, dst_ip));
2939
2940        // Should not send because from class E addr
2941        assert!(!should_send_icmpv4_error(
2942            Some(frame_dst),
2943            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
2944            dst_ip,
2945        ));
2946    }
2947
2948    #[test]
2949    fn test_should_send_icmpv6_error() {
2950        let src_ip = TEST_ADDRS_V6.local_ip;
2951        let dst_ip = TEST_ADDRS_V6.remote_ip;
2952        let frame_dst = FrameDestination::Individual { local: true };
2953        let multicast_ip_1 =
2954            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 1])).unwrap();
2955        let multicast_ip_2 =
2956            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 2])).unwrap();
2957
2958        // Should Send.
2959        assert!(should_send_icmpv6_error(
2960            Some(frame_dst),
2961            src_ip,
2962            dst_ip,
2963            false /* allow_dst_multicast */
2964        ));
2965        assert!(should_send_icmpv6_error(
2966            None, src_ip, dst_ip, false /* allow_dst_multicast */
2967        ));
2968        assert!(should_send_icmpv6_error(
2969            Some(frame_dst),
2970            src_ip,
2971            dst_ip,
2972            true /* allow_dst_multicast */
2973        ));
2974
2975        // Should send because loopback is allowed
2976        assert!(should_send_icmpv6_error(
2977            Some(frame_dst),
2978            Ipv6::LOOPBACK_ADDRESS,
2979            Ipv6::LOOPBACK_ADDRESS,
2980            false /* allow_dst_multicast */
2981        ));
2982        assert!(should_send_icmpv6_error(
2983            Some(frame_dst),
2984            Ipv6::LOOPBACK_ADDRESS,
2985            Ipv6::LOOPBACK_ADDRESS,
2986            true /* allow_dst_multicast */
2987        ));
2988
2989        // Should not send because destined for multicast addr, unless exception
2990        // applies.
2991        assert!(!should_send_icmpv6_error(
2992            Some(frame_dst),
2993            src_ip,
2994            multicast_ip_1,
2995            false /* allow_dst_multicast */
2996        ));
2997        assert!(should_send_icmpv6_error(
2998            Some(frame_dst),
2999            src_ip,
3000            multicast_ip_1,
3001            true /* allow_dst_multicast */
3002        ));
3003
3004        // Should not send because Link Layer Broadcast, unless exception
3005        // applies.
3006        assert!(!should_send_icmpv6_error(
3007            Some(FrameDestination::Broadcast),
3008            src_ip,
3009            dst_ip,
3010            false /* allow_dst_multicast */
3011        ));
3012        assert!(should_send_icmpv6_error(
3013            Some(FrameDestination::Broadcast),
3014            src_ip,
3015            dst_ip,
3016            true /* allow_dst_multicast */
3017        ));
3018
3019        // Should not send because from multicast addr.
3020        assert!(!should_send_icmpv6_error(
3021            Some(frame_dst),
3022            multicast_ip_2,
3023            dst_ip,
3024            false /* allow_dst_multicast */
3025        ));
3026        assert!(!should_send_icmpv6_error(
3027            Some(frame_dst),
3028            multicast_ip_2,
3029            dst_ip,
3030            true /* allow_dst_multicast */
3031        ));
3032
3033        // Should not send because from multicast addr, even though dest
3034        // multicast exception applies.
3035        assert!(!should_send_icmpv6_error(
3036            Some(FrameDestination::Broadcast),
3037            multicast_ip_2,
3038            dst_ip,
3039            false /* allow_dst_multicast */
3040        ));
3041        assert!(!should_send_icmpv6_error(
3042            Some(FrameDestination::Broadcast),
3043            multicast_ip_2,
3044            dst_ip,
3045            true /* allow_dst_multicast */
3046        ));
3047        assert!(!should_send_icmpv6_error(
3048            Some(frame_dst),
3049            multicast_ip_2,
3050            multicast_ip_1,
3051            false /* allow_dst_multicast */
3052        ));
3053        assert!(!should_send_icmpv6_error(
3054            Some(frame_dst),
3055            multicast_ip_2,
3056            multicast_ip_1,
3057            true /* allow_dst_multicast */
3058        ));
3059    }
3060
3061    // Tests that only require an ICMP stack. Unlike the preceding tests, these
3062    // only test the ICMP stack and state, and fake everything else. We define
3063    // the `FakeIcmpv4Ctx` and `FakeIcmpv6Ctx` types, which we wrap in a
3064    // `FakeCtx` to provide automatic implementations of a number of required
3065    // traits. The rest we implement manually.
3066
3067    #[derive(Default)]
3068    pub(super) struct FakeIcmpBindingsCtxState<I: IpExt> {
3069        _marker: core::marker::PhantomData<I>,
3070    }
3071
3072    impl InnerIcmpv4Context<FakeIcmpBindingsCtx<Ipv4>> for FakeIcmpCoreCtx<Ipv4> {
3073        fn should_send_timestamp_reply(&self) -> bool {
3074            false
3075        }
3076    }
3077    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv4>, FakeIcmpBindingsCtx<Ipv4>, Ipv4);
3078    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv6>, FakeIcmpBindingsCtx<Ipv6>, Ipv6);
3079
3080    impl<I: IcmpTestIpExt> IpSocketHandler<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3081        fn new_ip_socket<O>(
3082            &mut self,
3083            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3084            args: IpSocketArgs<'_, Self::DeviceId, I, O>,
3085        ) -> Result<IpSock<I, Self::WeakDeviceId>, IpSockCreationError>
3086        where
3087            O: RouteResolutionOptions<I>,
3088        {
3089            self.ip_socket_ctx.new_ip_socket(bindings_ctx, args)
3090        }
3091
3092        fn send_ip_packet<S, O>(
3093            &mut self,
3094            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3095            socket: &IpSock<I, Self::WeakDeviceId>,
3096            body: S,
3097            options: &O,
3098            tx_meta: FakeTxMetadata,
3099        ) -> Result<(), IpSockSendError>
3100        where
3101            S: TransportPacketSerializer<I>,
3102            S::Buffer: BufferMut,
3103            O: SendOptions<I> + RouteResolutionOptions<I>,
3104        {
3105            self.ip_socket_ctx.send_ip_packet(bindings_ctx, socket, body, options, tx_meta)
3106        }
3107
3108        fn confirm_reachable<O>(
3109            &mut self,
3110            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3111            socket: &IpSock<I, Self::WeakDeviceId>,
3112            options: &O,
3113        ) where
3114            O: RouteResolutionOptions<I>,
3115        {
3116            self.ip_socket_ctx.confirm_reachable(bindings_ctx, socket, options)
3117        }
3118    }
3119
3120    impl IpDeviceHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3121        fn is_router_device(&mut self, _device_id: &Self::DeviceId) -> bool {
3122            unimplemented!()
3123        }
3124
3125        fn set_default_hop_limit(&mut self, _device_id: &Self::DeviceId, _hop_limit: NonZeroU8) {
3126            unreachable!()
3127        }
3128
3129        fn handle_received_dad_packet(
3130            &mut self,
3131            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3132            _device_id: &Self::DeviceId,
3133            _addr: SpecifiedAddr<Ipv6Addr>,
3134            _probe_data: Option<NdpNonce<&'_ [u8]>>,
3135        ) -> Option<IpAddressState> {
3136            unimplemented!()
3137        }
3138    }
3139
3140    impl IpDeviceEgressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3141        fn with_next_packet_id<O, F: FnOnce(&()) -> O>(&self, cb: F) -> O {
3142            cb(&())
3143        }
3144
3145        fn get_local_addr_for_remote(
3146            &mut self,
3147            _device_id: &Self::DeviceId,
3148            _remote: Option<SpecifiedAddr<Ipv6Addr>>,
3149        ) -> Option<IpDeviceAddr<Ipv6Addr>> {
3150            unimplemented!()
3151        }
3152
3153        fn get_hop_limit(&mut self, _device_id: &Self::DeviceId) -> NonZeroU8 {
3154            unimplemented!()
3155        }
3156    }
3157
3158    impl IpDeviceIngressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3159        fn address_status_for_device(
3160            &mut self,
3161            _addr: SpecifiedAddr<Ipv6Addr>,
3162            _device_id: &Self::DeviceId,
3163        ) -> AddressStatus<Ipv6PresentAddressStatus> {
3164            unimplemented!()
3165        }
3166    }
3167
3168    impl Ipv6DeviceHandler<FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3169        type LinkLayerAddr = Uninstantiable;
3170
3171        fn get_link_layer_addr(&mut self, _device_id: &Self::DeviceId) -> Option<Uninstantiable> {
3172            unimplemented!()
3173        }
3174
3175        fn set_discovered_retrans_timer(
3176            &mut self,
3177            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3178            _device_id: &Self::DeviceId,
3179            _retrans_timer: NonZeroDuration,
3180        ) {
3181            unimplemented!()
3182        }
3183
3184        fn set_link_mtu(&mut self, _device_id: &Self::DeviceId, _mtu: Mtu) {
3185            unimplemented!()
3186        }
3187
3188        fn update_discovered_ipv6_route(
3189            &mut self,
3190            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3191            _device_id: &Self::DeviceId,
3192            _route: Ipv6DiscoveredRoute,
3193            _properties: Ipv6DiscoveredRouteProperties,
3194            _lifetime: Option<NonZeroNdpLifetime>,
3195        ) {
3196            unimplemented!()
3197        }
3198
3199        fn apply_slaac_update(
3200            &mut self,
3201            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3202            _device_id: &Self::DeviceId,
3203            _subnet: Subnet<Ipv6Addr>,
3204            _preferred_lifetime: Option<NonZeroNdpLifetime>,
3205            _valid_lifetime: Option<NonZeroNdpLifetime>,
3206        ) {
3207            unimplemented!()
3208        }
3209
3210        fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
3211            &mut self,
3212            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3213            _device: &FakeDeviceId,
3214            _src_ip: Ipv6SourceAddr,
3215            _dst_ip: SpecifiedAddr<Ipv6Addr>,
3216            _packet: MldPacket<B>,
3217            _header_info: &H,
3218        ) {
3219            unimplemented!()
3220        }
3221    }
3222
3223    impl IpLayerHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3224        fn send_ip_packet_from_device<S>(
3225            &mut self,
3226            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3227            _meta: SendIpPacketMeta<Ipv6, &Self::DeviceId, Option<SpecifiedAddr<Ipv6Addr>>>,
3228            _body: S,
3229        ) -> Result<(), IpSendFrameError<S>> {
3230            unimplemented!()
3231        }
3232
3233        fn send_ip_frame<S>(
3234            &mut self,
3235            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3236            _device: &Self::DeviceId,
3237            _destination: IpPacketDestination<Ipv6, &Self::DeviceId>,
3238            _body: S,
3239        ) -> Result<(), IpSendFrameError<S>>
3240        where
3241            S: Serializer,
3242            S::Buffer: BufferMut,
3243        {
3244            unimplemented!()
3245        }
3246    }
3247
3248    impl NudIpHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3249        fn handle_neighbor_probe(
3250            &mut self,
3251            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3252            _device_id: &Self::DeviceId,
3253            _neighbor: SpecifiedAddr<Ipv6Addr>,
3254            _link_addr: &[u8],
3255        ) {
3256            unimplemented!()
3257        }
3258
3259        fn handle_neighbor_confirmation(
3260            &mut self,
3261            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3262            _device_id: &Self::DeviceId,
3263            _neighbor: SpecifiedAddr<Ipv6Addr>,
3264            _link_addr: Option<&[u8]>,
3265            _flags: ConfirmationFlags,
3266        ) {
3267            unimplemented!()
3268        }
3269
3270        fn flush_neighbor_table(
3271            &mut self,
3272            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3273            _device_id: &Self::DeviceId,
3274        ) {
3275            unimplemented!()
3276        }
3277    }
3278
3279    #[test]
3280    fn test_receive_icmpv4_error() {
3281        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
3282        // the value 0) and, b) different from each other.
3283        const ICMP_ID: u16 = 0x0F;
3284        const SEQ_NUM: u16 = 0xF0;
3285
3286        /// Test receiving an ICMP error message.
3287        ///
3288        /// Test that receiving an ICMP error message with the given code and
3289        /// message contents, and containing the given original IPv4 packet,
3290        /// results in the counter values in `assert_counters`. After that
3291        /// assertion passes, `f` is called on the context so that the caller
3292        /// can perform whatever extra validation they want.
3293        ///
3294        /// The error message will be sent from `TEST_ADDRS_V4.remote_ip` to
3295        /// `TEST_ADDRS_V4.local_ip`. Before the message is sent, an ICMP
3296        /// socket will be established with the ID `ICMP_ID`, and
3297        /// `test_receive_icmpv4_error_helper` will assert that its `SocketId`
3298        /// is 0. This allows the caller to craft the `original_packet` so that
3299        /// it should be delivered to this socket.
3300        fn test_receive_icmpv4_error_helper<
3301            C: Debug,
3302            M: IcmpMessage<Ipv4, Code = C> + Debug,
3303            F: Fn(&FakeIcmpCtx<Ipv4>),
3304        >(
3305            original_packet: &mut [u8],
3306            code: C,
3307            msg: M,
3308            f: F,
3309        ) {
3310            set_logger_for_test();
3311
3312            let mut ctx: FakeIcmpCtx<Ipv4> = FakeIcmpCtx::default();
3313
3314            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
3315            <IcmpIpTransportContext as IpTransportContext<Ipv4, _, _>>::receive_ip_packet(
3316                core_ctx,
3317                bindings_ctx,
3318                &FakeDeviceId,
3319                Ipv4SourceAddr::new(*TEST_ADDRS_V4.remote_ip).unwrap(),
3320                TEST_ADDRS_V4.local_ip,
3321                IcmpPacketBuilder::new(TEST_ADDRS_V4.remote_ip, TEST_ADDRS_V4.local_ip, code, msg)
3322                    .wrap_body(Buf::new(original_packet, ..))
3323                    .serialize_vec_outer()
3324                    .unwrap(),
3325                &LocalDeliveryPacketInfo::default(),
3326                None,
3327            )
3328            .unwrap();
3329            f(&ctx);
3330        }
3331        // Test that, when we receive various ICMPv4 error messages, we properly
3332        // pass them up to the IP layer and, sometimes, to the transport layer.
3333
3334        // First, test with an original packet containing an ICMP message. Since
3335        // this test fake supports ICMP sockets, this error can be delivered all
3336        // the way up the stack.
3337
3338        // A buffer containing an ICMP echo request with ID `ICMP_ID` and
3339        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
3340        // ICMP error message which contains this as its original packet should
3341        // be delivered to the socket created in
3342        // `test_receive_icmpv4_error_helper`.
3343        let mut buffer = EmptyBuf
3344            .wrap_in(IcmpPacketBuilder::<Ipv4, _>::new(
3345                TEST_ADDRS_V4.local_ip,
3346                TEST_ADDRS_V4.remote_ip,
3347                IcmpZeroCode,
3348                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
3349            ))
3350            .wrap_in(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3351                TEST_ADDRS_V4.local_ip,
3352                TEST_ADDRS_V4.remote_ip,
3353                64,
3354                Ipv4Proto::Icmp,
3355            ))
3356            .serialize_vec_outer()
3357            .unwrap();
3358
3359        test_receive_icmpv4_error_helper(
3360            buffer.as_mut(),
3361            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3362            IcmpDestUnreachable::default(),
3363            |CtxPair { core_ctx, bindings_ctx: _ }| {
3364                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3365                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3366                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3367                assert_eq!(
3368                    core_ctx.icmp.rx_counters.dest_unreachable.dest_network_unreachable.get(),
3369                    1
3370                );
3371                let err = Icmpv4ErrorCode::DestUnreachable(
3372                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3373                    IcmpDestUnreachable::default(),
3374                );
3375                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3376            },
3377        );
3378
3379        test_receive_icmpv4_error_helper(
3380            buffer.as_mut(),
3381            Icmpv4TimeExceededCode::TtlExpired,
3382            IcmpTimeExceeded::default(),
3383            |CtxPair { core_ctx, bindings_ctx: _ }| {
3384                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3385                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3386                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3387                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.ttl_expired.get(), 1);
3388                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3389                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3390            },
3391        );
3392
3393        test_receive_icmpv4_error_helper(
3394            buffer.as_mut(),
3395            Icmpv4ParameterProblemCode::PointerIndicatesError,
3396            Icmpv4ParameterProblem::new(0),
3397            |CtxPair { core_ctx, bindings_ctx: _ }| {
3398                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3399                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3400                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3401                assert_eq!(
3402                    core_ctx.icmp.rx_counters.parameter_problem.pointer_indicates_error.get(),
3403                    1
3404                );
3405                let err = Icmpv4ErrorCode::ParameterProblem(
3406                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3407                );
3408                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3409            },
3410        );
3411
3412        // Second, test with an original packet containing a malformed ICMP
3413        // packet (we accomplish this by leaving the IP packet's body empty). We
3414        // should process this packet in
3415        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
3416        // further - in particular, we should not dispatch to the Echo sockets.
3417
3418        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3419            TEST_ADDRS_V4.local_ip,
3420            TEST_ADDRS_V4.remote_ip,
3421            64,
3422            Ipv4Proto::Icmp,
3423        )
3424        .wrap_body(EmptyBuf)
3425        .serialize_vec_outer()
3426        .unwrap();
3427
3428        test_receive_icmpv4_error_helper(
3429            buffer.as_mut(),
3430            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3431            IcmpDestUnreachable::default(),
3432            |CtxPair { core_ctx, bindings_ctx: _ }| {
3433                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3434                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3435                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3436                assert_eq!(
3437                    core_ctx.icmp.rx_counters.dest_unreachable.dest_network_unreachable.get(),
3438                    1
3439                );
3440                let err = Icmpv4ErrorCode::DestUnreachable(
3441                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3442                    IcmpDestUnreachable::default(),
3443                );
3444                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3445            },
3446        );
3447
3448        test_receive_icmpv4_error_helper(
3449            buffer.as_mut(),
3450            Icmpv4TimeExceededCode::TtlExpired,
3451            IcmpTimeExceeded::default(),
3452            |CtxPair { core_ctx, bindings_ctx: _ }| {
3453                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3454                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3455                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3456                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.ttl_expired.get(), 1);
3457                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3458                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3459            },
3460        );
3461
3462        test_receive_icmpv4_error_helper(
3463            buffer.as_mut(),
3464            Icmpv4ParameterProblemCode::PointerIndicatesError,
3465            Icmpv4ParameterProblem::new(0),
3466            |CtxPair { core_ctx, bindings_ctx: _ }| {
3467                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3468                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3469                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3470                assert_eq!(
3471                    core_ctx.icmp.rx_counters.parameter_problem.pointer_indicates_error.get(),
3472                    1
3473                );
3474                let err = Icmpv4ErrorCode::ParameterProblem(
3475                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3476                );
3477                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3478            },
3479        );
3480
3481        // Third, test with an original packet containing a UDP packet. This
3482        // allows us to verify that protocol numbers are handled properly by
3483        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
3484        // called.
3485
3486        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3487            TEST_ADDRS_V4.local_ip,
3488            TEST_ADDRS_V4.remote_ip,
3489            64,
3490            IpProto::Udp.into(),
3491        )
3492        .wrap_body(EmptyBuf)
3493        .serialize_vec_outer()
3494        .unwrap();
3495
3496        test_receive_icmpv4_error_helper(
3497            buffer.as_mut(),
3498            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3499            IcmpDestUnreachable::default(),
3500            |CtxPair { core_ctx, bindings_ctx: _ }| {
3501                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3502                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3503                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3504                assert_eq!(
3505                    core_ctx.icmp.rx_counters.dest_unreachable.dest_network_unreachable.get(),
3506                    1
3507                );
3508                let err = Icmpv4ErrorCode::DestUnreachable(
3509                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3510                    IcmpDestUnreachable::default(),
3511                );
3512                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3513            },
3514        );
3515
3516        test_receive_icmpv4_error_helper(
3517            buffer.as_mut(),
3518            Icmpv4TimeExceededCode::TtlExpired,
3519            IcmpTimeExceeded::default(),
3520            |CtxPair { core_ctx, bindings_ctx: _ }| {
3521                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3522                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3523                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3524                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.ttl_expired.get(), 1);
3525                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3526                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3527            },
3528        );
3529
3530        test_receive_icmpv4_error_helper(
3531            buffer.as_mut(),
3532            Icmpv4ParameterProblemCode::PointerIndicatesError,
3533            Icmpv4ParameterProblem::new(0),
3534            |CtxPair { core_ctx, bindings_ctx: _ }| {
3535                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3536                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3537                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3538                assert_eq!(
3539                    core_ctx.icmp.rx_counters.parameter_problem.pointer_indicates_error.get(),
3540                    1
3541                );
3542                let err = Icmpv4ErrorCode::ParameterProblem(
3543                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3544                );
3545                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3546            },
3547        );
3548    }
3549
3550    #[test]
3551    fn test_receive_icmpv6_error() {
3552        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
3553        // the value 0) and, b) different from each other.
3554        const ICMP_ID: u16 = 0x0F;
3555        const SEQ_NUM: u16 = 0xF0;
3556
3557        /// Test receiving an ICMPv6 error message.
3558        ///
3559        /// Test that receiving an ICMP error message with the given code and
3560        /// message contents, and containing the given original IPv4 packet,
3561        /// results in the counter values in `assert_counters`. After that
3562        /// assertion passes, `f` is called on the context so that the caller
3563        /// can perform whatever extra validation they want.
3564        ///
3565        /// The error message will be sent from `TEST_ADDRS_V6.remote_ip` to
3566        /// `TEST_ADDRS_V6.local_ip`. Before the message is sent, an ICMP
3567        /// socket will be established with the ID `ICMP_ID`, and
3568        /// `test_receive_icmpv6_error_helper` will assert that its `SocketId`
3569        /// is 0. This allows the caller to craft the `original_packet` so that
3570        /// it should be delivered to this socket.
3571        fn test_receive_icmpv6_error_helper<
3572            C: Debug,
3573            M: IcmpMessage<Ipv6, Code = C> + Debug,
3574            F: Fn(&FakeIcmpCtx<Ipv6>),
3575        >(
3576            original_packet: &mut [u8],
3577            code: C,
3578            msg: M,
3579            f: F,
3580        ) {
3581            set_logger_for_test();
3582
3583            let mut ctx = FakeIcmpCtx::<Ipv6>::default();
3584            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
3585            <IcmpIpTransportContext as IpTransportContext<Ipv6, _, _>>::receive_ip_packet(
3586                core_ctx,
3587                bindings_ctx,
3588                &FakeDeviceId,
3589                TEST_ADDRS_V6.remote_ip.get().try_into().unwrap(),
3590                TEST_ADDRS_V6.local_ip,
3591                IcmpPacketBuilder::new(TEST_ADDRS_V6.remote_ip, TEST_ADDRS_V6.local_ip, code, msg)
3592                    .wrap_body(Buf::new(original_packet, ..))
3593                    .serialize_vec_outer()
3594                    .unwrap(),
3595                &LocalDeliveryPacketInfo::default(),
3596                None,
3597            )
3598            .unwrap();
3599            f(&ctx);
3600        }
3601        // Test that, when we receive various ICMPv6 error messages, we properly
3602        // pass them up to the IP layer and, sometimes, to the transport layer.
3603
3604        // First, test with an original packet containing an ICMPv6 message.
3605        // Since this test fake supports ICMPv6 sockets, this error can be
3606        // delivered all the way up the stack.
3607
3608        // A buffer containing an ICMPv6 echo request with ID `ICMP_ID` and
3609        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
3610        // ICMPv6 error message which contains this as its original packet
3611        // should be delivered to the socket created in
3612        // `test_receive_icmpv6_error_helper`.
3613        let mut buffer = EmptyBuf
3614            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
3615                TEST_ADDRS_V6.local_ip,
3616                TEST_ADDRS_V6.remote_ip,
3617                IcmpZeroCode,
3618                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
3619            ))
3620            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
3621                TEST_ADDRS_V6.local_ip,
3622                TEST_ADDRS_V6.remote_ip,
3623                64,
3624                Ipv6Proto::Icmpv6,
3625            ))
3626            .serialize_vec_outer()
3627            .unwrap();
3628
3629        test_receive_icmpv6_error_helper(
3630            buffer.as_mut(),
3631            Icmpv6DestUnreachableCode::NoRoute,
3632            IcmpDestUnreachable::default(),
3633            |CtxPair { core_ctx, bindings_ctx: _ }| {
3634                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3635                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3636                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3637                assert_eq!(core_ctx.icmp.rx_counters.dest_unreachable.no_route.get(), 1);
3638                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
3639                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3640            },
3641        );
3642
3643        test_receive_icmpv6_error_helper(
3644            buffer.as_mut(),
3645            Icmpv6TimeExceededCode::HopLimitExceeded,
3646            IcmpTimeExceeded::default(),
3647            |CtxPair { core_ctx, bindings_ctx: _ }| {
3648                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3649                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3650                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3651                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.hop_limit_exceeded.get(), 1);
3652                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
3653                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3654            },
3655        );
3656
3657        test_receive_icmpv6_error_helper(
3658            buffer.as_mut(),
3659            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3660            Icmpv6ParameterProblem::new(0),
3661            |CtxPair { core_ctx, bindings_ctx: _ }| {
3662                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3663                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3664                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3665                assert_eq!(
3666                    core_ctx.icmp.rx_counters.parameter_problem.unrecognized_next_header_type.get(),
3667                    1
3668                );
3669                let err = Icmpv6ErrorCode::ParameterProblem(
3670                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3671                );
3672                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3673            },
3674        );
3675
3676        // Second, test with an original packet containing a malformed ICMPv6
3677        // packet (we accomplish this by leaving the IP packet's body empty). We
3678        // should process this packet in
3679        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
3680        // further - in particular, we should not call into Echo sockets.
3681
3682        let mut buffer = EmptyBuf
3683            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
3684                TEST_ADDRS_V6.local_ip,
3685                TEST_ADDRS_V6.remote_ip,
3686                64,
3687                Ipv6Proto::Icmpv6,
3688            ))
3689            .serialize_vec_outer()
3690            .unwrap();
3691
3692        test_receive_icmpv6_error_helper(
3693            buffer.as_mut(),
3694            Icmpv6DestUnreachableCode::NoRoute,
3695            IcmpDestUnreachable::default(),
3696            |CtxPair { core_ctx, bindings_ctx: _ }| {
3697                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3698                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3699                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3700                assert_eq!(core_ctx.icmp.rx_counters.dest_unreachable.no_route.get(), 1);
3701                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
3702                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3703            },
3704        );
3705
3706        test_receive_icmpv6_error_helper(
3707            buffer.as_mut(),
3708            Icmpv6TimeExceededCode::HopLimitExceeded,
3709            IcmpTimeExceeded::default(),
3710            |CtxPair { core_ctx, bindings_ctx: _ }| {
3711                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3712                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3713                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3714                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.hop_limit_exceeded.get(), 1);
3715                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
3716                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3717            },
3718        );
3719
3720        test_receive_icmpv6_error_helper(
3721            buffer.as_mut(),
3722            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3723            Icmpv6ParameterProblem::new(0),
3724            |CtxPair { core_ctx, bindings_ctx: _ }| {
3725                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3726                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3727                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3728                assert_eq!(
3729                    core_ctx.icmp.rx_counters.parameter_problem.unrecognized_next_header_type.get(),
3730                    1
3731                );
3732                let err = Icmpv6ErrorCode::ParameterProblem(
3733                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3734                );
3735                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3736            },
3737        );
3738
3739        // Third, test with an original packet containing a UDP packet. This
3740        // allows us to verify that protocol numbers are handled properly by
3741        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
3742        // called.
3743
3744        let mut buffer = <Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
3745            TEST_ADDRS_V6.local_ip,
3746            TEST_ADDRS_V6.remote_ip,
3747            64,
3748            IpProto::Udp.into(),
3749        )
3750        .wrap_body(EmptyBuf)
3751        .serialize_vec_outer()
3752        .unwrap();
3753
3754        test_receive_icmpv6_error_helper(
3755            buffer.as_mut(),
3756            Icmpv6DestUnreachableCode::NoRoute,
3757            IcmpDestUnreachable::default(),
3758            |CtxPair { core_ctx, bindings_ctx: _ }| {
3759                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3760                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3761                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3762                assert_eq!(core_ctx.icmp.rx_counters.dest_unreachable.no_route.get(), 1);
3763                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
3764                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3765            },
3766        );
3767
3768        test_receive_icmpv6_error_helper(
3769            buffer.as_mut(),
3770            Icmpv6TimeExceededCode::HopLimitExceeded,
3771            IcmpTimeExceeded::default(),
3772            |CtxPair { core_ctx, bindings_ctx: _ }| {
3773                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3774                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3775                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3776                assert_eq!(core_ctx.icmp.rx_counters.time_exceeded.hop_limit_exceeded.get(), 1);
3777                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
3778                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3779            },
3780        );
3781
3782        test_receive_icmpv6_error_helper(
3783            buffer.as_mut(),
3784            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3785            Icmpv6ParameterProblem::new(0),
3786            |CtxPair { core_ctx, bindings_ctx: _ }| {
3787                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3788                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3789                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3790                assert_eq!(
3791                    core_ctx.icmp.rx_counters.parameter_problem.unrecognized_next_header_type.get(),
3792                    1
3793                );
3794                let err = Icmpv6ErrorCode::ParameterProblem(
3795                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
3796                );
3797                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3798            },
3799        );
3800    }
3801
3802    #[test]
3803    fn test_error_rate_limit() {
3804        set_logger_for_test();
3805
3806        /// Call `send_icmpv4_ttl_expired` with fake values.
3807        fn send_icmpv4_ttl_expired_helper(
3808            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
3809        ) {
3810            let error = Icmpv4Error::TtlExpired;
3811            core_ctx.send_icmp_error_message(
3812                bindings_ctx,
3813                Some(&FakeDeviceId),
3814                Some(FrameDestination::Individual { local: true }),
3815                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
3816                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
3817                EmptyBuf,
3818                error,
3819                0,
3820                packet_formats::ip::IpProto::Udp.into(),
3821                &Default::default(),
3822            );
3823            assert_geq!(core_ctx.icmp.tx_counters.time_exceeded.ttl_expired.get(), 1);
3824        }
3825
3826        /// Call `send_icmpv4_parameter_problem` with fake values.
3827        fn send_icmpv4_parameter_problem_helper(
3828            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
3829        ) {
3830            let error = Icmpv4Error::ParameterProblem {
3831                code: Icmpv4ParameterProblemCode::PointerIndicatesError,
3832                pointer: 0,
3833            };
3834            core_ctx.send_icmp_error_message(
3835                bindings_ctx,
3836                Some(&FakeDeviceId),
3837                Some(FrameDestination::Individual { local: true }),
3838                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
3839                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
3840                EmptyBuf,
3841                error,
3842                0,
3843                packet_formats::ip::IpProto::Udp.into(),
3844                &Default::default(),
3845            );
3846            assert_geq!(
3847                core_ctx.icmp.tx_counters.parameter_problem.pointer_indicates_error.get(),
3848                1
3849            );
3850        }
3851
3852        /// Call `send_icmpv4_dest_unreachable` with fake values.
3853        fn send_icmpv4_dest_unreachable_helper(
3854            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
3855        ) {
3856            core_ctx.send_icmp_error_message(
3857                bindings_ctx,
3858                Some(&FakeDeviceId),
3859                Some(FrameDestination::Individual { local: true }),
3860                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
3861                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
3862                EmptyBuf,
3863                Icmpv4Error::NetUnreachable,
3864                0,
3865                packet_formats::ip::IpProto::Udp.into(),
3866                &Default::default(),
3867            );
3868            assert_geq!(
3869                core_ctx.icmp.tx_counters.dest_unreachable.dest_network_unreachable.get(),
3870                1
3871            );
3872        }
3873
3874        /// Call `send_icmpv6_ttl_expired` with fake values.
3875        fn send_icmpv6_ttl_expired_helper(
3876            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
3877        ) {
3878            core_ctx.send_icmp_error_message(
3879                bindings_ctx,
3880                Some(&FakeDeviceId),
3881                Some(FrameDestination::Individual { local: true }),
3882                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
3883                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
3884                EmptyBuf,
3885                Icmpv6Error::TtlExpired,
3886                0,
3887                Ipv6Proto::NoNextHeader,
3888                &Default::default(),
3889            );
3890            assert_geq!(core_ctx.icmp.tx_counters.time_exceeded.hop_limit_exceeded.get(), 1);
3891        }
3892
3893        /// Call `send_icmpv6_packet_too_big` with fake values.
3894        fn send_icmpv6_packet_too_big_helper(
3895            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
3896        ) {
3897            core_ctx.send_icmp_error_message(
3898                bindings_ctx,
3899                Some(&FakeDeviceId),
3900                Some(FrameDestination::Individual { local: true }),
3901                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
3902                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
3903                EmptyBuf,
3904                Icmpv6Error::PacketTooBig { mtu: Mtu::new(1280) },
3905                0,
3906                Ipv6Proto::NoNextHeader,
3907                &Default::default(),
3908            );
3909            assert_geq!(core_ctx.icmp.tx_counters.packet_too_big.get(), 1);
3910        }
3911
3912        /// Call `send_icmpv6_parameter_problem` with fake values.
3913        fn send_icmpv6_parameter_problem_helper(
3914            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
3915        ) {
3916            let error = Icmpv6Error::ParameterProblem {
3917                code: Icmpv6ParameterProblemCode::ErroneousHeaderField,
3918                pointer: 0,
3919                allow_dst_multicast: false,
3920            };
3921            core_ctx.send_icmp_error_message(
3922                bindings_ctx,
3923                Some(&FakeDeviceId),
3924                Some(FrameDestination::Individual { local: true }),
3925                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
3926                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
3927                EmptyBuf,
3928                error,
3929                0,
3930                Ipv6Proto::NoNextHeader,
3931                &Default::default(),
3932            );
3933            assert_geq!(
3934                core_ctx.icmp.tx_counters.parameter_problem.erroneous_header_field.get(),
3935                1
3936            );
3937        }
3938
3939        /// Call `send_icmpv6_dest_unreachable` with fake values.
3940        fn send_icmpv6_dest_unreachable_helper(
3941            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
3942        ) {
3943            core_ctx.send_icmp_error_message(
3944                bindings_ctx,
3945                Some(&FakeDeviceId),
3946                Some(FrameDestination::Individual { local: true }),
3947                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
3948                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
3949                EmptyBuf,
3950                Icmpv6Error::NetUnreachable,
3951                0,
3952                Ipv6Proto::NoNextHeader,
3953                &Default::default(),
3954            );
3955            assert_geq!(core_ctx.icmp.tx_counters.dest_unreachable.no_route.get(), 1);
3956        }
3957
3958        // Run tests for each function that sends error messages to make sure
3959        // they're all properly rate limited.
3960
3961        fn run_test<I: IcmpTestIpExt, W: Fn(u64) -> FakeIcmpCtx<I>, S: Fn(&mut FakeIcmpCtx<I>)>(
3962            with_errors_per_second: W,
3963            send: S,
3964        ) {
3965            // Note that we could theoretically have more precise tests here
3966            // (e.g., a test that we send at the correct rate over the long
3967            // term), but those would amount to testing the `TokenBucket`
3968            // implementation, which has its own exhaustive tests. Instead, we
3969            // just have a few sanity checks to make sure that we're actually
3970            // invoking it when we expect to (as opposed to bypassing it
3971            // entirely or something).
3972
3973            // Test that, if no time has elapsed, we can successfully send up to
3974            // `ERRORS_PER_SECOND` error messages, but no more.
3975
3976            // Don't use `DEFAULT_ERRORS_PER_SECOND` because it's 2^16 and it
3977            // makes this test take a long time.
3978            const ERRORS_PER_SECOND: u64 = 64;
3979
3980            let mut ctx = with_errors_per_second(ERRORS_PER_SECOND);
3981
3982            for i in 0..ERRORS_PER_SECOND {
3983                send(&mut ctx);
3984                assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), i + 1);
3985            }
3986
3987            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
3988            send(&mut ctx);
3989            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
3990
3991            // Test that, if we set a rate of 0, we are not able to send any
3992            // error messages regardless of how much time has elapsed.
3993
3994            let mut ctx = with_errors_per_second(0);
3995            send(&mut ctx);
3996            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
3997            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
3998            send(&mut ctx);
3999            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4000            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4001            send(&mut ctx);
4002            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4003        }
4004
4005        fn with_errors_per_second_v4(errors_per_second: u64) -> FakeIcmpCtx<Ipv4> {
4006            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4007        }
4008        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_ttl_expired_helper);
4009        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_parameter_problem_helper);
4010        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_dest_unreachable_helper);
4011
4012        fn with_errors_per_second_v6(errors_per_second: u64) -> FakeIcmpCtx<Ipv6> {
4013            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4014        }
4015
4016        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_ttl_expired_helper);
4017        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_packet_too_big_helper);
4018        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_parameter_problem_helper);
4019        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_dest_unreachable_helper);
4020    }
4021}