Skip to main content

netstack3_ip/
icmp.rs

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