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