Skip to main content

netstack3_icmp_echo/
socket.rs

1// Copyright 2024 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//! ICMP Echo Sockets.
6
7use alloc::vec::Vec;
8use core::borrow::Borrow;
9use core::fmt::Debug;
10use core::marker::PhantomData;
11use core::num::{NonZeroU8, NonZeroU16};
12use core::ops::ControlFlow;
13use lock_order::lock::{DelegatedOrderedLockAccess, OrderedLockAccess, OrderedLockRef};
14
15use derivative::Derivative;
16use either::Either;
17use log::{debug, trace};
18use net_types::ip::{GenericOverIp, Ip, IpVersionMarker};
19use net_types::{SpecifiedAddr, Witness as _, ZonedAddr};
20use netstack3_base::socket::{
21    self, AddrIsMappedError, AddrVec, AddrVecIter, ConnAddr, ConnInfoAddr, ConnIpAddr,
22    IncompatibleError, InsertError, ListenerAddrInfo, MaybeDualStack, ShutdownType, SocketCookie,
23    SocketIpAddr, SocketMapAddrSpec, SocketMapAddrStateSpec, SocketMapConflictPolicy,
24    SocketMapStateSpec,
25};
26use netstack3_base::socketmap::{IterShadows as _, SocketMap};
27use netstack3_base::sync::{BindingsSyncTypes, RwLock, StrongRc};
28use netstack3_base::{
29    AnyDevice, ContextPair, CoreTxMetadataContext, CounterContext, DeviceIdContext, IcmpIpExt,
30    Inspector, InspectorDeviceExt, LocalAddressError, Mark, MarkDomain, Marks, PortAllocImpl,
31    ReferenceNotifiers, RemoveResourceResultWithContext, RngContext, SocketError,
32    StrongDeviceIdentifier, UninstantiableWrapper, WeakDeviceIdentifier,
33};
34use netstack3_datagram::{
35    self as datagram, DatagramApi, DatagramBindingsTypes, DatagramFlowId, DatagramSocketMapSpec,
36    DatagramSocketSet, DatagramSocketSpec, DatagramSpecBoundStateContext, DatagramSpecStateContext,
37    DatagramStateContext, ExpectedUnboundError, NonDualStackConverter,
38    NonDualStackDatagramSpecBoundStateContext,
39};
40use netstack3_ip::icmp::{EchoTransportContextMarker, IcmpRxCounters};
41use netstack3_ip::socket::SocketHopLimits;
42use netstack3_ip::{
43    IpHeaderInfo, IpLayerIpExt, IpTransportContext, LocalDeliveryPacketInfo,
44    MulticastMembershipHandler, ReceiveIpPacketMeta, SocketMetadata, TransportIpContext,
45};
46use packet::{BufferMut, NestablePacketBuilder as _, ParsablePacket as _, ParseBuffer};
47use packet_formats::icmp::{IcmpEchoReply, IcmpEchoRequest, IcmpPacketBuilder, IcmpPacketRaw};
48use packet_formats::ip::{IpProtoExt, Ipv4Proto, Ipv6Proto};
49
50/// A marker trait for all IP extensions required by ICMP sockets.
51pub trait IpExt: datagram::IpExt + IcmpIpExt + IpLayerIpExt {}
52impl<O: datagram::IpExt + IcmpIpExt + IpLayerIpExt> IpExt for O {}
53
54/// Holds the stack's ICMP echo sockets.
55#[derive(Derivative, GenericOverIp)]
56#[derivative(Default(bound = ""))]
57#[generic_over_ip(I, Ip)]
58pub struct IcmpSockets<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> {
59    bound_and_id_allocator: RwLock<BoundSockets<I, D, BT>, BT::RwLock>,
60    // Destroy all_sockets last so the strong references in the demux are
61    // dropped before the primary references in the set.
62    all_sockets: RwLock<IcmpSocketSet<I, D, BT>, BT::RwLock>,
63}
64
65impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
66    OrderedLockAccess<BoundSockets<I, D, BT>> for IcmpSockets<I, D, BT>
67{
68    type Lock = RwLock<BoundSockets<I, D, BT>, BT::RwLock>;
69    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
70        OrderedLockRef::new(&self.bound_and_id_allocator)
71    }
72}
73
74impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
75    OrderedLockAccess<IcmpSocketSet<I, D, BT>> for IcmpSockets<I, D, BT>
76{
77    type Lock = RwLock<IcmpSocketSet<I, D, BT>, BT::RwLock>;
78    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
79        OrderedLockRef::new(&self.all_sockets)
80    }
81}
82
83/// An ICMP socket.
84#[derive(GenericOverIp, Derivative)]
85#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
86#[generic_over_ip(I, Ip)]
87pub struct IcmpSocketId<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
88    datagram::StrongRc<I, D, Icmp<BT>>,
89);
90
91impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
92    /// Returns a `SocketCookie` for this socket.
93    pub fn socket_cookie(&self) -> SocketCookie {
94        let Self(inner) = self;
95        SocketCookie::new(inner.resource_token())
96    }
97
98    /// Returns a `SocketInfo` for this socket.
99    pub fn socket_info(&self) -> netstack3_base::socket::SocketInfo {
100        netstack3_base::socket::SocketInfo {
101            proto: I::map_ip(
102                (),
103                |()| netstack3_base::socket::EitherIpProto::V4(Ipv4Proto::Icmp),
104                |()| netstack3_base::socket::EitherIpProto::V6(Ipv6Proto::Icmpv6),
105            ),
106            cookie: self.socket_cookie(),
107        }
108    }
109}
110
111impl<CC, I, BT> SocketMetadata<CC> for IcmpSocketId<I, CC::WeakDeviceId, BT>
112where
113    CC: IcmpEchoStateContext<I, BT>,
114    I: IpExt,
115    BT: IcmpEchoBindingsTypes,
116{
117    fn socket_info(&self, _core_ctx: &mut CC) -> netstack3_base::socket::SocketInfo {
118        self.socket_info()
119    }
120
121    fn marks(&self, core_ctx: &mut CC) -> Marks {
122        core_ctx.with_socket_state(self, |_core_ctx, state| state.options().marks().clone())
123    }
124}
125
126impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Clone
127    for IcmpSocketId<I, D, BT>
128{
129    #[cfg_attr(feature = "instrumented", track_caller)]
130    fn clone(&self) -> Self {
131        let Self(rc) = self;
132        Self(StrongRc::clone(rc))
133    }
134}
135
136impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
137    From<datagram::StrongRc<I, D, Icmp<BT>>> for IcmpSocketId<I, D, BT>
138{
139    fn from(value: datagram::StrongRc<I, D, Icmp<BT>>) -> Self {
140        Self(value)
141    }
142}
143
144impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
145    Borrow<datagram::StrongRc<I, D, Icmp<BT>>> for IcmpSocketId<I, D, BT>
146{
147    fn borrow(&self) -> &datagram::StrongRc<I, D, Icmp<BT>> {
148        let Self(rc) = self;
149        rc
150    }
151}
152
153impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
154    PartialEq<WeakIcmpSocketId<I, D, BT>> for IcmpSocketId<I, D, BT>
155{
156    fn eq(&self, other: &WeakIcmpSocketId<I, D, BT>) -> bool {
157        let Self(rc) = self;
158        let WeakIcmpSocketId(weak) = other;
159        StrongRc::weak_ptr_eq(rc, weak)
160    }
161}
162
163impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Debug
164    for IcmpSocketId<I, D, BT>
165{
166    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167        let Self(rc) = self;
168        f.debug_tuple("IcmpSocketId").field(&StrongRc::debug_id(rc)).finish()
169    }
170}
171
172impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
173    /// Returns the inner state for this socket, to be used in conjunction with
174    /// lock ordering mechanisms.
175    #[cfg(any(test, feature = "testutils"))]
176    pub fn state(&self) -> &RwLock<IcmpSocketState<I, D, BT>, BT::RwLock> {
177        let Self(rc) = self;
178        rc.state()
179    }
180
181    /// Returns a means to debug outstanding references to this socket.
182    pub fn debug_references(&self) -> impl Debug {
183        let Self(rc) = self;
184        StrongRc::debug_references(rc)
185    }
186
187    /// Downgrades this ID to a weak reference.
188    pub fn downgrade(&self) -> WeakIcmpSocketId<I, D, BT> {
189        let Self(rc) = self;
190        WeakIcmpSocketId(StrongRc::downgrade(rc))
191    }
192
193    /// Returns external data associated with this socket.
194    pub fn external_data(&self) -> &BT::ExternalData<I> {
195        let Self(rc) = self;
196        rc.external_data()
197    }
198}
199
200impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
201    DelegatedOrderedLockAccess<IcmpSocketState<I, D, BT>> for IcmpSocketId<I, D, BT>
202{
203    type Inner = datagram::ReferenceState<I, D, Icmp<BT>>;
204    fn delegate_ordered_lock_access(&self) -> &Self::Inner {
205        let Self(rc) = self;
206        &*rc
207    }
208}
209
210/// A weak reference to an ICMP socket.
211#[derive(GenericOverIp, Derivative)]
212#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""), Clone(bound = ""))]
213#[generic_over_ip(I, Ip)]
214pub struct WeakIcmpSocketId<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
215    datagram::WeakRc<I, D, Icmp<BT>>,
216);
217
218impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> PartialEq<IcmpSocketId<I, D, BT>>
219    for WeakIcmpSocketId<I, D, BT>
220{
221    fn eq(&self, other: &IcmpSocketId<I, D, BT>) -> bool {
222        PartialEq::eq(other, self)
223    }
224}
225
226impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Debug
227    for WeakIcmpSocketId<I, D, BT>
228{
229    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
230        let Self(rc) = self;
231        f.debug_tuple("WeakIcmpSocketId").field(&rc.debug_id()).finish()
232    }
233}
234
235impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> WeakIcmpSocketId<I, D, BT> {
236    #[cfg_attr(feature = "instrumented", track_caller)]
237    pub fn upgrade(&self) -> Option<IcmpSocketId<I, D, BT>> {
238        let Self(rc) = self;
239        rc.upgrade().map(IcmpSocketId)
240    }
241}
242
243/// The set of ICMP sockets.
244pub type IcmpSocketSet<I, D, BT> = DatagramSocketSet<I, D, Icmp<BT>>;
245/// The state of an ICMP socket.
246pub type IcmpSocketState<I, D, BT> = datagram::SocketState<I, D, Icmp<BT>>;
247/// The tx metadata for an ICMP echo socket.
248pub type IcmpSocketTxMetadata<I, D, BT> = datagram::TxMetadata<I, D, Icmp<BT>>;
249
250/// Errors that Bindings may encounter when receiving an ICMP Echo datagram.
251pub enum ReceiveIcmpEchoError {
252    /// The socket's receive queue is full and can't hold the datagram.
253    QueueFull,
254}
255
256/// The context required by the ICMP layer in order to deliver events related to
257/// ICMP sockets.
258pub trait IcmpEchoBindingsContext<I: IpExt, D: StrongDeviceIdentifier>:
259    IcmpEchoBindingsTypes + ReferenceNotifiers + RngContext
260{
261    /// Receives an ICMP echo reply.
262    fn receive_icmp_echo_reply<B: BufferMut>(
263        &mut self,
264        conn: &IcmpSocketId<I, D::Weak, Self>,
265        device_id: &D,
266        src_ip: I::Addr,
267        dst_ip: I::Addr,
268        id: u16,
269        data: B,
270    ) -> Result<(), ReceiveIcmpEchoError>;
271}
272
273/// The bindings context providing external types to ICMP sockets.
274///
275/// # Discussion
276///
277/// We'd like this trait to take an `I` type parameter instead of using GAT to
278/// get the IP version, however we end up with problems due to the shape of
279/// [`DatagramSocketSpec`] and the underlying support for dual stack sockets.
280///
281/// This is completely fine for all known implementations, except for a rough
282/// edge in fake tests bindings contexts that are already parameterized on I
283/// themselves. This is still better than relying on `Box<dyn Any>` to keep the
284/// external data in our references so we take the rough edge.
285pub trait IcmpEchoBindingsTypes: DatagramBindingsTypes + Sized + 'static {
286    /// Opaque bindings data held by core for a given IP version.
287    type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
288    /// A token representing resources allocated for an in-flight send operation.
289    ///
290    /// Core holds this token until the packet is either transmitted by the
291    /// device or dropped along the egress path. This allows bindings to track
292    /// send buffer capacity or other per-packet resources.
293    type SendToken: Debug + Send + Sync + 'static;
294}
295
296/// Resolve coherence issues by requiring a trait implementation with no type
297/// parameters, which makes the blanket implementations for the datagram specs
298/// viable.
299pub trait IcmpEchoContextMarker {}
300
301/// A Context that provides access to the sockets' states.
302pub trait IcmpEchoBoundStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
303    DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
304{
305    /// The inner context providing IP socket access.
306    type IpSocketsCtx<'a>: TransportIpContext<I, BC>
307        + MulticastMembershipHandler<I, BC>
308        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
309        + CounterContext<IcmpRxCounters<I>>
310        + CoreTxMetadataContext<IcmpSocketTxMetadata<I, Self::WeakDeviceId, BC>, BC>;
311
312    /// Calls the function with a mutable reference to `IpSocketsCtx` and
313    /// a mutable reference to ICMP sockets.
314    fn with_icmp_ctx_and_sockets_mut<
315        O,
316        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut BoundSockets<I, Self::WeakDeviceId, BC>) -> O,
317    >(
318        &mut self,
319        cb: F,
320    ) -> O;
321}
322
323/// A Context that provides access to the sockets' states.
324pub trait IcmpEchoStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
325    DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
326{
327    /// The inner socket context.
328    type SocketStateCtx<'a>: IcmpEchoBoundStateContext<I, BC>
329        + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
330
331    /// Calls the function with mutable access to the set with all ICMP
332    /// sockets.
333    fn with_all_sockets_mut<O, F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
334        &mut self,
335        cb: F,
336    ) -> O;
337
338    /// Calls the function with immutable access to the set with all ICMP
339    /// sockets.
340    fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
341        &mut self,
342        cb: F,
343    ) -> O;
344
345    /// Calls the function without access to ICMP socket state.
346    fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
347        &mut self,
348        cb: F,
349    ) -> O;
350
351    /// Calls the function with an immutable reference to the given socket's
352    /// state.
353    fn with_socket_state<
354        O,
355        F: FnOnce(&mut Self::SocketStateCtx<'_>, &IcmpSocketState<I, Self::WeakDeviceId, BC>) -> O,
356    >(
357        &mut self,
358        id: &IcmpSocketId<I, Self::WeakDeviceId, BC>,
359        cb: F,
360    ) -> O;
361
362    /// Calls the function with a mutable reference to the given socket's state.
363    fn with_socket_state_mut<
364        O,
365        F: FnOnce(&mut Self::SocketStateCtx<'_>, &mut IcmpSocketState<I, Self::WeakDeviceId, BC>) -> O,
366    >(
367        &mut self,
368        id: &IcmpSocketId<I, Self::WeakDeviceId, BC>,
369        cb: F,
370    ) -> O;
371
372    /// Call `f` with each socket's state.
373    fn for_each_socket<
374        F: FnMut(
375            &mut Self::SocketStateCtx<'_>,
376            &IcmpSocketId<I, Self::WeakDeviceId, BC>,
377            &IcmpSocketState<I, Self::WeakDeviceId, BC>,
378        ),
379    >(
380        &mut self,
381        cb: F,
382    );
383}
384
385/// Uninstantiatable type for implementing [`DatagramSocketSpec`].
386pub struct Icmp<BT>(PhantomData<BT>, !);
387
388impl<BT: IcmpEchoBindingsTypes> BindingsSyncTypes for Icmp<BT> {
389    type Mutex = BT::Mutex;
390    type RwLock = BT::RwLock;
391    type Once = BT::Once;
392}
393
394impl<BT: IcmpEchoBindingsTypes> DatagramSocketSpec for Icmp<BT> {
395    const NAME: &'static str = "ICMP_ECHO";
396    type AddrSpec = IcmpAddrSpec;
397
398    type SocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = IcmpSocketId<I, D, BT>;
399    type WeakSocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = WeakIcmpSocketId<I, D, BT>;
400
401    type OtherStackIpOptions<I: datagram::IpExt, D: WeakDeviceIdentifier> = ();
402
403    type SharingState = ();
404
405    type SocketMapSpec<I: datagram::IpExt + datagram::DualStackIpExt, D: WeakDeviceIdentifier> =
406        IcmpSocketMapStateSpec<I, D, BT>;
407
408    fn ip_proto<I: IpProtoExt>() -> I::Proto {
409        I::map_ip((), |()| Ipv4Proto::Icmp, |()| Ipv6Proto::Icmpv6)
410    }
411
412    fn make_bound_socket_map_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
413        s: &Self::SocketId<I, D>,
414    ) -> <Self::SocketMapSpec<I, D> as datagram::DatagramSocketMapSpec<
415        I,
416        D,
417        Self::AddrSpec,
418    >>::BoundSocketId{
419        s.clone()
420    }
421
422    type Serializer<I: datagram::IpExt, B: BufferMut> =
423        packet::Nested<B, IcmpPacketBuilder<I, IcmpEchoRequest>>;
424    type SerializeError = packet_formats::error::ParseError;
425
426    type ExternalData<I: Ip> = BT::ExternalData<I>;
427    type SendToken = BT::SendToken;
428
429    // NB: At present, there's no need to track per-socket ICMP counters.
430    type Counters<I: Ip> = ();
431
432    fn make_packet<I: datagram::IpExt, B: BufferMut>(
433        mut body: B,
434        addr: &socket::ConnIpAddr<
435            I::Addr,
436            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
437            <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
438        >,
439    ) -> Result<Self::Serializer<I, B>, Self::SerializeError> {
440        let ConnIpAddr { local: (local_ip, id), remote: (remote_ip, ()) } = addr;
441        let icmp_echo: packet_formats::icmp::IcmpPacketRaw<I, &[u8], IcmpEchoRequest> =
442            body.parse()?;
443        debug!(
444            "preparing ICMP echo request {local_ip} to {remote_ip}: id={}, seq={}",
445            id,
446            icmp_echo.message().seq()
447        );
448        let icmp_builder = IcmpPacketBuilder::<I, _>::new(
449            local_ip.addr(),
450            remote_ip.addr(),
451            packet_formats::icmp::IcmpZeroCode,
452            IcmpEchoRequest::new(id.get(), icmp_echo.message().seq()),
453        );
454        Ok(icmp_builder.wrap_body(body))
455    }
456
457    fn try_alloc_listen_identifier<I: datagram::IpExt, D: WeakDeviceIdentifier>(
458        bindings_ctx: &mut impl RngContext,
459        is_available: impl Fn(
460            <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
461        ) -> Result<(), datagram::InUseError>,
462    ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
463        let mut port = IcmpPortAlloc::<I, D, BT>::rand_ephemeral(&mut bindings_ctx.rng());
464        for _ in IcmpPortAlloc::<I, D, BT>::EPHEMERAL_RANGE {
465            // We can unwrap here because we know that the EPHEMERAL_RANGE doesn't
466            // include 0.
467            let tryport = NonZeroU16::new(port.get()).unwrap();
468            match is_available(tryport) {
469                Ok(()) => return Some(tryport),
470                Err(datagram::InUseError {}) => port.next(),
471            }
472        }
473        None
474    }
475
476    type ListenerIpAddr<I: datagram::IpExt> = socket::ListenerIpAddr<I::Addr, NonZeroU16>;
477
478    type ConnIpAddr<I: datagram::IpExt> = ConnIpAddr<
479        I::Addr,
480        <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
481        <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
482    >;
483
484    type ConnState<I: datagram::IpExt, D: WeakDeviceIdentifier> = datagram::ConnState<I, D, Self>;
485    // Store the remote port/id set by `connect`. This does not participate in
486    // demuxing, so not part of the socketmap, but we need to store it so that
487    // it can be reported later.
488    type ConnStateExtra = u16;
489
490    fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
491        state: &Self::ConnState<I, D>,
492    ) -> datagram::ConnInfo<I::Addr, D> {
493        let ConnAddr { ip, device } = state.addr();
494        let extra = state.extra();
495        let ConnInfoAddr { local: (local_ip, local_identifier), remote: (remote_ip, ()) } =
496            ip.clone().into();
497        datagram::ConnInfo::new(local_ip, local_identifier, remote_ip, *extra, || {
498            // The invariant that a zone is present if needed is upheld by connect.
499            device.clone().expect("device must be bound for addresses that require zones")
500        })
501    }
502
503    fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
504        bound: &IcmpBoundSockets<I, D, BT>,
505        bindings_ctx: &mut BC,
506        flow: datagram::DatagramFlowId<I::Addr, ()>,
507    ) -> Option<NonZeroU16> {
508        let mut rng = bindings_ctx.rng();
509        netstack3_base::simple_randomized_port_alloc(&mut rng, &flow, &IcmpPortAlloc(bound), &())
510            .map(|p| NonZeroU16::new(p).expect("ephemeral ports should be non-zero"))
511    }
512
513    fn upgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
514        id: &Self::WeakSocketId<I, D>,
515    ) -> Option<Self::SocketId<I, D>> {
516        id.upgrade()
517    }
518
519    fn downgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
520        id: &Self::SocketId<I, D>,
521    ) -> Self::WeakSocketId<I, D> {
522        IcmpSocketId::downgrade(id)
523    }
524}
525
526/// Uninstantiatable type for implementing [`SocketMapAddrSpec`].
527pub enum IcmpAddrSpec {}
528
529impl SocketMapAddrSpec for IcmpAddrSpec {
530    type RemoteIdentifier = ();
531    type LocalIdentifier = NonZeroU16;
532}
533
534type IcmpBoundSockets<I, D, BT> = datagram::BoundDatagramSocketMap<I, D, Icmp<BT>>;
535
536struct IcmpPortAlloc<'a, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
537    &'a IcmpBoundSockets<I, D, BT>,
538);
539
540impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> PortAllocImpl
541    for IcmpPortAlloc<'_, I, D, BT>
542{
543    const EPHEMERAL_RANGE: core::ops::RangeInclusive<u16> = 1..=u16::MAX;
544    type Id = DatagramFlowId<I::Addr, ()>;
545    type PortAvailableArg = ();
546
547    fn is_port_available(&self, id: &Self::Id, port: u16, (): &()) -> bool {
548        let Self(socketmap) = self;
549        // We can safely unwrap here, because the ports received in
550        // `is_port_available` are guaranteed to be in `EPHEMERAL_RANGE`.
551        let port = NonZeroU16::new(port).unwrap();
552        let conn = ConnAddr {
553            ip: ConnIpAddr { local: (id.local_ip, port), remote: (id.remote_ip, ()) },
554            device: None,
555        };
556
557        // A port is free if there are no sockets currently using it, and if
558        // there are no sockets that are shadowing it.
559        AddrVec::from(conn).iter_shadows().all(|a| match &a {
560            AddrVec::Listen(l) => socketmap.listeners().get_by_addr(&l).is_none(),
561            AddrVec::Conn(c) => socketmap.conns().get_by_addr(&c).is_none(),
562        } && socketmap.get_shadower_counts(&a) == 0)
563    }
564}
565
566/// The demux state for ICMP echo sockets.
567#[derive(Derivative)]
568#[derivative(Default(bound = ""))]
569pub struct BoundSockets<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> {
570    pub(crate) socket_map: IcmpBoundSockets<I, D, BT>,
571}
572
573impl<I, BC, CC> NonDualStackDatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
574where
575    I: IpExt + datagram::DualStackIpExt,
576    BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
577    CC: DeviceIdContext<AnyDevice> + IcmpEchoContextMarker,
578{
579    fn nds_converter(_core_ctx: &CC) -> impl NonDualStackConverter<I, CC::WeakDeviceId, Self> {
580        ()
581    }
582}
583
584impl<I, BC, CC> DatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
585where
586    I: IpExt + datagram::DualStackIpExt,
587    BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
588    CC: IcmpEchoBoundStateContext<I, BC> + IcmpEchoContextMarker,
589{
590    type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
591
592    // ICMP sockets doesn't support dual-stack operations.
593    type DualStackContext = UninstantiableWrapper<CC>;
594
595    type NonDualStackContext = CC;
596
597    fn with_bound_sockets<
598        O,
599        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
600    >(
601        core_ctx: &mut CC,
602        cb: F,
603    ) -> O {
604        IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
605            core_ctx,
606            |ctx, BoundSockets { socket_map }| cb(ctx, &socket_map),
607        )
608    }
609
610    fn with_bound_sockets_mut<
611        O,
612        F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
613    >(
614        core_ctx: &mut CC,
615        cb: F,
616    ) -> O {
617        IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
618            core_ctx,
619            |ctx, BoundSockets { socket_map }| cb(ctx, socket_map),
620        )
621    }
622
623    fn dual_stack_context(
624        core_ctx: &CC,
625    ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
626        MaybeDualStack::NotDualStack(core_ctx)
627    }
628
629    fn dual_stack_context_mut(
630        core_ctx: &mut CC,
631    ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
632        MaybeDualStack::NotDualStack(core_ctx)
633    }
634
635    fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
636        core_ctx: &mut CC,
637        cb: F,
638    ) -> O {
639        IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(core_ctx, |ctx, _sockets| cb(ctx))
640    }
641}
642
643impl<I, BC, CC> DatagramSpecStateContext<I, CC, BC> for Icmp<BC>
644where
645    I: IpExt + datagram::DualStackIpExt,
646    BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
647    CC: IcmpEchoStateContext<I, BC>,
648{
649    type SocketsStateCtx<'a> = CC::SocketStateCtx<'a>;
650
651    fn with_all_sockets_mut<O, F: FnOnce(&mut IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
652        core_ctx: &mut CC,
653        cb: F,
654    ) -> O {
655        IcmpEchoStateContext::with_all_sockets_mut(core_ctx, cb)
656    }
657
658    fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
659        core_ctx: &mut CC,
660        cb: F,
661    ) -> O {
662        IcmpEchoStateContext::with_all_sockets(core_ctx, cb)
663    }
664
665    fn with_socket_state<
666        O,
667        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
668    >(
669        core_ctx: &mut CC,
670        id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
671        cb: F,
672    ) -> O {
673        IcmpEchoStateContext::with_socket_state(core_ctx, id, cb)
674    }
675
676    fn with_socket_state_mut<
677        O,
678        F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
679    >(
680        core_ctx: &mut CC,
681        id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
682        cb: F,
683    ) -> O {
684        IcmpEchoStateContext::with_socket_state_mut(core_ctx, id, cb)
685    }
686
687    fn for_each_socket<
688        F: FnMut(
689            &mut Self::SocketsStateCtx<'_>,
690            &IcmpSocketId<I, CC::WeakDeviceId, BC>,
691            &IcmpSocketState<I, CC::WeakDeviceId, BC>,
692        ),
693    >(
694        core_ctx: &mut CC,
695        cb: F,
696    ) {
697        IcmpEchoStateContext::for_each_socket(core_ctx, cb)
698    }
699}
700
701/// An uninstantiable type providing a [`SocketMapStateSpec`] implementation for
702/// ICMP.
703pub struct IcmpSocketMapStateSpec<I, D, BT>(PhantomData<(I, D, BT)>, !);
704
705impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapStateSpec
706    for IcmpSocketMapStateSpec<I, D, BT>
707{
708    type ListenerId = IcmpSocketId<I, D, BT>;
709    type ConnId = IcmpSocketId<I, D, BT>;
710
711    type AddrVecTag = ();
712
713    type ListenerSharingState = ();
714    type ConnSharingState = ();
715
716    type ListenerAddrState = Self::ListenerId;
717
718    type ConnAddrState = Self::ConnId;
719    fn listener_tag(
720        ListenerAddrInfo { has_device: _, specified_addr: _ }: ListenerAddrInfo,
721        _state: &Self::ListenerAddrState,
722    ) -> Self::AddrVecTag {
723        ()
724    }
725    fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
726        ()
727    }
728}
729
730impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapAddrStateSpec
731    for IcmpSocketId<I, D, BT>
732{
733    type Id = Self;
734
735    type SharingState = ();
736
737    type Inserter<'a>
738        = !
739    where
740        Self: 'a;
741
742    fn new(_new_sharing_state: &Self::SharingState, id: Self::Id) -> Self {
743        id
744    }
745
746    fn contains_id(&self, id: &Self::Id) -> bool {
747        self == id
748    }
749
750    fn try_get_inserter<'a, 'b>(
751        &'b mut self,
752        _new_sharing_state: &'a Self::SharingState,
753    ) -> Result<Self::Inserter<'b>, IncompatibleError> {
754        Err(IncompatibleError)
755    }
756
757    fn could_insert(
758        &self,
759        _new_sharing_state: &Self::SharingState,
760    ) -> Result<(), IncompatibleError> {
761        Err(IncompatibleError)
762    }
763
764    fn remove_by_id(&mut self, _id: Self::Id) -> socket::RemoveResult {
765        socket::RemoveResult::IsLast
766    }
767
768    fn sharing_state(&self) -> Self::SharingState {
769        ()
770    }
771}
772
773impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
774    DatagramSocketMapSpec<I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
775{
776    type BoundSocketId = IcmpSocketId<I, D, BT>;
777}
778
779impl<AA, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
780    SocketMapConflictPolicy<AA, (), I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
781where
782    AA: Into<AddrVec<I, D, IcmpAddrSpec>> + Clone,
783{
784    fn check_insert_conflicts(
785        _new_sharing_state: &(),
786        addr: &AA,
787        socketmap: &SocketMap<AddrVec<I, D, IcmpAddrSpec>, socket::Bound<Self>>,
788    ) -> Result<(), socket::InsertError> {
789        let addr: AddrVec<_, _, _> = addr.clone().into();
790        // Having a value present at a shadowed address is disqualifying.
791        if addr.iter_shadows().any(|a| socketmap.get(&a).is_some()) {
792            return Err(InsertError::ShadowAddrExists);
793        }
794
795        // Likewise, the presence of a value that shadows the target address is
796        // also disqualifying.
797        if socketmap.descendant_counts(&addr).len() != 0 {
798            return Err(InsertError::WouldShadowExisting);
799        }
800        Ok(())
801    }
802}
803
804/// The ICMP Echo sockets API.
805pub struct IcmpEchoSocketApi<I: Ip, C>(C, IpVersionMarker<I>);
806
807impl<I: Ip, C> IcmpEchoSocketApi<I, C> {
808    /// Creates a new API instance.
809    pub fn new(ctx: C) -> Self {
810        Self(ctx, IpVersionMarker::new())
811    }
812}
813
814/// A local alias for [`IcmpSocketId`] for use in [`IcmpEchoSocketApi`].
815///
816/// TODO(https://github.com/rust-lang/rust/issues/8995): Make this an inherent
817/// associated type.
818type IcmpApiSocketId<I, C> = IcmpSocketId<
819    I,
820    <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
821    <C as ContextPair>::BindingsContext,
822>;
823
824impl<I, C> IcmpEchoSocketApi<I, C>
825where
826    I: datagram::IpExt,
827    C: ContextPair,
828    C::CoreContext: IcmpEchoStateContext<I, C::BindingsContext>
829        // NB: This bound is somewhat redundant to StateContext but it helps the
830        // compiler know we're using ICMP datagram sockets.
831        + DatagramStateContext<I, C::BindingsContext, Icmp<C::BindingsContext>>,
832    C::BindingsContext:
833        IcmpEchoBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
834{
835    fn core_ctx(&mut self) -> &mut C::CoreContext {
836        let Self(pair, IpVersionMarker { .. }) = self;
837        pair.core_ctx()
838    }
839
840    fn datagram(&mut self) -> &mut DatagramApi<I, C, Icmp<C::BindingsContext>> {
841        let Self(pair, IpVersionMarker { .. }) = self;
842        DatagramApi::wrap(pair)
843    }
844
845    /// Creates a new unbound ICMP socket with default external data.
846    pub fn create(&mut self) -> IcmpApiSocketId<I, C>
847    where
848        <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>: Default,
849    {
850        self.create_with(Default::default())
851    }
852
853    /// Creates a new unbound ICMP socket with provided external data.
854    pub fn create_with(
855        &mut self,
856        external_data: <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
857    ) -> IcmpApiSocketId<I, C> {
858        self.datagram().create(external_data)
859    }
860
861    /// Connects an ICMP socket to remote IP.
862    ///
863    /// If the socket is never bound, an local ID will be allocated.
864    pub fn connect(
865        &mut self,
866        id: &IcmpApiSocketId<I, C>,
867        remote_ip: Option<
868            ZonedAddr<
869                SpecifiedAddr<I::Addr>,
870                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
871            >,
872        >,
873        remote_id: u16,
874    ) -> Result<(), datagram::ConnectError> {
875        self.datagram().connect(id, remote_ip, (), remote_id)
876    }
877
878    /// Binds an ICMP socket to a local IP address and a local ID.
879    ///
880    /// Both the IP and the ID are optional. When IP is missing, the "any" IP is
881    /// assumed; When the ID is missing, it will be allocated.
882    pub fn bind(
883        &mut self,
884        id: &IcmpApiSocketId<I, C>,
885        local_ip: Option<
886            ZonedAddr<
887                SpecifiedAddr<I::Addr>,
888                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
889            >,
890        >,
891        icmp_id: Option<NonZeroU16>,
892    ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
893        self.datagram().listen(id, local_ip, icmp_id)
894    }
895
896    /// Gets the information about an ICMP socket.
897    pub fn get_info(
898        &mut self,
899        id: &IcmpApiSocketId<I, C>,
900    ) -> datagram::SocketInfo<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>
901    {
902        self.datagram().get_info(id)
903    }
904
905    /// Sets the bound device for a socket.
906    ///
907    /// Sets the device to be used for sending and receiving packets for a
908    /// socket. If the socket is not currently bound to a local address and
909    /// port, the device will be used when binding.
910    pub fn set_device(
911        &mut self,
912        id: &IcmpApiSocketId<I, C>,
913        device_id: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
914    ) -> Result<(), SocketError> {
915        self.datagram().set_device(id, device_id)
916    }
917
918    /// Gets the device the specified socket is bound to.
919    pub fn get_bound_device(
920        &mut self,
921        id: &IcmpApiSocketId<I, C>,
922    ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
923        self.datagram().get_bound_device(id)
924    }
925
926    /// Disconnects an ICMP socket.
927    pub fn disconnect(
928        &mut self,
929        id: &IcmpApiSocketId<I, C>,
930    ) -> Result<(), datagram::ExpectedConnError> {
931        self.datagram().disconnect_connected(id)
932    }
933
934    /// Shuts down an ICMP socket.
935    pub fn shutdown(
936        &mut self,
937        id: &IcmpApiSocketId<I, C>,
938        shutdown_type: ShutdownType,
939    ) -> Result<(), datagram::ExpectedConnError> {
940        self.datagram().shutdown_connected(id, shutdown_type)
941    }
942
943    /// Gets the current shutdown state of an ICMP socket.
944    pub fn get_shutdown(&mut self, id: &IcmpApiSocketId<I, C>) -> Option<ShutdownType> {
945        self.datagram().get_shutdown_connected(id)
946    }
947
948    /// Closes an ICMP socket.
949    pub fn close(
950        &mut self,
951        id: IcmpApiSocketId<I, C>,
952    ) -> RemoveResourceResultWithContext<
953        <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
954        C::BindingsContext,
955    > {
956        self.datagram().close(id, |state| {
957            let (_state, external_data) = state.into_state_and_external_data();
958            external_data
959        })
960    }
961
962    /// Gets unicast IP hop limit for ICMP sockets.
963    pub fn get_unicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
964        self.datagram().get_ip_hop_limits(id).unicast
965    }
966
967    /// Gets multicast IP hop limit for ICMP sockets.
968    pub fn get_multicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
969        self.datagram().get_ip_hop_limits(id).multicast
970    }
971
972    /// Sets unicast IP hop limit for ICMP sockets.
973    pub fn set_unicast_hop_limit(
974        &mut self,
975        id: &IcmpApiSocketId<I, C>,
976        hop_limit: Option<NonZeroU8>,
977    ) {
978        self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_unicast(hop_limit))
979    }
980
981    /// Sets multicast IP hop limit for ICMP sockets.
982    pub fn set_multicast_hop_limit(
983        &mut self,
984        id: &IcmpApiSocketId<I, C>,
985        hop_limit: Option<NonZeroU8>,
986    ) {
987        self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_multicast(hop_limit))
988    }
989
990    /// Gets the loopback multicast option.
991    pub fn get_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>) -> bool {
992        self.datagram().get_multicast_loop(id)
993    }
994
995    /// Sets the loopback multicast option.
996    pub fn set_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>, value: bool) {
997        self.datagram().set_multicast_loop(id, value);
998    }
999
1000    /// Sets the socket mark for the socket domain.
1001    pub fn set_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain, mark: Mark) {
1002        self.datagram().set_mark(id, domain, mark)
1003    }
1004
1005    /// Gets the socket mark for the socket domain.
1006    pub fn get_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
1007        self.datagram().get_mark(id, domain)
1008    }
1009
1010    /// Sends an ICMP packet through a connection.
1011    ///
1012    /// The socket must be connected in order for the operation to succeed.
1013    pub fn send<B: BufferMut>(
1014        &mut self,
1015        id: &IcmpApiSocketId<I, C>,
1016        body: B,
1017        send_token: <C::BindingsContext as IcmpEchoBindingsTypes>::SendToken,
1018    ) -> Result<(), datagram::SendError<packet_formats::error::ParseError>> {
1019        self.datagram().send_conn(id, body, send_token)
1020    }
1021
1022    /// Sends an ICMP packet with an remote address.
1023    ///
1024    /// The socket doesn't need to be connected.
1025    pub fn send_to<B: BufferMut>(
1026        &mut self,
1027        id: &IcmpApiSocketId<I, C>,
1028        remote_ip: Option<
1029            ZonedAddr<
1030                SpecifiedAddr<I::Addr>,
1031                <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
1032            >,
1033        >,
1034        body: B,
1035        send_token: <C::BindingsContext as IcmpEchoBindingsTypes>::SendToken,
1036    ) -> Result<(), datagram::SendToError<packet_formats::error::ParseError>> {
1037        self.datagram().send_to(id, remote_ip, (), body, send_token)
1038    }
1039
1040    /// Collects all currently opened sockets, returning a cloned reference for
1041    /// each one.
1042    pub fn collect_all_sockets(&mut self) -> Vec<IcmpApiSocketId<I, C>> {
1043        self.datagram().collect_all_sockets()
1044    }
1045
1046    /// Provides inspect data for ICMP echo sockets.
1047    pub fn inspect<N>(&mut self, inspector: &mut N)
1048    where
1049        N: Inspector
1050            + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1051        for<'a> N::ChildInspector<'a>:
1052            InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1053    {
1054        DatagramStateContext::for_each_socket(self.core_ctx(), |_ctx, socket_id, socket_state| {
1055            inspector.record_debug_child(socket_id, |inspector| {
1056                socket_state.record_common_info(inspector);
1057            });
1058        });
1059    }
1060}
1061
1062/// An [`IpTransportContext`] implementation for handling ICMP Echo replies.
1063///
1064/// This special implementation will panic if it receives any packets that are
1065/// not ICMP echo replies and any error that are not originally an ICMP Echo
1066/// request.
1067pub enum IcmpEchoIpTransportContext {}
1068
1069impl EchoTransportContextMarker for IcmpEchoIpTransportContext {}
1070
1071impl<I: IpExt, BC: IcmpEchoBindingsContext<I, CC::DeviceId>, CC: IcmpEchoBoundStateContext<I, BC>>
1072    IpTransportContext<I, BC, CC> for IcmpEchoIpTransportContext
1073{
1074    type EarlyDemuxSocket = !;
1075
1076    fn early_demux<B: ParseBuffer>(
1077        _core_ctx: &mut CC,
1078        _device: &CC::DeviceId,
1079        _src_ip: I::Addr,
1080        _dst_ip: I::Addr,
1081        _buffer: B,
1082    ) -> Option<Self::EarlyDemuxSocket> {
1083        None
1084    }
1085
1086    fn receive_icmp_error(
1087        core_ctx: &mut CC,
1088        _bindings_ctx: &mut BC,
1089        _device: &CC::DeviceId,
1090        original_src_ip: Option<SpecifiedAddr<I::Addr>>,
1091        original_dst_ip: SpecifiedAddr<I::Addr>,
1092        mut original_body: &[u8],
1093        err: I::ErrorCode,
1094    ) {
1095        let echo_request = original_body
1096            .parse::<IcmpPacketRaw<I, _, IcmpEchoRequest>>()
1097            .expect("received non-echo request");
1098
1099        let original_src_ip = match original_src_ip {
1100            Some(ip) => ip,
1101            None => {
1102                trace!("IcmpIpTransportContext::receive_icmp_error: unspecified source IP address");
1103                return;
1104            }
1105        };
1106        let original_src_ip: SocketIpAddr<_> = match original_src_ip.try_into() {
1107            Ok(ip) => ip,
1108            Err(AddrIsMappedError {}) => {
1109                trace!("IcmpIpTransportContext::receive_icmp_error: mapped source IP address");
1110                return;
1111            }
1112        };
1113        let original_dst_ip: SocketIpAddr<_> = match original_dst_ip.try_into() {
1114            Ok(ip) => ip,
1115            Err(AddrIsMappedError {}) => {
1116                trace!("IcmpIpTransportContext::receive_icmp_error: mapped destination IP address");
1117                return;
1118            }
1119        };
1120
1121        // The netstack will never generate an ID of 0, so we can just throw
1122        // this away for being invalid.
1123        let Some(id) = NonZeroU16::new(echo_request.message().id()) else {
1124            debug!(
1125                "ICMP received ICMP error {:?} from {:?}, to {:?} with an ID of 0",
1126                err, original_dst_ip, original_src_ip,
1127            );
1128            return;
1129        };
1130
1131        core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1132            if let Some(conn) = sockets.socket_map.conns().get_by_addr(&ConnAddr {
1133                ip: ConnIpAddr { local: (original_src_ip, id), remote: (original_dst_ip, ()) },
1134                device: None,
1135            }) {
1136                // NB: At the moment bindings has no need to consume ICMP
1137                // errors, so we swallow them here.
1138                debug!(
1139                    "ICMP received ICMP error {:?} from {:?}, to {:?} on socket {:?}",
1140                    err, original_dst_ip, original_src_ip, conn
1141                );
1142                CounterContext::<IcmpRxCounters<I>>::counters(core_ctx)
1143                    .error_delivered_to_socket
1144                    .increment()
1145            } else {
1146                trace!(
1147                    "IcmpIpTransportContext::receive_icmp_error: Got ICMP error message for \
1148                    nonexistent ICMP echo socket; either the socket responsible has since been \
1149                    removed, or the error message was sent in error or corrupted"
1150                );
1151            }
1152        })
1153    }
1154
1155    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
1156        core_ctx: &mut CC,
1157        bindings_ctx: &mut BC,
1158        device: &CC::DeviceId,
1159        src_ip: I::RecvSrcAddr,
1160        dst_ip: SpecifiedAddr<I::Addr>,
1161        mut buffer: B,
1162        info: &mut LocalDeliveryPacketInfo<I, H>,
1163        _early_demux_socket: Option<!>,
1164    ) -> Result<(), (B, I::IcmpError)> {
1165        let LocalDeliveryPacketInfo { meta, header_info: _, marks: _, packet_type: _, gso_info: _ } =
1166            info;
1167        let ReceiveIpPacketMeta { broadcast: _, transparent_override, parsing_context: _ } = meta;
1168        if let Some(delivery) = transparent_override.as_ref() {
1169            unreachable!(
1170                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1171                transparent proxy rules can only be configured for TCP and UDP packets"
1172            );
1173        }
1174        // NB: We're doing raw parsing here just to extract the ID and body to
1175        // send up to bindings. The IP layer has performed full validation
1176        // including checksum for us.
1177        let echo_reply =
1178            buffer.parse::<IcmpPacketRaw<I, _, IcmpEchoReply>>().expect("received non-echo reply");
1179        // We never generate requests with ID zero due to local socket map.
1180        let Some(id) = NonZeroU16::new(echo_reply.message().id()) else { return Ok(()) };
1181
1182        // Undo parse so we give out the full ICMP header.
1183        let meta = echo_reply.parse_metadata();
1184        buffer.undo_parse(meta);
1185
1186        let src_ip = match SpecifiedAddr::new(src_ip.into_addr()) {
1187            Some(src_ip) => src_ip,
1188            None => {
1189                trace!("receive_icmp_echo_reply: unspecified source address");
1190                return Ok(());
1191            }
1192        };
1193        let src_ip: SocketIpAddr<_> = match src_ip.try_into() {
1194            Ok(src_ip) => src_ip,
1195            Err(AddrIsMappedError {}) => {
1196                trace!("receive_icmp_echo_reply: mapped source address");
1197                return Ok(());
1198            }
1199        };
1200        let dst_ip: SocketIpAddr<_> = match dst_ip.try_into() {
1201            Ok(dst_ip) => dst_ip,
1202            Err(AddrIsMappedError {}) => {
1203                trace!("receive_icmp_echo_reply: mapped destination address");
1204                return Ok(());
1205            }
1206        };
1207
1208        core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1209            let mut addrs_to_search = AddrVecIter::<I, CC::WeakDeviceId, IcmpAddrSpec>::with_device(
1210                ConnIpAddr { local: (dst_ip, id), remote: (src_ip, ()) }.into(),
1211                device.downgrade(),
1212            );
1213            let socket = match addrs_to_search.try_for_each(|addr_vec| {
1214                match addr_vec {
1215                    AddrVec::Conn(c) => {
1216                        if let Some(id) = sockets.socket_map.conns().get_by_addr(&c) {
1217                            return ControlFlow::Break(id);
1218                        }
1219                    }
1220                    AddrVec::Listen(l) => {
1221                        if let Some(id) = sockets.socket_map.listeners().get_by_addr(&l) {
1222                            return ControlFlow::Break(id);
1223                        }
1224                    }
1225                }
1226                ControlFlow::Continue(())
1227            }) {
1228                ControlFlow::Continue(()) => None,
1229                ControlFlow::Break(id) => Some(id),
1230            };
1231            if let Some(socket) = socket {
1232                trace!("receive_icmp_echo_reply: Received echo reply for local socket");
1233                match bindings_ctx.receive_icmp_echo_reply(
1234                    socket,
1235                    device,
1236                    src_ip.addr(),
1237                    dst_ip.addr(),
1238                    id.get(),
1239                    buffer,
1240                ) {
1241                    Ok(()) => {}
1242                    Err(ReceiveIcmpEchoError::QueueFull) => {
1243                        core_ctx.counters().queue_full.increment();
1244                    }
1245                }
1246                return;
1247            }
1248            // TODO(https://fxbug.dev/42124755): Neither the ICMPv4 or ICMPv6 RFCs
1249            // explicitly state what to do in case we receive an "unsolicited"
1250            // echo reply. We only expose the replies if we have a registered
1251            // connection for the IcmpAddr of the incoming reply for now. Given
1252            // that a reply should only be sent in response to a request, an
1253            // ICMP unreachable-type message is probably not appropriate for
1254            // unsolicited replies. However, it's also possible that we sent a
1255            // request and then closed the socket before receiving the reply, so
1256            // this doesn't necessarily indicate a buggy or malicious remote
1257            // host. We should figure this out definitively.
1258            //
1259            // If we do decide to send an ICMP error message, the appropriate
1260            // thing to do is probably to have this function return a `Result`,
1261            // and then have the top-level implementation of
1262            // `IpTransportContext::receive_ip_packet` return the
1263            // appropriate error.
1264            trace!("receive_icmp_echo_reply: Received echo reply with no local socket");
1265        });
1266        Ok(())
1267    }
1268}
1269
1270#[cfg(test)]
1271mod tests {
1272    use alloc::rc::Rc;
1273    use alloc::vec;
1274    use core::cell::RefCell;
1275    use core::ops::{Deref, DerefMut};
1276
1277    use assert_matches::assert_matches;
1278    use ip_test_macro::ip_test;
1279    use net_declare::net_ip_v6;
1280    use net_types::Witness;
1281    use net_types::ip::Ipv6;
1282    use netstack3_base::socket::StrictlyZonedAddr;
1283    use netstack3_base::testutil::{
1284        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSendToken, FakeWeakDeviceId, TestIpExt,
1285    };
1286    use netstack3_base::{CtxPair, Icmpv4ErrorCode, Icmpv6ErrorCode, NetworkSerializationContext};
1287    use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx, InnerFakeIpSocketCtx};
1288    use netstack3_ip::{LocalDeliveryPacketInfo, SendIpPacketMeta};
1289    use packet::{Buf, EmptyBuf, NestableSerializer as _, Serializer};
1290    use packet_formats::icmp::{
1291        IcmpDestUnreachable, IcmpPacket, IcmpParseArgs, IcmpZeroCode, Icmpv4DestUnreachableCode,
1292        Icmpv6DestUnreachableCode,
1293    };
1294
1295    use super::*;
1296
1297    const REMOTE_ID: u16 = 27;
1298    const ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1299    const SEQ_NUM: u16 = 0xF0;
1300
1301    /// Utilities for accessing locked internal state in tests.
1302    impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
1303        fn get(&self) -> impl Deref<Target = IcmpSocketState<I, D, BT>> + '_ {
1304            self.state().read()
1305        }
1306
1307        fn get_mut(&self) -> impl DerefMut<Target = IcmpSocketState<I, D, BT>> + '_ {
1308            self.state().write()
1309        }
1310    }
1311
1312    struct FakeIcmpCoreCtxState<I: IpExt> {
1313        bound_sockets:
1314            Rc<RefCell<BoundSockets<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>>>,
1315        all_sockets: IcmpSocketSet<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1316        ip_socket_ctx: FakeIpSocketCtx<I, FakeDeviceId>,
1317        rx_counters: IcmpRxCounters<I>,
1318    }
1319
1320    impl<I: IpExt> InnerFakeIpSocketCtx<I, FakeDeviceId> for FakeIcmpCoreCtxState<I> {
1321        fn fake_ip_socket_ctx_mut(&mut self) -> &mut FakeIpSocketCtx<I, FakeDeviceId> {
1322            &mut self.ip_socket_ctx
1323        }
1324    }
1325
1326    impl<I: IpExt + TestIpExt> Default for FakeIcmpCoreCtxState<I> {
1327        fn default() -> Self {
1328            Self {
1329                bound_sockets: Default::default(),
1330                all_sockets: Default::default(),
1331                ip_socket_ctx: FakeIpSocketCtx::new(core::iter::once(FakeDeviceConfig {
1332                    device: FakeDeviceId,
1333                    local_ips: vec![I::TEST_ADDRS.local_ip],
1334                    remote_ips: vec![I::TEST_ADDRS.remote_ip],
1335                })),
1336                rx_counters: Default::default(),
1337            }
1338        }
1339    }
1340
1341    type FakeIcmpCoreCtx<I> = FakeCoreCtx<
1342        FakeIcmpCoreCtxState<I>,
1343        SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
1344        FakeDeviceId,
1345    >;
1346    type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<(), (), FakeIcmpBindingsCtxState<I>, ()>;
1347    type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
1348
1349    #[derive(Derivative)]
1350    #[derivative(Default)]
1351
1352    struct FakeIcmpBindingsCtxState<I: IpExt> {
1353        received: Vec<ReceivedEchoPacket<I>>,
1354        #[derivative(Default(value = "usize::MAX"))]
1355        max_size: usize,
1356    }
1357
1358    #[derive(Debug)]
1359    struct ReceivedEchoPacket<I: IpExt> {
1360        src_ip: I::Addr,
1361        dst_ip: I::Addr,
1362        socket: IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1363        id: u16,
1364        data: Vec<u8>,
1365    }
1366
1367    impl<I: IpExt> IcmpEchoContextMarker for FakeIcmpCoreCtx<I> {}
1368
1369    impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtxState<I> {
1370        fn counters(&self) -> &IcmpRxCounters<I> {
1371            &self.rx_counters
1372        }
1373    }
1374
1375    impl<I: IpExt> IcmpEchoBoundStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1376        type IpSocketsCtx<'a> = Self;
1377
1378        fn with_icmp_ctx_and_sockets_mut<
1379            O,
1380            F: FnOnce(
1381                &mut Self::IpSocketsCtx<'_>,
1382                &mut BoundSockets<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1383            ) -> O,
1384        >(
1385            &mut self,
1386            cb: F,
1387        ) -> O {
1388            let bound_sockets = self.state.bound_sockets.clone();
1389            let mut bound_sockets = bound_sockets.borrow_mut();
1390            cb(self, &mut bound_sockets)
1391        }
1392    }
1393
1394    impl<I: IpExt> IcmpEchoStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1395        type SocketStateCtx<'a> = Self;
1396
1397        fn with_all_sockets_mut<
1398            O,
1399            F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1400        >(
1401            &mut self,
1402            cb: F,
1403        ) -> O {
1404            cb(&mut self.state.all_sockets)
1405        }
1406
1407        fn with_all_sockets<
1408            O,
1409            F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1410        >(
1411            &mut self,
1412            cb: F,
1413        ) -> O {
1414            cb(&self.state.all_sockets)
1415        }
1416
1417        fn with_socket_state<
1418            O,
1419            F: FnOnce(
1420                &mut Self::SocketStateCtx<'_>,
1421                &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1422            ) -> O,
1423        >(
1424            &mut self,
1425            id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1426            cb: F,
1427        ) -> O {
1428            cb(self, &id.get())
1429        }
1430
1431        fn with_socket_state_mut<
1432            O,
1433            F: FnOnce(
1434                &mut Self::SocketStateCtx<'_>,
1435                &mut IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1436            ) -> O,
1437        >(
1438            &mut self,
1439            id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1440            cb: F,
1441        ) -> O {
1442            cb(self, &mut id.get_mut())
1443        }
1444
1445        fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
1446            &mut self,
1447            cb: F,
1448        ) -> O {
1449            cb(self)
1450        }
1451
1452        fn for_each_socket<
1453            F: FnMut(
1454                &mut Self::SocketStateCtx<'_>,
1455                &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1456                &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1457            ),
1458        >(
1459            &mut self,
1460            mut cb: F,
1461        ) {
1462            let socks = self
1463                .state
1464                .all_sockets
1465                .keys()
1466                .map(|id| IcmpSocketId::from(id.clone()))
1467                .collect::<Vec<_>>();
1468            for id in socks {
1469                cb(self, &id, &id.get());
1470            }
1471        }
1472    }
1473
1474    impl<I: IpExt> IcmpEchoBindingsContext<I, FakeDeviceId> for FakeIcmpBindingsCtx<I> {
1475        fn receive_icmp_echo_reply<B: BufferMut>(
1476            &mut self,
1477            socket: &IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1478            _device_id: &FakeDeviceId,
1479            src_ip: I::Addr,
1480            dst_ip: I::Addr,
1481            id: u16,
1482            data: B,
1483        ) -> Result<(), ReceiveIcmpEchoError> {
1484            if self.state.received.len() < self.state.max_size {
1485                self.state.received.push(ReceivedEchoPacket {
1486                    src_ip,
1487                    dst_ip,
1488                    id,
1489                    data: data.to_flattened_vec(),
1490                    socket: socket.clone(),
1491                });
1492                Ok(())
1493            } else {
1494                Err(ReceiveIcmpEchoError::QueueFull)
1495            }
1496        }
1497    }
1498
1499    impl<I: IpExt> IcmpEchoBindingsTypes for FakeIcmpBindingsCtx<I> {
1500        type ExternalData<II: Ip> = ();
1501        type SendToken = FakeSendToken;
1502    }
1503
1504    #[test]
1505    fn test_connect_dual_stack_fails() {
1506        // Verify that connecting to an ipv4-mapped-ipv6 address fails, as ICMP
1507        // sockets do not support dual-stack operations.
1508        let mut ctx = FakeIcmpCtx::<Ipv6>::default();
1509        let mut api = IcmpEchoSocketApi::<Ipv6, _>::new(ctx.as_mut());
1510        let conn = api.create();
1511        assert_eq!(
1512            api.connect(
1513                &conn,
1514                Some(ZonedAddr::Unzoned(
1515                    SpecifiedAddr::new(net_ip_v6!("::ffff:192.0.2.1")).unwrap(),
1516                )),
1517                REMOTE_ID,
1518            ),
1519            Err(datagram::ConnectError::RemoteUnexpectedlyMapped)
1520        );
1521    }
1522
1523    #[ip_test(I)]
1524    fn send_invalid_icmp_echo<I: TestIpExt + IpExt>() {
1525        let mut ctx = FakeIcmpCtx::<I>::default();
1526        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1527        let conn = api.create();
1528        api.connect(&conn, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1529
1530        let pb = IcmpPacketBuilder::<I, _>::new(
1531            I::TEST_ADDRS.local_ip.get(),
1532            I::TEST_ADDRS.remote_ip.get(),
1533            IcmpZeroCode,
1534            packet_formats::icmp::IcmpEchoReply::new(0, 1),
1535        );
1536        let buf = pb
1537            .wrap_body(Buf::new(Vec::new(), ..))
1538            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1539            .unwrap()
1540            .into_inner();
1541        assert_matches!(
1542            api.send(&conn, buf, FakeSendToken::default()),
1543            Err(datagram::SendError::SerializeError(
1544                packet_formats::error::ParseError::NotExpected
1545            ))
1546        );
1547    }
1548
1549    #[ip_test(I)]
1550    fn get_info<I: TestIpExt + IpExt>() {
1551        let mut ctx = FakeIcmpCtx::<I>::default();
1552        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1553
1554        let id = api.create();
1555        assert_eq!(api.get_info(&id), datagram::SocketInfo::Unbound);
1556
1557        api.bind(&id, None, Some(ICMP_ID)).unwrap();
1558        assert_eq!(
1559            api.get_info(&id),
1560            datagram::SocketInfo::Listener(datagram::ListenerInfo {
1561                local_ip: None,
1562                local_identifier: ICMP_ID
1563            })
1564        );
1565
1566        api.connect(&id, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1567        assert_eq!(
1568            api.get_info(&id),
1569            datagram::SocketInfo::Connected(datagram::ConnInfo {
1570                local_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.local_ip),
1571                local_identifier: ICMP_ID,
1572                remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.remote_ip),
1573                remote_identifier: REMOTE_ID,
1574            })
1575        );
1576    }
1577
1578    #[ip_test(I)]
1579    fn send<I: TestIpExt + IpExt>() {
1580        let mut ctx = FakeIcmpCtx::<I>::default();
1581        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1582        let sock = api.create();
1583
1584        api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1585        api.connect(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1586
1587        let packet = Buf::new([1u8, 2, 3, 4], ..)
1588            .wrap_in(IcmpPacketBuilder::<I, _>::new(
1589                I::UNSPECIFIED_ADDRESS,
1590                I::UNSPECIFIED_ADDRESS,
1591                IcmpZeroCode,
1592                // Use 0 here to show that this is filled by the API.
1593                IcmpEchoRequest::new(0, SEQ_NUM),
1594            ))
1595            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1596            .unwrap()
1597            .unwrap_b();
1598        api.send(&sock, Buf::new(packet, ..), FakeSendToken::default()).unwrap();
1599        let frames = ctx.core_ctx.frames.take_frames();
1600        let (SendIpPacketMeta { device: _, src_ip, dst_ip, .. }, body) =
1601            assert_matches!(&frames[..], [f] => f);
1602        assert_eq!(dst_ip, &I::TEST_ADDRS.remote_ip);
1603
1604        let mut body = &body[..];
1605        let echo_req: IcmpPacket<I, _, IcmpEchoRequest> =
1606            body.parse_with(IcmpParseArgs::new(src_ip.get(), dst_ip.get())).unwrap();
1607        assert_eq!(echo_req.message().id(), ICMP_ID.get());
1608        assert_eq!(echo_req.message().seq(), SEQ_NUM);
1609    }
1610
1611    #[ip_test(I)]
1612    fn receive<I: TestIpExt + IpExt>() {
1613        let mut ctx = FakeIcmpCtx::<I>::default();
1614        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1615        let sock = api.create();
1616
1617        api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1618
1619        let reply = IcmpPacketBuilder::<I, _>::new(
1620            // Use whatever here this is not validated by this module.
1621            I::UNSPECIFIED_ADDRESS,
1622            I::UNSPECIFIED_ADDRESS,
1623            IcmpZeroCode,
1624            IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1625        )
1626        .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1627        .serialize_vec_outer(&mut NetworkSerializationContext::default())
1628        .unwrap();
1629
1630        let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1631        let src_ip = I::TEST_ADDRS.remote_ip;
1632        let dst_ip = I::TEST_ADDRS.local_ip;
1633        <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1634            core_ctx,
1635            bindings_ctx,
1636            &FakeDeviceId,
1637            I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1638            dst_ip,
1639            reply.clone(),
1640            &mut LocalDeliveryPacketInfo::default(),
1641            None,
1642        )
1643        .unwrap();
1644
1645        let received = core::mem::take(&mut bindings_ctx.state.received);
1646        let ReceivedEchoPacket {
1647            src_ip: got_src_ip,
1648            dst_ip: got_dst_ip,
1649            socket: got_socket,
1650            id: got_id,
1651            data: got_data,
1652        } = assert_matches!(&received[..], [f] => f);
1653        assert_eq!(got_src_ip, &src_ip.get());
1654        assert_eq!(got_dst_ip, &dst_ip.get());
1655        assert_eq!(got_socket, &sock);
1656        assert_eq!(got_id, &ICMP_ID.get());
1657        assert_eq!(&got_data[..], reply.as_ref());
1658    }
1659
1660    #[ip_test(I)]
1661    fn receive_no_socket<I: TestIpExt + IpExt>() {
1662        let mut ctx = FakeIcmpCtx::<I>::default();
1663        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1664        let sock = api.create();
1665
1666        const BIND_ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1667        const OTHER_ICMP_ID: NonZeroU16 = NonZeroU16::new(16).unwrap();
1668
1669        api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(BIND_ICMP_ID))
1670            .unwrap();
1671
1672        let reply = IcmpPacketBuilder::<I, _>::new(
1673            // Use whatever here this is not validated by this module.
1674            I::UNSPECIFIED_ADDRESS,
1675            I::UNSPECIFIED_ADDRESS,
1676            IcmpZeroCode,
1677            IcmpEchoReply::new(OTHER_ICMP_ID.get(), SEQ_NUM),
1678        )
1679        .wrap_body(EmptyBuf)
1680        .serialize_vec_outer(&mut NetworkSerializationContext::default())
1681        .unwrap();
1682
1683        let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1684        <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1685            core_ctx,
1686            bindings_ctx,
1687            &FakeDeviceId,
1688            I::RecvSrcAddr::new(I::TEST_ADDRS.remote_ip.get()).unwrap(),
1689            I::TEST_ADDRS.local_ip,
1690            reply,
1691            &mut LocalDeliveryPacketInfo::default(),
1692            None,
1693        )
1694        .unwrap();
1695        assert_matches!(&bindings_ctx.state.received[..], []);
1696    }
1697
1698    #[ip_test(I)]
1699    fn receive_queue_full<I: TestIpExt + IpExt>() {
1700        let mut ctx = FakeIcmpCtx::<I>::default();
1701        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1702        let sock = api.create();
1703
1704        api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1705
1706        // Simulate a full RX queue.
1707        let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1708        bindings_ctx.state.max_size = 0;
1709
1710        let reply = IcmpPacketBuilder::<I, _>::new(
1711            // Use whatever here this is not validated by this module.
1712            I::UNSPECIFIED_ADDRESS,
1713            I::UNSPECIFIED_ADDRESS,
1714            IcmpZeroCode,
1715            IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1716        )
1717        .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1718        .serialize_vec_outer(&mut NetworkSerializationContext::default())
1719        .unwrap();
1720
1721        let src_ip = I::TEST_ADDRS.remote_ip;
1722        let dst_ip = I::TEST_ADDRS.local_ip;
1723        <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1724            core_ctx,
1725            bindings_ctx,
1726            &FakeDeviceId,
1727            I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1728            dst_ip,
1729            reply,
1730            &mut LocalDeliveryPacketInfo::default(),
1731            None,
1732        )
1733        .unwrap();
1734
1735        assert_eq!(core_ctx.counters().queue_full.get(), 1);
1736    }
1737
1738    #[ip_test(I, test = false)]
1739    #[test_case::test_matrix(
1740        [MarkDomain::Mark1, MarkDomain::Mark2],
1741        [None, Some(0), Some(1)]
1742    )]
1743    fn icmp_socket_marks<I: TestIpExt + IpExt>(domain: MarkDomain, mark: Option<u32>) {
1744        let mut ctx = FakeIcmpCtx::<I>::default();
1745        let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1746        let socket = api.create();
1747
1748        // Doesn't have a mark by default.
1749        assert_eq!(api.get_mark(&socket, domain), Mark(None));
1750
1751        let mark = Mark(mark);
1752        // We can set and get back the mark.
1753        api.set_mark(&socket, domain, mark);
1754        assert_eq!(api.get_mark(&socket, domain), mark);
1755    }
1756
1757    /// A test for the crash seen in https://fxbug.dev/445389479.
1758    ///
1759    /// Because the netstack assumes it could never send an ICMP echo request
1760    /// with an ID of 0, that it would never see an echo request inside an ICMP
1761    /// error with an ID of 0.
1762    #[ip_test(I)]
1763    fn icmp_error_with_inner_icmp_echo_with_id_0<I: TestIpExt + IpExt>() {
1764        let mut ctx = FakeIcmpCtx::<I>::default();
1765
1766        let src_ip = I::TEST_ADDRS.remote_ip;
1767        let dst_ip = I::TEST_ADDRS.local_ip;
1768
1769        let original_body = Buf::new([1, 2, 3, 4], ..)
1770            .wrap_in(IcmpPacketBuilder::<I, _>::new(
1771                *src_ip,
1772                *dst_ip,
1773                IcmpZeroCode,
1774                // The zero below is sorta invalid because we'd never send an
1775                // ICMP echo with an ID of 0.
1776                IcmpEchoRequest::new(0, 1),
1777            ))
1778            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1779            .unwrap()
1780            .unwrap_b();
1781
1782        let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1783        <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
1784            core_ctx,
1785            bindings_ctx,
1786            &FakeDeviceId,
1787            Some(src_ip),
1788            dst_ip,
1789            original_body.as_ref(),
1790            I::map_ip_out(
1791                (),
1792                |()| {
1793                    Icmpv4ErrorCode::DestUnreachable(
1794                        Icmpv4DestUnreachableCode::DestNetworkUnreachable,
1795                        IcmpDestUnreachable::default(),
1796                    )
1797                },
1798                |()| Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::AddrUnreachable),
1799            ),
1800        )
1801    }
1802}