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