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