Skip to main content

netstack3_ip/
icmp.rs

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