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