1use 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
53pub trait IpExt: datagram::IpExt + IcmpIpExt {}
55impl<O: datagram::IpExt + IcmpIpExt> IpExt for O {}
56
57#[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 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#[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 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 #[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 pub fn debug_references(&self) -> impl Debug {
159 let Self(rc) = self;
160 StrongRc::debug_references(rc)
161 }
162
163 pub fn downgrade(&self) -> WeakIcmpSocketId<I, D, BT> {
165 let Self(rc) = self;
166 WeakIcmpSocketId(StrongRc::downgrade(rc))
167 }
168
169 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#[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
219pub type IcmpSocketSet<I, D, BT> = DatagramSocketSet<I, D, Icmp<BT>>;
221pub type IcmpSocketState<I, D, BT> = datagram::SocketState<I, D, Icmp<BT>>;
223pub type IcmpSocketTxMetadata<I, D, BT> = datagram::TxMetadata<I, D, Icmp<BT>>;
225
226pub enum ReceiveIcmpEchoError {
228 QueueFull,
230}
231
232pub trait IcmpEchoBindingsContext<I: IpExt, D: StrongDeviceIdentifier>:
235 IcmpEchoBindingsTypes + ReferenceNotifiers + RngContext + SettingsContext<IcmpEchoSettings>
236{
237 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
249pub trait IcmpEchoBindingsTypes: DatagramBindingsTypes + Sized + 'static {
262 type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
264 type SocketWritableListener: SocketWritableListener + Debug + Send + Sync + 'static;
266}
267
268pub trait IcmpEchoContextMarker {}
272
273pub trait IcmpEchoBoundStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
275 DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
276{
277 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 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
295pub trait IcmpEchoStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
297 DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
298{
299 type SocketStateCtx<'a>: IcmpEchoBoundStateContext<I, BC>
301 + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
302
303 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 fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
313 &mut self,
314 cb: F,
315 ) -> O;
316
317 fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
319 &mut self,
320 cb: F,
321 ) -> O;
322
323 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 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 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
357pub 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 type Counters<I: Ip> = ();
397 type SocketWritableListener = BT::SocketWritableListener;
398
399 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 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 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 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
499pub 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 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 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#[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 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
675pub 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 if addr.iter_shadows().any(|a| socketmap.get(&a).is_some()) {
762 return Err(InsertError::ShadowAddrExists);
763 }
764
765 if socketmap.descendant_counts(&addr).len() != 0 {
768 return Err(InsertError::ShadowerExists);
769 }
770 Ok(())
771 }
772}
773
774pub struct IcmpEchoSocketApi<I: Ip, C>(C, IpVersionMarker<I>);
776
777impl<I: Ip, C> IcmpEchoSocketApi<I, C> {
778 pub fn new(ctx: C) -> Self {
780 Self(ctx, IpVersionMarker::new())
781 }
782}
783
784type 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 + 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 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 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 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 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 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 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 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 pub fn disconnect(
900 &mut self,
901 id: &IcmpApiSocketId<I, C>,
902 ) -> Result<(), datagram::ExpectedConnError> {
903 self.datagram().disconnect_connected(id)
904 }
905
906 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 pub fn get_shutdown(&mut self, id: &IcmpApiSocketId<I, C>) -> Option<ShutdownType> {
917 self.datagram().get_shutdown_connected(id)
918 }
919
920 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 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 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 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 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 pub fn get_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>) -> bool {
961 self.datagram().get_multicast_loop(id)
962 }
963
964 pub fn set_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>, value: bool) {
966 self.datagram().set_multicast_loop(id, value);
967 }
968
969 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 pub fn get_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
976 self.datagram().get_mark(id, domain)
977 }
978
979 pub fn set_send_buffer(&mut self, id: &IcmpApiSocketId<I, C>, size: usize) {
981 self.datagram().set_send_buffer(id, size)
982 }
983
984 pub fn send_buffer(&mut self, id: &IcmpApiSocketId<I, C>) -> usize {
986 self.datagram().send_buffer(id)
987 }
988
989 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 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 pub fn collect_all_sockets(&mut self) -> Vec<IcmpApiSocketId<I, C>> {
1023 self.datagram().collect_all_sockets()
1024 }
1025
1026 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
1042pub 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 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 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 let echo_reply =
1144 buffer.parse::<IcmpPacketRaw<I, _, IcmpEchoReply>>().expect("received non-echo reply");
1145 let Some(id) = NonZeroU16::new(echo_reply.message().id()) else { return Ok(()) };
1147
1148 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 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 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 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 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 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 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 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1670 bindings_ctx.state.max_size = 0;
1671
1672 let reply = IcmpPacketBuilder::<I, _>::new(
1673 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 assert_eq!(api.get_mark(&socket, domain), Mark(None));
1711
1712 let mark = Mark(mark);
1713 api.set_mark(&socket, domain, mark);
1715 assert_eq!(api.get_mark(&socket, domain), mark);
1716 }
1717
1718 #[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 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}