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