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