Skip to main content

netstack3_ip/
icmp.rs

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