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, Marks, 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, IpLayerIpExt, IpTransportContext, LocalDeliveryPacketInfo,
45 MulticastMembershipHandler, ReceiveIpPacketMeta, SocketMetadata, TransportIpContext,
46};
47use packet::{BufferMut, PacketBuilder, ParsablePacket as _, ParseBuffer};
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 + IpLayerIpExt {}
55impl<O: datagram::IpExt + IcmpIpExt + IpLayerIpExt> 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<CC, I, BT> SocketMetadata<CC> for IcmpSocketId<I, CC::WeakDeviceId, BT>
103where
104 CC: IcmpEchoStateContext<I, BT>,
105 I: IpExt,
106 BT: IcmpEchoBindingsTypes,
107{
108 fn socket_cookie(&self, _core_ctx: &mut CC) -> SocketCookie {
109 self.socket_cookie()
110 }
111 fn marks(&self, core_ctx: &mut CC) -> Marks {
112 core_ctx.with_socket_state(self, |_core_ctx, state| state.options().marks().clone())
113 }
114}
115
116impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Clone
117 for IcmpSocketId<I, D, BT>
118{
119 #[cfg_attr(feature = "instrumented", track_caller)]
120 fn clone(&self) -> Self {
121 let Self(rc) = self;
122 Self(StrongRc::clone(rc))
123 }
124}
125
126impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
127 From<datagram::StrongRc<I, D, Icmp<BT>>> for IcmpSocketId<I, D, BT>
128{
129 fn from(value: datagram::StrongRc<I, D, Icmp<BT>>) -> Self {
130 Self(value)
131 }
132}
133
134impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
135 Borrow<datagram::StrongRc<I, D, Icmp<BT>>> for IcmpSocketId<I, D, BT>
136{
137 fn borrow(&self) -> &datagram::StrongRc<I, D, Icmp<BT>> {
138 let Self(rc) = self;
139 rc
140 }
141}
142
143impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
144 PartialEq<WeakIcmpSocketId<I, D, BT>> for IcmpSocketId<I, D, BT>
145{
146 fn eq(&self, other: &WeakIcmpSocketId<I, D, BT>) -> bool {
147 let Self(rc) = self;
148 let WeakIcmpSocketId(weak) = other;
149 StrongRc::weak_ptr_eq(rc, weak)
150 }
151}
152
153impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Debug
154 for IcmpSocketId<I, D, BT>
155{
156 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 let Self(rc) = self;
158 f.debug_tuple("IcmpSocketId").field(&StrongRc::debug_id(rc)).finish()
159 }
160}
161
162impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
163 #[cfg(any(test, feature = "testutils"))]
166 pub fn state(&self) -> &RwLock<IcmpSocketState<I, D, BT>> {
167 let Self(rc) = self;
168 rc.state()
169 }
170
171 pub fn debug_references(&self) -> impl Debug {
173 let Self(rc) = self;
174 StrongRc::debug_references(rc)
175 }
176
177 pub fn downgrade(&self) -> WeakIcmpSocketId<I, D, BT> {
179 let Self(rc) = self;
180 WeakIcmpSocketId(StrongRc::downgrade(rc))
181 }
182
183 pub fn external_data(&self) -> &BT::ExternalData<I> {
185 let Self(rc) = self;
186 rc.external_data()
187 }
188}
189
190impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
191 DelegatedOrderedLockAccess<IcmpSocketState<I, D, BT>> for IcmpSocketId<I, D, BT>
192{
193 type Inner = datagram::ReferenceState<I, D, Icmp<BT>>;
194 fn delegate_ordered_lock_access(&self) -> &Self::Inner {
195 let Self(rc) = self;
196 &*rc
197 }
198}
199
200#[derive(GenericOverIp, Derivative)]
202#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""), Clone(bound = ""))]
203#[generic_over_ip(I, Ip)]
204pub struct WeakIcmpSocketId<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
205 datagram::WeakRc<I, D, Icmp<BT>>,
206);
207
208impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> PartialEq<IcmpSocketId<I, D, BT>>
209 for WeakIcmpSocketId<I, D, BT>
210{
211 fn eq(&self, other: &IcmpSocketId<I, D, BT>) -> bool {
212 PartialEq::eq(other, self)
213 }
214}
215
216impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> Debug
217 for WeakIcmpSocketId<I, D, BT>
218{
219 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 let Self(rc) = self;
221 f.debug_tuple("WeakIcmpSocketId").field(&rc.debug_id()).finish()
222 }
223}
224
225impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> WeakIcmpSocketId<I, D, BT> {
226 #[cfg_attr(feature = "instrumented", track_caller)]
227 pub fn upgrade(&self) -> Option<IcmpSocketId<I, D, BT>> {
228 let Self(rc) = self;
229 rc.upgrade().map(IcmpSocketId)
230 }
231}
232
233pub type IcmpSocketSet<I, D, BT> = DatagramSocketSet<I, D, Icmp<BT>>;
235pub type IcmpSocketState<I, D, BT> = datagram::SocketState<I, D, Icmp<BT>>;
237pub type IcmpSocketTxMetadata<I, D, BT> = datagram::TxMetadata<I, D, Icmp<BT>>;
239
240pub enum ReceiveIcmpEchoError {
242 QueueFull,
244}
245
246pub trait IcmpEchoBindingsContext<I: IpExt, D: StrongDeviceIdentifier>:
249 IcmpEchoBindingsTypes + ReferenceNotifiers + RngContext + SettingsContext<IcmpEchoSettings>
250{
251 fn receive_icmp_echo_reply<B: BufferMut>(
253 &mut self,
254 conn: &IcmpSocketId<I, D::Weak, Self>,
255 device_id: &D,
256 src_ip: I::Addr,
257 dst_ip: I::Addr,
258 id: u16,
259 data: B,
260 ) -> Result<(), ReceiveIcmpEchoError>;
261}
262
263pub trait IcmpEchoBindingsTypes: DatagramBindingsTypes + Sized + 'static {
276 type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
278 type SocketWritableListener: SocketWritableListener + Debug + Send + Sync + 'static;
280}
281
282pub trait IcmpEchoContextMarker {}
286
287pub trait IcmpEchoBoundStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
289 DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
290{
291 type IpSocketsCtx<'a>: TransportIpContext<I, BC>
293 + MulticastMembershipHandler<I, BC>
294 + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
295 + CounterContext<IcmpRxCounters<I>>
296 + CoreTxMetadataContext<IcmpSocketTxMetadata<I, Self::WeakDeviceId, BC>, BC>;
297
298 fn with_icmp_ctx_and_sockets_mut<
301 O,
302 F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut BoundSockets<I, Self::WeakDeviceId, BC>) -> O,
303 >(
304 &mut self,
305 cb: F,
306 ) -> O;
307}
308
309pub trait IcmpEchoStateContext<I: IcmpIpExt + IpExt, BC: IcmpEchoBindingsTypes>:
311 DeviceIdContext<AnyDevice> + IcmpEchoContextMarker
312{
313 type SocketStateCtx<'a>: IcmpEchoBoundStateContext<I, BC>
315 + DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
316
317 fn with_all_sockets_mut<O, F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
320 &mut self,
321 cb: F,
322 ) -> O;
323
324 fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, BC>) -> O>(
327 &mut self,
328 cb: F,
329 ) -> O;
330
331 fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
333 &mut self,
334 cb: F,
335 ) -> O;
336
337 fn with_socket_state<
340 O,
341 F: FnOnce(&mut Self::SocketStateCtx<'_>, &IcmpSocketState<I, Self::WeakDeviceId, BC>) -> O,
342 >(
343 &mut self,
344 id: &IcmpSocketId<I, Self::WeakDeviceId, BC>,
345 cb: F,
346 ) -> O;
347
348 fn with_socket_state_mut<
350 O,
351 F: FnOnce(&mut Self::SocketStateCtx<'_>, &mut IcmpSocketState<I, Self::WeakDeviceId, BC>) -> O,
352 >(
353 &mut self,
354 id: &IcmpSocketId<I, Self::WeakDeviceId, BC>,
355 cb: F,
356 ) -> O;
357
358 fn for_each_socket<
360 F: FnMut(
361 &mut Self::SocketStateCtx<'_>,
362 &IcmpSocketId<I, Self::WeakDeviceId, BC>,
363 &IcmpSocketState<I, Self::WeakDeviceId, BC>,
364 ),
365 >(
366 &mut self,
367 cb: F,
368 );
369}
370
371pub struct Icmp<BT>(PhantomData<BT>, Never);
373
374impl<BT: IcmpEchoBindingsTypes> DatagramSocketSpec for Icmp<BT> {
375 const NAME: &'static str = "ICMP_ECHO";
376 type AddrSpec = IcmpAddrSpec;
377
378 type SocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = IcmpSocketId<I, D, BT>;
379 type WeakSocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = WeakIcmpSocketId<I, D, BT>;
380
381 type OtherStackIpOptions<I: datagram::IpExt, D: WeakDeviceIdentifier> = ();
382
383 type SharingState = ();
384
385 type SocketMapSpec<I: datagram::IpExt + datagram::DualStackIpExt, D: WeakDeviceIdentifier> =
386 IcmpSocketMapStateSpec<I, D, BT>;
387
388 fn ip_proto<I: IpProtoExt>() -> I::Proto {
389 I::map_ip((), |()| Ipv4Proto::Icmp, |()| Ipv6Proto::Icmpv6)
390 }
391
392 fn make_bound_socket_map_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
393 s: &Self::SocketId<I, D>,
394 ) -> <Self::SocketMapSpec<I, D> as datagram::DatagramSocketMapSpec<
395 I,
396 D,
397 Self::AddrSpec,
398 >>::BoundSocketId{
399 s.clone()
400 }
401
402 type Serializer<I: datagram::IpExt, B: BufferMut> =
403 packet::Nested<B, IcmpPacketBuilder<I, IcmpEchoRequest>>;
404 type SerializeError = packet_formats::error::ParseError;
405
406 type ExternalData<I: Ip> = BT::ExternalData<I>;
407 type Settings = IcmpEchoSettings;
408
409 type Counters<I: Ip> = ();
411 type SocketWritableListener = BT::SocketWritableListener;
412
413 const FIXED_HEADER_SIZE: usize = 0;
417
418 fn make_packet<I: datagram::IpExt, B: BufferMut>(
419 mut body: B,
420 addr: &socket::ConnIpAddr<
421 I::Addr,
422 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
423 <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
424 >,
425 ) -> Result<Self::Serializer<I, B>, Self::SerializeError> {
426 let ConnIpAddr { local: (local_ip, id), remote: (remote_ip, ()) } = addr;
427 let icmp_echo: packet_formats::icmp::IcmpPacketRaw<I, &[u8], IcmpEchoRequest> =
428 body.parse()?;
429 debug!(
430 "preparing ICMP echo request {local_ip} to {remote_ip}: id={}, seq={}",
431 id,
432 icmp_echo.message().seq()
433 );
434 let icmp_builder = IcmpPacketBuilder::<I, _>::new(
435 local_ip.addr(),
436 remote_ip.addr(),
437 packet_formats::icmp::IcmpZeroCode,
438 IcmpEchoRequest::new(id.get(), icmp_echo.message().seq()),
439 );
440 Ok(icmp_builder.wrap_body(body))
441 }
442
443 fn try_alloc_listen_identifier<I: datagram::IpExt, D: WeakDeviceIdentifier>(
444 bindings_ctx: &mut impl RngContext,
445 is_available: impl Fn(
446 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
447 ) -> Result<(), datagram::InUseError>,
448 ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
449 let mut port = IcmpPortAlloc::<I, D, BT>::rand_ephemeral(&mut bindings_ctx.rng());
450 for _ in IcmpPortAlloc::<I, D, BT>::EPHEMERAL_RANGE {
451 let tryport = NonZeroU16::new(port.get()).unwrap();
454 match is_available(tryport) {
455 Ok(()) => return Some(tryport),
456 Err(datagram::InUseError {}) => port.next(),
457 }
458 }
459 None
460 }
461
462 type ListenerIpAddr<I: datagram::IpExt> = socket::ListenerIpAddr<I::Addr, NonZeroU16>;
463
464 type ConnIpAddr<I: datagram::IpExt> = ConnIpAddr<
465 I::Addr,
466 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
467 <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
468 >;
469
470 type ConnState<I: datagram::IpExt, D: WeakDeviceIdentifier> = datagram::ConnState<I, D, Self>;
471 type ConnStateExtra = u16;
475
476 fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
477 state: &Self::ConnState<I, D>,
478 ) -> datagram::ConnInfo<I::Addr, D> {
479 let ConnAddr { ip, device } = state.addr();
480 let extra = state.extra();
481 let ConnInfoAddr { local: (local_ip, local_identifier), remote: (remote_ip, ()) } =
482 ip.clone().into();
483 datagram::ConnInfo::new(local_ip, local_identifier, remote_ip, *extra, || {
484 device.clone().expect("device must be bound for addresses that require zones")
486 })
487 }
488
489 fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
490 bound: &IcmpBoundSockets<I, D, BT>,
491 bindings_ctx: &mut BC,
492 flow: datagram::DatagramFlowId<I::Addr, ()>,
493 ) -> Option<NonZeroU16> {
494 let mut rng = bindings_ctx.rng();
495 netstack3_base::simple_randomized_port_alloc(&mut rng, &flow, &IcmpPortAlloc(bound), &())
496 .map(|p| NonZeroU16::new(p).expect("ephemeral ports should be non-zero"))
497 }
498
499 fn upgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
500 id: &Self::WeakSocketId<I, D>,
501 ) -> Option<Self::SocketId<I, D>> {
502 id.upgrade()
503 }
504
505 fn downgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
506 id: &Self::SocketId<I, D>,
507 ) -> Self::WeakSocketId<I, D> {
508 IcmpSocketId::downgrade(id)
509 }
510}
511
512pub enum IcmpAddrSpec {}
514
515impl SocketMapAddrSpec for IcmpAddrSpec {
516 type RemoteIdentifier = ();
517 type LocalIdentifier = NonZeroU16;
518}
519
520type IcmpBoundSockets<I, D, BT> = datagram::BoundDatagramSocketMap<I, D, Icmp<BT>>;
521
522struct IcmpPortAlloc<'a, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
523 &'a IcmpBoundSockets<I, D, BT>,
524);
525
526impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> PortAllocImpl
527 for IcmpPortAlloc<'_, I, D, BT>
528{
529 const EPHEMERAL_RANGE: core::ops::RangeInclusive<u16> = 1..=u16::MAX;
530 type Id = DatagramFlowId<I::Addr, ()>;
531 type PortAvailableArg = ();
532
533 fn is_port_available(&self, id: &Self::Id, port: u16, (): &()) -> bool {
534 let Self(socketmap) = self;
535 let port = NonZeroU16::new(port).unwrap();
538 let conn = ConnAddr {
539 ip: ConnIpAddr { local: (id.local_ip, port), remote: (id.remote_ip, ()) },
540 device: None,
541 };
542
543 AddrVec::from(conn).iter_shadows().all(|a| match &a {
546 AddrVec::Listen(l) => socketmap.listeners().get_by_addr(&l).is_none(),
547 AddrVec::Conn(c) => socketmap.conns().get_by_addr(&c).is_none(),
548 } && socketmap.get_shadower_counts(&a) == 0)
549 }
550}
551
552#[derive(Derivative)]
554#[derivative(Default(bound = ""))]
555pub struct BoundSockets<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> {
556 pub(crate) socket_map: IcmpBoundSockets<I, D, BT>,
557}
558
559impl<I, BC, CC> NonDualStackDatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
560where
561 I: IpExt + datagram::DualStackIpExt,
562 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
563 CC: DeviceIdContext<AnyDevice> + IcmpEchoContextMarker,
564{
565 fn nds_converter(_core_ctx: &CC) -> impl NonDualStackConverter<I, CC::WeakDeviceId, Self> {
566 ()
567 }
568}
569
570impl<I, BC, CC> DatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
571where
572 I: IpExt + datagram::DualStackIpExt,
573 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
574 CC: IcmpEchoBoundStateContext<I, BC> + IcmpEchoContextMarker,
575{
576 type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
577
578 type DualStackContext = UninstantiableWrapper<CC>;
580
581 type NonDualStackContext = CC;
582
583 fn with_bound_sockets<
584 O,
585 F: FnOnce(&mut Self::IpSocketsCtx<'_>, &IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
586 >(
587 core_ctx: &mut CC,
588 cb: F,
589 ) -> O {
590 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
591 core_ctx,
592 |ctx, BoundSockets { socket_map }| cb(ctx, &socket_map),
593 )
594 }
595
596 fn with_bound_sockets_mut<
597 O,
598 F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
599 >(
600 core_ctx: &mut CC,
601 cb: F,
602 ) -> O {
603 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
604 core_ctx,
605 |ctx, BoundSockets { socket_map }| cb(ctx, socket_map),
606 )
607 }
608
609 fn dual_stack_context(
610 core_ctx: &CC,
611 ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
612 MaybeDualStack::NotDualStack(core_ctx)
613 }
614
615 fn dual_stack_context_mut(
616 core_ctx: &mut CC,
617 ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
618 MaybeDualStack::NotDualStack(core_ctx)
619 }
620
621 fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
622 core_ctx: &mut CC,
623 cb: F,
624 ) -> O {
625 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(core_ctx, |ctx, _sockets| cb(ctx))
626 }
627}
628
629impl<I, BC, CC> DatagramSpecStateContext<I, CC, BC> for Icmp<BC>
630where
631 I: IpExt + datagram::DualStackIpExt,
632 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
633 CC: IcmpEchoStateContext<I, BC>,
634{
635 type SocketsStateCtx<'a> = CC::SocketStateCtx<'a>;
636
637 fn with_all_sockets_mut<O, F: FnOnce(&mut IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
638 core_ctx: &mut CC,
639 cb: F,
640 ) -> O {
641 IcmpEchoStateContext::with_all_sockets_mut(core_ctx, cb)
642 }
643
644 fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
645 core_ctx: &mut CC,
646 cb: F,
647 ) -> O {
648 IcmpEchoStateContext::with_all_sockets(core_ctx, cb)
649 }
650
651 fn with_socket_state<
652 O,
653 F: FnOnce(&mut Self::SocketsStateCtx<'_>, &IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
654 >(
655 core_ctx: &mut CC,
656 id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
657 cb: F,
658 ) -> O {
659 IcmpEchoStateContext::with_socket_state(core_ctx, id, cb)
660 }
661
662 fn with_socket_state_mut<
663 O,
664 F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
665 >(
666 core_ctx: &mut CC,
667 id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
668 cb: F,
669 ) -> O {
670 IcmpEchoStateContext::with_socket_state_mut(core_ctx, id, cb)
671 }
672
673 fn for_each_socket<
674 F: FnMut(
675 &mut Self::SocketsStateCtx<'_>,
676 &IcmpSocketId<I, CC::WeakDeviceId, BC>,
677 &IcmpSocketState<I, CC::WeakDeviceId, BC>,
678 ),
679 >(
680 core_ctx: &mut CC,
681 cb: F,
682 ) {
683 IcmpEchoStateContext::for_each_socket(core_ctx, cb)
684 }
685}
686
687pub struct IcmpSocketMapStateSpec<I, D, BT>(PhantomData<(I, D, BT)>, Never);
690
691impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapStateSpec
692 for IcmpSocketMapStateSpec<I, D, BT>
693{
694 type ListenerId = IcmpSocketId<I, D, BT>;
695 type ConnId = IcmpSocketId<I, D, BT>;
696
697 type AddrVecTag = ();
698
699 type ListenerSharingState = ();
700 type ConnSharingState = ();
701
702 type ListenerAddrState = Self::ListenerId;
703
704 type ConnAddrState = Self::ConnId;
705 fn listener_tag(
706 ListenerAddrInfo { has_device: _, specified_addr: _ }: ListenerAddrInfo,
707 _state: &Self::ListenerAddrState,
708 ) -> Self::AddrVecTag {
709 ()
710 }
711 fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
712 ()
713 }
714}
715
716impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapAddrStateSpec
717 for IcmpSocketId<I, D, BT>
718{
719 type Id = Self;
720
721 type SharingState = ();
722
723 type Inserter<'a>
724 = core::convert::Infallible
725 where
726 Self: 'a;
727
728 fn new(_new_sharing_state: &Self::SharingState, id: Self::Id) -> Self {
729 id
730 }
731
732 fn contains_id(&self, id: &Self::Id) -> bool {
733 self == id
734 }
735
736 fn try_get_inserter<'a, 'b>(
737 &'b mut self,
738 _new_sharing_state: &'a Self::SharingState,
739 ) -> Result<Self::Inserter<'b>, IncompatibleError> {
740 Err(IncompatibleError)
741 }
742
743 fn could_insert(
744 &self,
745 _new_sharing_state: &Self::SharingState,
746 ) -> Result<(), IncompatibleError> {
747 Err(IncompatibleError)
748 }
749
750 fn remove_by_id(&mut self, _id: Self::Id) -> socket::RemoveResult {
751 socket::RemoveResult::IsLast
752 }
753}
754
755impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
756 DatagramSocketMapSpec<I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
757{
758 type BoundSocketId = IcmpSocketId<I, D, BT>;
759}
760
761impl<AA, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
762 SocketMapConflictPolicy<AA, (), I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
763where
764 AA: Into<AddrVec<I, D, IcmpAddrSpec>> + Clone,
765{
766 fn check_insert_conflicts(
767 _new_sharing_state: &(),
768 addr: &AA,
769 socketmap: &SocketMap<AddrVec<I, D, IcmpAddrSpec>, socket::Bound<Self>>,
770 ) -> Result<(), socket::InsertError> {
771 let addr: AddrVec<_, _, _> = addr.clone().into();
772 if addr.iter_shadows().any(|a| socketmap.get(&a).is_some()) {
774 return Err(InsertError::ShadowAddrExists);
775 }
776
777 if socketmap.descendant_counts(&addr).len() != 0 {
780 return Err(InsertError::ShadowerExists);
781 }
782 Ok(())
783 }
784}
785
786pub struct IcmpEchoSocketApi<I: Ip, C>(C, IpVersionMarker<I>);
788
789impl<I: Ip, C> IcmpEchoSocketApi<I, C> {
790 pub fn new(ctx: C) -> Self {
792 Self(ctx, IpVersionMarker::new())
793 }
794}
795
796type IcmpApiSocketId<I, C> = IcmpSocketId<
801 I,
802 <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
803 <C as ContextPair>::BindingsContext,
804>;
805
806impl<I, C> IcmpEchoSocketApi<I, C>
807where
808 I: datagram::IpExt,
809 C: ContextPair,
810 C::CoreContext: IcmpEchoStateContext<I, C::BindingsContext>
811 + DatagramStateContext<I, C::BindingsContext, Icmp<C::BindingsContext>>,
814 C::BindingsContext:
815 IcmpEchoBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
816{
817 fn core_ctx(&mut self) -> &mut C::CoreContext {
818 let Self(pair, IpVersionMarker { .. }) = self;
819 pair.core_ctx()
820 }
821
822 fn datagram(&mut self) -> &mut DatagramApi<I, C, Icmp<C::BindingsContext>> {
823 let Self(pair, IpVersionMarker { .. }) = self;
824 DatagramApi::wrap(pair)
825 }
826
827 pub fn create(&mut self) -> IcmpApiSocketId<I, C>
829 where
830 <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>: Default,
831 <C::BindingsContext as IcmpEchoBindingsTypes>::SocketWritableListener: Default,
832 {
833 self.create_with(Default::default(), Default::default())
834 }
835
836 pub fn create_with(
838 &mut self,
839 external_data: <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
840 writable_listener: <C::BindingsContext as IcmpEchoBindingsTypes>::SocketWritableListener,
841 ) -> IcmpApiSocketId<I, C> {
842 self.datagram().create(external_data, writable_listener)
843 }
844
845 pub fn connect(
849 &mut self,
850 id: &IcmpApiSocketId<I, C>,
851 remote_ip: Option<
852 ZonedAddr<
853 SpecifiedAddr<I::Addr>,
854 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
855 >,
856 >,
857 remote_id: u16,
858 ) -> Result<(), datagram::ConnectError> {
859 self.datagram().connect(id, remote_ip, (), remote_id)
860 }
861
862 pub fn bind(
867 &mut self,
868 id: &IcmpApiSocketId<I, C>,
869 local_ip: Option<
870 ZonedAddr<
871 SpecifiedAddr<I::Addr>,
872 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
873 >,
874 >,
875 icmp_id: Option<NonZeroU16>,
876 ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
877 self.datagram().listen(id, local_ip, icmp_id)
878 }
879
880 pub fn get_info(
882 &mut self,
883 id: &IcmpApiSocketId<I, C>,
884 ) -> datagram::SocketInfo<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>
885 {
886 self.datagram().get_info(id)
887 }
888
889 pub fn set_device(
895 &mut self,
896 id: &IcmpApiSocketId<I, C>,
897 device_id: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
898 ) -> Result<(), SocketError> {
899 self.datagram().set_device(id, device_id)
900 }
901
902 pub fn get_bound_device(
904 &mut self,
905 id: &IcmpApiSocketId<I, C>,
906 ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
907 self.datagram().get_bound_device(id)
908 }
909
910 pub fn disconnect(
912 &mut self,
913 id: &IcmpApiSocketId<I, C>,
914 ) -> Result<(), datagram::ExpectedConnError> {
915 self.datagram().disconnect_connected(id)
916 }
917
918 pub fn shutdown(
920 &mut self,
921 id: &IcmpApiSocketId<I, C>,
922 shutdown_type: ShutdownType,
923 ) -> Result<(), datagram::ExpectedConnError> {
924 self.datagram().shutdown_connected(id, shutdown_type)
925 }
926
927 pub fn get_shutdown(&mut self, id: &IcmpApiSocketId<I, C>) -> Option<ShutdownType> {
929 self.datagram().get_shutdown_connected(id)
930 }
931
932 pub fn close(
934 &mut self,
935 id: IcmpApiSocketId<I, C>,
936 ) -> RemoveResourceResultWithContext<
937 <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
938 C::BindingsContext,
939 > {
940 self.datagram().close(id)
941 }
942
943 pub fn get_unicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
945 self.datagram().get_ip_hop_limits(id).unicast
946 }
947
948 pub fn get_multicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
950 self.datagram().get_ip_hop_limits(id).multicast
951 }
952
953 pub fn set_unicast_hop_limit(
955 &mut self,
956 id: &IcmpApiSocketId<I, C>,
957 hop_limit: Option<NonZeroU8>,
958 ) {
959 self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_unicast(hop_limit))
960 }
961
962 pub fn set_multicast_hop_limit(
964 &mut self,
965 id: &IcmpApiSocketId<I, C>,
966 hop_limit: Option<NonZeroU8>,
967 ) {
968 self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_multicast(hop_limit))
969 }
970
971 pub fn get_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>) -> bool {
973 self.datagram().get_multicast_loop(id)
974 }
975
976 pub fn set_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>, value: bool) {
978 self.datagram().set_multicast_loop(id, value);
979 }
980
981 pub fn set_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain, mark: Mark) {
983 self.datagram().set_mark(id, domain, mark)
984 }
985
986 pub fn get_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
988 self.datagram().get_mark(id, domain)
989 }
990
991 pub fn set_send_buffer(&mut self, id: &IcmpApiSocketId<I, C>, size: usize) {
993 self.datagram().set_send_buffer(id, size)
994 }
995
996 pub fn send_buffer(&mut self, id: &IcmpApiSocketId<I, C>) -> usize {
998 self.datagram().send_buffer(id)
999 }
1000
1001 pub fn send<B: BufferMut>(
1005 &mut self,
1006 id: &IcmpApiSocketId<I, C>,
1007 body: B,
1008 ) -> Result<(), datagram::SendError<packet_formats::error::ParseError>> {
1009 self.datagram().send_conn(id, body)
1010 }
1011
1012 pub fn send_to<B: BufferMut>(
1016 &mut self,
1017 id: &IcmpApiSocketId<I, C>,
1018 remote_ip: Option<
1019 ZonedAddr<
1020 SpecifiedAddr<I::Addr>,
1021 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
1022 >,
1023 >,
1024 body: B,
1025 ) -> Result<
1026 (),
1027 either::Either<LocalAddressError, datagram::SendToError<packet_formats::error::ParseError>>,
1028 > {
1029 self.datagram().send_to(id, remote_ip, (), body)
1030 }
1031
1032 pub fn collect_all_sockets(&mut self) -> Vec<IcmpApiSocketId<I, C>> {
1035 self.datagram().collect_all_sockets()
1036 }
1037
1038 pub fn inspect<N>(&mut self, inspector: &mut N)
1040 where
1041 N: Inspector
1042 + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1043 for<'a> N::ChildInspector<'a>:
1044 InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1045 {
1046 DatagramStateContext::for_each_socket(self.core_ctx(), |_ctx, socket_id, socket_state| {
1047 inspector.record_debug_child(socket_id, |inspector| {
1048 socket_state.record_common_info(inspector);
1049 });
1050 });
1051 }
1052}
1053
1054pub enum IcmpEchoIpTransportContext {}
1060
1061impl EchoTransportContextMarker for IcmpEchoIpTransportContext {}
1062
1063impl<I: IpExt, BC: IcmpEchoBindingsContext<I, CC::DeviceId>, CC: IcmpEchoBoundStateContext<I, BC>>
1064 IpTransportContext<I, BC, CC> for IcmpEchoIpTransportContext
1065{
1066 type EarlyDemuxSocket = Never;
1067
1068 fn early_demux<B: ParseBuffer>(
1069 _core_ctx: &mut CC,
1070 _device: &CC::DeviceId,
1071 _src_ip: I::Addr,
1072 _dst_ip: I::Addr,
1073 _buffer: B,
1074 ) -> Option<Self::EarlyDemuxSocket> {
1075 None
1076 }
1077
1078 fn receive_icmp_error(
1079 core_ctx: &mut CC,
1080 _bindings_ctx: &mut BC,
1081 _device: &CC::DeviceId,
1082 original_src_ip: Option<SpecifiedAddr<I::Addr>>,
1083 original_dst_ip: SpecifiedAddr<I::Addr>,
1084 mut original_body: &[u8],
1085 err: I::ErrorCode,
1086 ) {
1087 let echo_request = original_body
1088 .parse::<IcmpPacketRaw<I, _, IcmpEchoRequest>>()
1089 .expect("received non-echo request");
1090
1091 let original_src_ip = match original_src_ip {
1092 Some(ip) => ip,
1093 None => {
1094 trace!("IcmpIpTransportContext::receive_icmp_error: unspecified source IP address");
1095 return;
1096 }
1097 };
1098 let original_src_ip: SocketIpAddr<_> = match original_src_ip.try_into() {
1099 Ok(ip) => ip,
1100 Err(AddrIsMappedError {}) => {
1101 trace!("IcmpIpTransportContext::receive_icmp_error: mapped source IP address");
1102 return;
1103 }
1104 };
1105 let original_dst_ip: SocketIpAddr<_> = match original_dst_ip.try_into() {
1106 Ok(ip) => ip,
1107 Err(AddrIsMappedError {}) => {
1108 trace!("IcmpIpTransportContext::receive_icmp_error: mapped destination IP address");
1109 return;
1110 }
1111 };
1112
1113 let Some(id) = NonZeroU16::new(echo_request.message().id()) else {
1116 debug!(
1117 "ICMP received ICMP error {:?} from {:?}, to {:?} with an ID of 0",
1118 err, original_dst_ip, original_src_ip,
1119 );
1120 return;
1121 };
1122
1123 core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1124 if let Some(conn) = sockets.socket_map.conns().get_by_addr(&ConnAddr {
1125 ip: ConnIpAddr { local: (original_src_ip, id), remote: (original_dst_ip, ()) },
1126 device: None,
1127 }) {
1128 debug!(
1131 "ICMP received ICMP error {:?} from {:?}, to {:?} on socket {:?}",
1132 err, original_dst_ip, original_src_ip, conn
1133 );
1134 CounterContext::<IcmpRxCounters<I>>::counters(core_ctx)
1135 .error_delivered_to_socket
1136 .increment()
1137 } else {
1138 trace!(
1139 "IcmpIpTransportContext::receive_icmp_error: Got ICMP error message for \
1140 nonexistent ICMP echo socket; either the socket responsible has since been \
1141 removed, or the error message was sent in error or corrupted"
1142 );
1143 }
1144 })
1145 }
1146
1147 fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
1148 core_ctx: &mut CC,
1149 bindings_ctx: &mut BC,
1150 device: &CC::DeviceId,
1151 src_ip: I::RecvSrcAddr,
1152 dst_ip: SpecifiedAddr<I::Addr>,
1153 mut buffer: B,
1154 info: &LocalDeliveryPacketInfo<I, H>,
1155 _early_demux_socket: Option<Never>,
1156 ) -> Result<(), (B, I::IcmpError)> {
1157 let LocalDeliveryPacketInfo { meta, header_info: _, marks: _ } = info;
1158 let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
1159 if let Some(delivery) = transparent_override.as_ref() {
1160 unreachable!(
1161 "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1162 transparent proxy rules can only be configured for TCP and UDP packets"
1163 );
1164 }
1165 let echo_reply =
1169 buffer.parse::<IcmpPacketRaw<I, _, IcmpEchoReply>>().expect("received non-echo reply");
1170 let Some(id) = NonZeroU16::new(echo_reply.message().id()) else { return Ok(()) };
1172
1173 let meta = echo_reply.parse_metadata();
1175 buffer.undo_parse(meta);
1176
1177 let src_ip = match SpecifiedAddr::new(src_ip.into_addr()) {
1178 Some(src_ip) => src_ip,
1179 None => {
1180 trace!("receive_icmp_echo_reply: unspecified source address");
1181 return Ok(());
1182 }
1183 };
1184 let src_ip: SocketIpAddr<_> = match src_ip.try_into() {
1185 Ok(src_ip) => src_ip,
1186 Err(AddrIsMappedError {}) => {
1187 trace!("receive_icmp_echo_reply: mapped source address");
1188 return Ok(());
1189 }
1190 };
1191 let dst_ip: SocketIpAddr<_> = match dst_ip.try_into() {
1192 Ok(dst_ip) => dst_ip,
1193 Err(AddrIsMappedError {}) => {
1194 trace!("receive_icmp_echo_reply: mapped destination address");
1195 return Ok(());
1196 }
1197 };
1198
1199 core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1200 let mut addrs_to_search = AddrVecIter::<I, CC::WeakDeviceId, IcmpAddrSpec>::with_device(
1201 ConnIpAddr { local: (dst_ip, id), remote: (src_ip, ()) }.into(),
1202 device.downgrade(),
1203 );
1204 let socket = match addrs_to_search.try_for_each(|addr_vec| {
1205 match addr_vec {
1206 AddrVec::Conn(c) => {
1207 if let Some(id) = sockets.socket_map.conns().get_by_addr(&c) {
1208 return ControlFlow::Break(id);
1209 }
1210 }
1211 AddrVec::Listen(l) => {
1212 if let Some(id) = sockets.socket_map.listeners().get_by_addr(&l) {
1213 return ControlFlow::Break(id);
1214 }
1215 }
1216 }
1217 ControlFlow::Continue(())
1218 }) {
1219 ControlFlow::Continue(()) => None,
1220 ControlFlow::Break(id) => Some(id),
1221 };
1222 if let Some(socket) = socket {
1223 trace!("receive_icmp_echo_reply: Received echo reply for local socket");
1224 match bindings_ctx.receive_icmp_echo_reply(
1225 socket,
1226 device,
1227 src_ip.addr(),
1228 dst_ip.addr(),
1229 id.get(),
1230 buffer,
1231 ) {
1232 Ok(()) => {}
1233 Err(ReceiveIcmpEchoError::QueueFull) => {
1234 core_ctx.counters().queue_full.increment();
1235 }
1236 }
1237 return;
1238 }
1239 trace!("receive_icmp_echo_reply: Received echo reply with no local socket");
1256 });
1257 Ok(())
1258 }
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263 use alloc::rc::Rc;
1264 use alloc::vec;
1265 use core::cell::RefCell;
1266 use core::ops::{Deref, DerefMut};
1267
1268 use assert_matches::assert_matches;
1269 use ip_test_macro::ip_test;
1270 use net_declare::net_ip_v6;
1271 use net_types::Witness;
1272 use net_types::ip::Ipv6;
1273 use netstack3_base::socket::StrictlyZonedAddr;
1274 use netstack3_base::testutil::{
1275 FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSocketWritableListener, FakeWeakDeviceId,
1276 TestIpExt,
1277 };
1278 use netstack3_base::{CtxPair, Icmpv4ErrorCode, Icmpv6ErrorCode};
1279 use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx, InnerFakeIpSocketCtx};
1280 use netstack3_ip::{LocalDeliveryPacketInfo, SendIpPacketMeta};
1281 use packet::{Buf, EmptyBuf, Serializer};
1282 use packet_formats::icmp::{
1283 IcmpDestUnreachable, IcmpPacket, IcmpParseArgs, IcmpZeroCode, Icmpv4DestUnreachableCode,
1284 Icmpv6DestUnreachableCode,
1285 };
1286
1287 use super::*;
1288
1289 const REMOTE_ID: u16 = 27;
1290 const ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1291 const SEQ_NUM: u16 = 0xF0;
1292
1293 impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
1295 fn get(&self) -> impl Deref<Target = IcmpSocketState<I, D, BT>> + '_ {
1296 self.state().read()
1297 }
1298
1299 fn get_mut(&self) -> impl DerefMut<Target = IcmpSocketState<I, D, BT>> + '_ {
1300 self.state().write()
1301 }
1302 }
1303
1304 struct FakeIcmpCoreCtxState<I: IpExt> {
1305 bound_sockets:
1306 Rc<RefCell<BoundSockets<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>>>,
1307 all_sockets: IcmpSocketSet<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1308 ip_socket_ctx: FakeIpSocketCtx<I, FakeDeviceId>,
1309 rx_counters: IcmpRxCounters<I>,
1310 }
1311
1312 impl<I: IpExt> InnerFakeIpSocketCtx<I, FakeDeviceId> for FakeIcmpCoreCtxState<I> {
1313 fn fake_ip_socket_ctx_mut(&mut self) -> &mut FakeIpSocketCtx<I, FakeDeviceId> {
1314 &mut self.ip_socket_ctx
1315 }
1316 }
1317
1318 impl<I: IpExt + TestIpExt> Default for FakeIcmpCoreCtxState<I> {
1319 fn default() -> Self {
1320 Self {
1321 bound_sockets: Default::default(),
1322 all_sockets: Default::default(),
1323 ip_socket_ctx: FakeIpSocketCtx::new(core::iter::once(FakeDeviceConfig {
1324 device: FakeDeviceId,
1325 local_ips: vec![I::TEST_ADDRS.local_ip],
1326 remote_ips: vec![I::TEST_ADDRS.remote_ip],
1327 })),
1328 rx_counters: Default::default(),
1329 }
1330 }
1331 }
1332
1333 type FakeIcmpCoreCtx<I> = FakeCoreCtx<
1334 FakeIcmpCoreCtxState<I>,
1335 SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
1336 FakeDeviceId,
1337 >;
1338 type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<(), (), FakeIcmpBindingsCtxState<I>, ()>;
1339 type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
1340
1341 #[derive(Derivative)]
1342 #[derivative(Default)]
1343
1344 struct FakeIcmpBindingsCtxState<I: IpExt> {
1345 received: Vec<ReceivedEchoPacket<I>>,
1346 #[derivative(Default(value = "usize::MAX"))]
1347 max_size: usize,
1348 }
1349
1350 #[derive(Debug)]
1351 struct ReceivedEchoPacket<I: IpExt> {
1352 src_ip: I::Addr,
1353 dst_ip: I::Addr,
1354 socket: IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1355 id: u16,
1356 data: Vec<u8>,
1357 }
1358
1359 impl<I: IpExt> IcmpEchoContextMarker for FakeIcmpCoreCtx<I> {}
1360
1361 impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtxState<I> {
1362 fn counters(&self) -> &IcmpRxCounters<I> {
1363 &self.rx_counters
1364 }
1365 }
1366
1367 impl<I: IpExt> IcmpEchoBoundStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1368 type IpSocketsCtx<'a> = Self;
1369
1370 fn with_icmp_ctx_and_sockets_mut<
1371 O,
1372 F: FnOnce(
1373 &mut Self::IpSocketsCtx<'_>,
1374 &mut BoundSockets<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1375 ) -> O,
1376 >(
1377 &mut self,
1378 cb: F,
1379 ) -> O {
1380 let bound_sockets = self.state.bound_sockets.clone();
1381 let mut bound_sockets = bound_sockets.borrow_mut();
1382 cb(self, &mut bound_sockets)
1383 }
1384 }
1385
1386 impl<I: IpExt> IcmpEchoStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1387 type SocketStateCtx<'a> = Self;
1388
1389 fn with_all_sockets_mut<
1390 O,
1391 F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1392 >(
1393 &mut self,
1394 cb: F,
1395 ) -> O {
1396 cb(&mut self.state.all_sockets)
1397 }
1398
1399 fn with_all_sockets<
1400 O,
1401 F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1402 >(
1403 &mut self,
1404 cb: F,
1405 ) -> O {
1406 cb(&self.state.all_sockets)
1407 }
1408
1409 fn with_socket_state<
1410 O,
1411 F: FnOnce(
1412 &mut Self::SocketStateCtx<'_>,
1413 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1414 ) -> O,
1415 >(
1416 &mut self,
1417 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1418 cb: F,
1419 ) -> O {
1420 cb(self, &id.get())
1421 }
1422
1423 fn with_socket_state_mut<
1424 O,
1425 F: FnOnce(
1426 &mut Self::SocketStateCtx<'_>,
1427 &mut IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1428 ) -> O,
1429 >(
1430 &mut self,
1431 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1432 cb: F,
1433 ) -> O {
1434 cb(self, &mut id.get_mut())
1435 }
1436
1437 fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
1438 &mut self,
1439 cb: F,
1440 ) -> O {
1441 cb(self)
1442 }
1443
1444 fn for_each_socket<
1445 F: FnMut(
1446 &mut Self::SocketStateCtx<'_>,
1447 &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1448 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1449 ),
1450 >(
1451 &mut self,
1452 mut cb: F,
1453 ) {
1454 let socks = self
1455 .state
1456 .all_sockets
1457 .keys()
1458 .map(|id| IcmpSocketId::from(id.clone()))
1459 .collect::<Vec<_>>();
1460 for id in socks {
1461 cb(self, &id, &id.get());
1462 }
1463 }
1464 }
1465
1466 impl<I: IpExt> IcmpEchoBindingsContext<I, FakeDeviceId> for FakeIcmpBindingsCtx<I> {
1467 fn receive_icmp_echo_reply<B: BufferMut>(
1468 &mut self,
1469 socket: &IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1470 _device_id: &FakeDeviceId,
1471 src_ip: I::Addr,
1472 dst_ip: I::Addr,
1473 id: u16,
1474 data: B,
1475 ) -> Result<(), ReceiveIcmpEchoError> {
1476 if self.state.received.len() < self.state.max_size {
1477 self.state.received.push(ReceivedEchoPacket {
1478 src_ip,
1479 dst_ip,
1480 id,
1481 data: data.to_flattened_vec(),
1482 socket: socket.clone(),
1483 });
1484 Ok(())
1485 } else {
1486 Err(ReceiveIcmpEchoError::QueueFull)
1487 }
1488 }
1489 }
1490
1491 impl<I: IpExt> IcmpEchoBindingsTypes for FakeIcmpBindingsCtx<I> {
1492 type ExternalData<II: Ip> = ();
1493 type SocketWritableListener = FakeSocketWritableListener;
1494 }
1495
1496 #[test]
1497 fn test_connect_dual_stack_fails() {
1498 let mut ctx = FakeIcmpCtx::<Ipv6>::default();
1501 let mut api = IcmpEchoSocketApi::<Ipv6, _>::new(ctx.as_mut());
1502 let conn = api.create();
1503 assert_eq!(
1504 api.connect(
1505 &conn,
1506 Some(ZonedAddr::Unzoned(
1507 SpecifiedAddr::new(net_ip_v6!("::ffff:192.0.2.1")).unwrap(),
1508 )),
1509 REMOTE_ID,
1510 ),
1511 Err(datagram::ConnectError::RemoteUnexpectedlyMapped)
1512 );
1513 }
1514
1515 #[ip_test(I)]
1516 fn send_invalid_icmp_echo<I: TestIpExt + IpExt>() {
1517 let mut ctx = FakeIcmpCtx::<I>::default();
1518 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1519 let conn = api.create();
1520 api.connect(&conn, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1521
1522 let pb = IcmpPacketBuilder::<I, _>::new(
1523 I::TEST_ADDRS.local_ip.get(),
1524 I::TEST_ADDRS.remote_ip.get(),
1525 IcmpZeroCode,
1526 packet_formats::icmp::IcmpEchoReply::new(0, 1),
1527 );
1528 let buf =
1529 pb.wrap_body(Buf::new(Vec::new(), ..)).serialize_vec_outer().unwrap().into_inner();
1530 assert_matches!(
1531 api.send(&conn, buf),
1532 Err(datagram::SendError::SerializeError(
1533 packet_formats::error::ParseError::NotExpected
1534 ))
1535 );
1536 }
1537
1538 #[ip_test(I)]
1539 fn get_info<I: TestIpExt + IpExt>() {
1540 let mut ctx = FakeIcmpCtx::<I>::default();
1541 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1542
1543 let id = api.create();
1544 assert_eq!(api.get_info(&id), datagram::SocketInfo::Unbound);
1545
1546 api.bind(&id, None, Some(ICMP_ID)).unwrap();
1547 assert_eq!(
1548 api.get_info(&id),
1549 datagram::SocketInfo::Listener(datagram::ListenerInfo {
1550 local_ip: None,
1551 local_identifier: ICMP_ID
1552 })
1553 );
1554
1555 api.connect(&id, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1556 assert_eq!(
1557 api.get_info(&id),
1558 datagram::SocketInfo::Connected(datagram::ConnInfo {
1559 local_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.local_ip),
1560 local_identifier: ICMP_ID,
1561 remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.remote_ip),
1562 remote_identifier: REMOTE_ID,
1563 })
1564 );
1565 }
1566
1567 #[ip_test(I)]
1568 fn send<I: TestIpExt + IpExt>() {
1569 let mut ctx = FakeIcmpCtx::<I>::default();
1570 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1571 let sock = api.create();
1572
1573 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1574 api.connect(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1575
1576 let packet = Buf::new([1u8, 2, 3, 4], ..)
1577 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1578 I::UNSPECIFIED_ADDRESS,
1579 I::UNSPECIFIED_ADDRESS,
1580 IcmpZeroCode,
1581 IcmpEchoRequest::new(0, SEQ_NUM),
1583 ))
1584 .serialize_vec_outer()
1585 .unwrap()
1586 .unwrap_b();
1587 api.send(&sock, Buf::new(packet, ..)).unwrap();
1588 let frames = ctx.core_ctx.frames.take_frames();
1589 let (SendIpPacketMeta { device: _, src_ip, dst_ip, .. }, body) =
1590 assert_matches!(&frames[..], [f] => f);
1591 assert_eq!(dst_ip, &I::TEST_ADDRS.remote_ip);
1592
1593 let mut body = &body[..];
1594 let echo_req: IcmpPacket<I, _, IcmpEchoRequest> =
1595 body.parse_with(IcmpParseArgs::new(src_ip.get(), dst_ip.get())).unwrap();
1596 assert_eq!(echo_req.message().id(), ICMP_ID.get());
1597 assert_eq!(echo_req.message().seq(), SEQ_NUM);
1598 }
1599
1600 #[ip_test(I)]
1601 fn receive<I: TestIpExt + IpExt>() {
1602 let mut ctx = FakeIcmpCtx::<I>::default();
1603 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1604 let sock = api.create();
1605
1606 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1607
1608 let reply = IcmpPacketBuilder::<I, _>::new(
1609 I::UNSPECIFIED_ADDRESS,
1611 I::UNSPECIFIED_ADDRESS,
1612 IcmpZeroCode,
1613 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1614 )
1615 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1616 .serialize_vec_outer()
1617 .unwrap();
1618
1619 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1620 let src_ip = I::TEST_ADDRS.remote_ip;
1621 let dst_ip = I::TEST_ADDRS.local_ip;
1622 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1623 core_ctx,
1624 bindings_ctx,
1625 &FakeDeviceId,
1626 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1627 dst_ip,
1628 reply.clone(),
1629 &LocalDeliveryPacketInfo::default(),
1630 None,
1631 )
1632 .unwrap();
1633
1634 let received = core::mem::take(&mut bindings_ctx.state.received);
1635 let ReceivedEchoPacket {
1636 src_ip: got_src_ip,
1637 dst_ip: got_dst_ip,
1638 socket: got_socket,
1639 id: got_id,
1640 data: got_data,
1641 } = assert_matches!(&received[..], [f] => f);
1642 assert_eq!(got_src_ip, &src_ip.get());
1643 assert_eq!(got_dst_ip, &dst_ip.get());
1644 assert_eq!(got_socket, &sock);
1645 assert_eq!(got_id, &ICMP_ID.get());
1646 assert_eq!(&got_data[..], reply.as_ref());
1647 }
1648
1649 #[ip_test(I)]
1650 fn receive_no_socket<I: TestIpExt + IpExt>() {
1651 let mut ctx = FakeIcmpCtx::<I>::default();
1652 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1653 let sock = api.create();
1654
1655 const BIND_ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1656 const OTHER_ICMP_ID: NonZeroU16 = NonZeroU16::new(16).unwrap();
1657
1658 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(BIND_ICMP_ID))
1659 .unwrap();
1660
1661 let reply = IcmpPacketBuilder::<I, _>::new(
1662 I::UNSPECIFIED_ADDRESS,
1664 I::UNSPECIFIED_ADDRESS,
1665 IcmpZeroCode,
1666 IcmpEchoReply::new(OTHER_ICMP_ID.get(), SEQ_NUM),
1667 )
1668 .wrap_body(EmptyBuf)
1669 .serialize_vec_outer()
1670 .unwrap();
1671
1672 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1673 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1674 core_ctx,
1675 bindings_ctx,
1676 &FakeDeviceId,
1677 I::RecvSrcAddr::new(I::TEST_ADDRS.remote_ip.get()).unwrap(),
1678 I::TEST_ADDRS.local_ip,
1679 reply,
1680 &LocalDeliveryPacketInfo::default(),
1681 None,
1682 )
1683 .unwrap();
1684 assert_matches!(&bindings_ctx.state.received[..], []);
1685 }
1686
1687 #[ip_test(I)]
1688 fn receive_queue_full<I: TestIpExt + IpExt>() {
1689 let mut ctx = FakeIcmpCtx::<I>::default();
1690 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1691 let sock = api.create();
1692
1693 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1694
1695 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1697 bindings_ctx.state.max_size = 0;
1698
1699 let reply = IcmpPacketBuilder::<I, _>::new(
1700 I::UNSPECIFIED_ADDRESS,
1702 I::UNSPECIFIED_ADDRESS,
1703 IcmpZeroCode,
1704 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1705 )
1706 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1707 .serialize_vec_outer()
1708 .unwrap();
1709
1710 let src_ip = I::TEST_ADDRS.remote_ip;
1711 let dst_ip = I::TEST_ADDRS.local_ip;
1712 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1713 core_ctx,
1714 bindings_ctx,
1715 &FakeDeviceId,
1716 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1717 dst_ip,
1718 reply,
1719 &LocalDeliveryPacketInfo::default(),
1720 None,
1721 )
1722 .unwrap();
1723
1724 assert_eq!(core_ctx.counters().queue_full.get(), 1);
1725 }
1726
1727 #[ip_test(I)]
1728 #[test_case::test_matrix(
1729 [MarkDomain::Mark1, MarkDomain::Mark2],
1730 [None, Some(0), Some(1)]
1731 )]
1732 fn icmp_socket_marks<I: TestIpExt + IpExt>(domain: MarkDomain, mark: Option<u32>) {
1733 let mut ctx = FakeIcmpCtx::<I>::default();
1734 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1735 let socket = api.create();
1736
1737 assert_eq!(api.get_mark(&socket, domain), Mark(None));
1739
1740 let mark = Mark(mark);
1741 api.set_mark(&socket, domain, mark);
1743 assert_eq!(api.get_mark(&socket, domain), mark);
1744 }
1745
1746 #[ip_test(I)]
1752 fn icmp_error_with_inner_icmp_echo_with_id_0<I: TestIpExt + IpExt>() {
1753 let mut ctx = FakeIcmpCtx::<I>::default();
1754
1755 let src_ip = I::TEST_ADDRS.remote_ip;
1756 let dst_ip = I::TEST_ADDRS.local_ip;
1757
1758 let original_body = Buf::new([1, 2, 3, 4], ..)
1759 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1760 *src_ip,
1761 *dst_ip,
1762 IcmpZeroCode,
1763 IcmpEchoRequest::new(0, 1),
1766 ))
1767 .serialize_vec_outer()
1768 .unwrap()
1769 .unwrap_b();
1770
1771 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1772 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
1773 core_ctx,
1774 bindings_ctx,
1775 &FakeDeviceId,
1776 Some(src_ip),
1777 dst_ip,
1778 original_body.as_ref(),
1779 I::map_ip_out(
1780 (),
1781 |()| {
1782 Icmpv4ErrorCode::DestUnreachable(
1783 Icmpv4DestUnreachableCode::DestNetworkUnreachable,
1784 IcmpDestUnreachable::default(),
1785 )
1786 },
1787 |()| Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::AddrUnreachable),
1788 ),
1789 )
1790 }
1791}