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