Skip to main content

netstack3_ip/
icmp.rs

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