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: _ } = info;
1160 let ReceiveIpPacketMeta { broadcast: _, transparent_override, parsing_context: _ } = meta;
1161 if let Some(delivery) = transparent_override.as_ref() {
1162 unreachable!(
1163 "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1164 transparent proxy rules can only be configured for TCP and UDP packets"
1165 );
1166 }
1167 let echo_reply =
1171 buffer.parse::<IcmpPacketRaw<I, _, IcmpEchoReply>>().expect("received non-echo reply");
1172 let Some(id) = NonZeroU16::new(echo_reply.message().id()) else { return Ok(()) };
1174
1175 let meta = echo_reply.parse_metadata();
1177 buffer.undo_parse(meta);
1178
1179 let src_ip = match SpecifiedAddr::new(src_ip.into_addr()) {
1180 Some(src_ip) => src_ip,
1181 None => {
1182 trace!("receive_icmp_echo_reply: unspecified source address");
1183 return Ok(());
1184 }
1185 };
1186 let src_ip: SocketIpAddr<_> = match src_ip.try_into() {
1187 Ok(src_ip) => src_ip,
1188 Err(AddrIsMappedError {}) => {
1189 trace!("receive_icmp_echo_reply: mapped source address");
1190 return Ok(());
1191 }
1192 };
1193 let dst_ip: SocketIpAddr<_> = match dst_ip.try_into() {
1194 Ok(dst_ip) => dst_ip,
1195 Err(AddrIsMappedError {}) => {
1196 trace!("receive_icmp_echo_reply: mapped destination address");
1197 return Ok(());
1198 }
1199 };
1200
1201 core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1202 let mut addrs_to_search = AddrVecIter::<I, CC::WeakDeviceId, IcmpAddrSpec>::with_device(
1203 ConnIpAddr { local: (dst_ip, id), remote: (src_ip, ()) }.into(),
1204 device.downgrade(),
1205 );
1206 let socket = match addrs_to_search.try_for_each(|addr_vec| {
1207 match addr_vec {
1208 AddrVec::Conn(c) => {
1209 if let Some(id) = sockets.socket_map.conns().get_by_addr(&c) {
1210 return ControlFlow::Break(id);
1211 }
1212 }
1213 AddrVec::Listen(l) => {
1214 if let Some(id) = sockets.socket_map.listeners().get_by_addr(&l) {
1215 return ControlFlow::Break(id);
1216 }
1217 }
1218 }
1219 ControlFlow::Continue(())
1220 }) {
1221 ControlFlow::Continue(()) => None,
1222 ControlFlow::Break(id) => Some(id),
1223 };
1224 if let Some(socket) = socket {
1225 trace!("receive_icmp_echo_reply: Received echo reply for local socket");
1226 match bindings_ctx.receive_icmp_echo_reply(
1227 socket,
1228 device,
1229 src_ip.addr(),
1230 dst_ip.addr(),
1231 id.get(),
1232 buffer,
1233 ) {
1234 Ok(()) => {}
1235 Err(ReceiveIcmpEchoError::QueueFull) => {
1236 core_ctx.counters().queue_full.increment();
1237 }
1238 }
1239 return;
1240 }
1241 trace!("receive_icmp_echo_reply: Received echo reply with no local socket");
1258 });
1259 Ok(())
1260 }
1261}
1262
1263#[cfg(test)]
1264mod tests {
1265 use alloc::rc::Rc;
1266 use alloc::vec;
1267 use core::cell::RefCell;
1268 use core::ops::{Deref, DerefMut};
1269
1270 use assert_matches::assert_matches;
1271 use ip_test_macro::ip_test;
1272 use net_declare::net_ip_v6;
1273 use net_types::Witness;
1274 use net_types::ip::Ipv6;
1275 use netstack3_base::socket::StrictlyZonedAddr;
1276 use netstack3_base::testutil::{
1277 FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSendToken, FakeWeakDeviceId, TestIpExt,
1278 };
1279 use netstack3_base::{CtxPair, Icmpv4ErrorCode, Icmpv6ErrorCode, NetworkSerializationContext};
1280 use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx, InnerFakeIpSocketCtx};
1281 use netstack3_ip::{LocalDeliveryPacketInfo, SendIpPacketMeta};
1282 use packet::{Buf, EmptyBuf, NestableSerializer as _, Serializer};
1283 use packet_formats::icmp::{
1284 IcmpDestUnreachable, IcmpPacket, IcmpParseArgs, IcmpZeroCode, Icmpv4DestUnreachableCode,
1285 Icmpv6DestUnreachableCode,
1286 };
1287
1288 use super::*;
1289
1290 const REMOTE_ID: u16 = 27;
1291 const ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1292 const SEQ_NUM: u16 = 0xF0;
1293
1294 impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
1296 fn get(&self) -> impl Deref<Target = IcmpSocketState<I, D, BT>> + '_ {
1297 self.state().read()
1298 }
1299
1300 fn get_mut(&self) -> impl DerefMut<Target = IcmpSocketState<I, D, BT>> + '_ {
1301 self.state().write()
1302 }
1303 }
1304
1305 struct FakeIcmpCoreCtxState<I: IpExt> {
1306 bound_sockets:
1307 Rc<RefCell<BoundSockets<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>>>,
1308 all_sockets: IcmpSocketSet<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1309 ip_socket_ctx: FakeIpSocketCtx<I, FakeDeviceId>,
1310 rx_counters: IcmpRxCounters<I>,
1311 }
1312
1313 impl<I: IpExt> InnerFakeIpSocketCtx<I, FakeDeviceId> for FakeIcmpCoreCtxState<I> {
1314 fn fake_ip_socket_ctx_mut(&mut self) -> &mut FakeIpSocketCtx<I, FakeDeviceId> {
1315 &mut self.ip_socket_ctx
1316 }
1317 }
1318
1319 impl<I: IpExt + TestIpExt> Default for FakeIcmpCoreCtxState<I> {
1320 fn default() -> Self {
1321 Self {
1322 bound_sockets: Default::default(),
1323 all_sockets: Default::default(),
1324 ip_socket_ctx: FakeIpSocketCtx::new(core::iter::once(FakeDeviceConfig {
1325 device: FakeDeviceId,
1326 local_ips: vec![I::TEST_ADDRS.local_ip],
1327 remote_ips: vec![I::TEST_ADDRS.remote_ip],
1328 })),
1329 rx_counters: Default::default(),
1330 }
1331 }
1332 }
1333
1334 type FakeIcmpCoreCtx<I> = FakeCoreCtx<
1335 FakeIcmpCoreCtxState<I>,
1336 SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
1337 FakeDeviceId,
1338 >;
1339 type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<(), (), FakeIcmpBindingsCtxState<I>, ()>;
1340 type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
1341
1342 #[derive(Derivative)]
1343 #[derivative(Default)]
1344
1345 struct FakeIcmpBindingsCtxState<I: IpExt> {
1346 received: Vec<ReceivedEchoPacket<I>>,
1347 #[derivative(Default(value = "usize::MAX"))]
1348 max_size: usize,
1349 }
1350
1351 #[derive(Debug)]
1352 struct ReceivedEchoPacket<I: IpExt> {
1353 src_ip: I::Addr,
1354 dst_ip: I::Addr,
1355 socket: IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1356 id: u16,
1357 data: Vec<u8>,
1358 }
1359
1360 impl<I: IpExt> IcmpEchoContextMarker for FakeIcmpCoreCtx<I> {}
1361
1362 impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtxState<I> {
1363 fn counters(&self) -> &IcmpRxCounters<I> {
1364 &self.rx_counters
1365 }
1366 }
1367
1368 impl<I: IpExt> IcmpEchoBoundStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1369 type IpSocketsCtx<'a> = Self;
1370
1371 fn with_icmp_ctx_and_sockets_mut<
1372 O,
1373 F: FnOnce(
1374 &mut Self::IpSocketsCtx<'_>,
1375 &mut BoundSockets<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1376 ) -> O,
1377 >(
1378 &mut self,
1379 cb: F,
1380 ) -> O {
1381 let bound_sockets = self.state.bound_sockets.clone();
1382 let mut bound_sockets = bound_sockets.borrow_mut();
1383 cb(self, &mut bound_sockets)
1384 }
1385 }
1386
1387 impl<I: IpExt> IcmpEchoStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1388 type SocketStateCtx<'a> = Self;
1389
1390 fn with_all_sockets_mut<
1391 O,
1392 F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1393 >(
1394 &mut self,
1395 cb: F,
1396 ) -> O {
1397 cb(&mut self.state.all_sockets)
1398 }
1399
1400 fn with_all_sockets<
1401 O,
1402 F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1403 >(
1404 &mut self,
1405 cb: F,
1406 ) -> O {
1407 cb(&self.state.all_sockets)
1408 }
1409
1410 fn with_socket_state<
1411 O,
1412 F: FnOnce(
1413 &mut Self::SocketStateCtx<'_>,
1414 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1415 ) -> O,
1416 >(
1417 &mut self,
1418 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1419 cb: F,
1420 ) -> O {
1421 cb(self, &id.get())
1422 }
1423
1424 fn with_socket_state_mut<
1425 O,
1426 F: FnOnce(
1427 &mut Self::SocketStateCtx<'_>,
1428 &mut IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1429 ) -> O,
1430 >(
1431 &mut self,
1432 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1433 cb: F,
1434 ) -> O {
1435 cb(self, &mut id.get_mut())
1436 }
1437
1438 fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
1439 &mut self,
1440 cb: F,
1441 ) -> O {
1442 cb(self)
1443 }
1444
1445 fn for_each_socket<
1446 F: FnMut(
1447 &mut Self::SocketStateCtx<'_>,
1448 &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1449 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1450 ),
1451 >(
1452 &mut self,
1453 mut cb: F,
1454 ) {
1455 let socks = self
1456 .state
1457 .all_sockets
1458 .keys()
1459 .map(|id| IcmpSocketId::from(id.clone()))
1460 .collect::<Vec<_>>();
1461 for id in socks {
1462 cb(self, &id, &id.get());
1463 }
1464 }
1465 }
1466
1467 impl<I: IpExt> IcmpEchoBindingsContext<I, FakeDeviceId> for FakeIcmpBindingsCtx<I> {
1468 fn receive_icmp_echo_reply<B: BufferMut>(
1469 &mut self,
1470 socket: &IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1471 _device_id: &FakeDeviceId,
1472 src_ip: I::Addr,
1473 dst_ip: I::Addr,
1474 id: u16,
1475 data: B,
1476 ) -> Result<(), ReceiveIcmpEchoError> {
1477 if self.state.received.len() < self.state.max_size {
1478 self.state.received.push(ReceivedEchoPacket {
1479 src_ip,
1480 dst_ip,
1481 id,
1482 data: data.to_flattened_vec(),
1483 socket: socket.clone(),
1484 });
1485 Ok(())
1486 } else {
1487 Err(ReceiveIcmpEchoError::QueueFull)
1488 }
1489 }
1490 }
1491
1492 impl<I: IpExt> IcmpEchoBindingsTypes for FakeIcmpBindingsCtx<I> {
1493 type ExternalData<II: Ip> = ();
1494 type SendToken = FakeSendToken;
1495 }
1496
1497 #[test]
1498 fn test_connect_dual_stack_fails() {
1499 let mut ctx = FakeIcmpCtx::<Ipv6>::default();
1502 let mut api = IcmpEchoSocketApi::<Ipv6, _>::new(ctx.as_mut());
1503 let conn = api.create();
1504 assert_eq!(
1505 api.connect(
1506 &conn,
1507 Some(ZonedAddr::Unzoned(
1508 SpecifiedAddr::new(net_ip_v6!("::ffff:192.0.2.1")).unwrap(),
1509 )),
1510 REMOTE_ID,
1511 ),
1512 Err(datagram::ConnectError::RemoteUnexpectedlyMapped)
1513 );
1514 }
1515
1516 #[ip_test(I)]
1517 fn send_invalid_icmp_echo<I: TestIpExt + IpExt>() {
1518 let mut ctx = FakeIcmpCtx::<I>::default();
1519 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1520 let conn = api.create();
1521 api.connect(&conn, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1522
1523 let pb = IcmpPacketBuilder::<I, _>::new(
1524 I::TEST_ADDRS.local_ip.get(),
1525 I::TEST_ADDRS.remote_ip.get(),
1526 IcmpZeroCode,
1527 packet_formats::icmp::IcmpEchoReply::new(0, 1),
1528 );
1529 let buf = pb
1530 .wrap_body(Buf::new(Vec::new(), ..))
1531 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1532 .unwrap()
1533 .into_inner();
1534 assert_matches!(
1535 api.send(&conn, buf, FakeSendToken::default()),
1536 Err(datagram::SendError::SerializeError(
1537 packet_formats::error::ParseError::NotExpected
1538 ))
1539 );
1540 }
1541
1542 #[ip_test(I)]
1543 fn get_info<I: TestIpExt + IpExt>() {
1544 let mut ctx = FakeIcmpCtx::<I>::default();
1545 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1546
1547 let id = api.create();
1548 assert_eq!(api.get_info(&id), datagram::SocketInfo::Unbound);
1549
1550 api.bind(&id, None, Some(ICMP_ID)).unwrap();
1551 assert_eq!(
1552 api.get_info(&id),
1553 datagram::SocketInfo::Listener(datagram::ListenerInfo {
1554 local_ip: None,
1555 local_identifier: ICMP_ID
1556 })
1557 );
1558
1559 api.connect(&id, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1560 assert_eq!(
1561 api.get_info(&id),
1562 datagram::SocketInfo::Connected(datagram::ConnInfo {
1563 local_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.local_ip),
1564 local_identifier: ICMP_ID,
1565 remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.remote_ip),
1566 remote_identifier: REMOTE_ID,
1567 })
1568 );
1569 }
1570
1571 #[ip_test(I)]
1572 fn send<I: TestIpExt + IpExt>() {
1573 let mut ctx = FakeIcmpCtx::<I>::default();
1574 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1575 let sock = api.create();
1576
1577 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1578 api.connect(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1579
1580 let packet = Buf::new([1u8, 2, 3, 4], ..)
1581 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1582 I::UNSPECIFIED_ADDRESS,
1583 I::UNSPECIFIED_ADDRESS,
1584 IcmpZeroCode,
1585 IcmpEchoRequest::new(0, SEQ_NUM),
1587 ))
1588 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1589 .unwrap()
1590 .unwrap_b();
1591 api.send(&sock, Buf::new(packet, ..), FakeSendToken::default()).unwrap();
1592 let frames = ctx.core_ctx.frames.take_frames();
1593 let (SendIpPacketMeta { device: _, src_ip, dst_ip, .. }, body) =
1594 assert_matches!(&frames[..], [f] => f);
1595 assert_eq!(dst_ip, &I::TEST_ADDRS.remote_ip);
1596
1597 let mut body = &body[..];
1598 let echo_req: IcmpPacket<I, _, IcmpEchoRequest> =
1599 body.parse_with(IcmpParseArgs::new(src_ip.get(), dst_ip.get())).unwrap();
1600 assert_eq!(echo_req.message().id(), ICMP_ID.get());
1601 assert_eq!(echo_req.message().seq(), SEQ_NUM);
1602 }
1603
1604 #[ip_test(I)]
1605 fn receive<I: TestIpExt + IpExt>() {
1606 let mut ctx = FakeIcmpCtx::<I>::default();
1607 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1608 let sock = api.create();
1609
1610 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1611
1612 let reply = IcmpPacketBuilder::<I, _>::new(
1613 I::UNSPECIFIED_ADDRESS,
1615 I::UNSPECIFIED_ADDRESS,
1616 IcmpZeroCode,
1617 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1618 )
1619 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1620 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1621 .unwrap();
1622
1623 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1624 let src_ip = I::TEST_ADDRS.remote_ip;
1625 let dst_ip = I::TEST_ADDRS.local_ip;
1626 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1627 core_ctx,
1628 bindings_ctx,
1629 &FakeDeviceId,
1630 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1631 dst_ip,
1632 reply.clone(),
1633 &mut LocalDeliveryPacketInfo::default(),
1634 None,
1635 )
1636 .unwrap();
1637
1638 let received = core::mem::take(&mut bindings_ctx.state.received);
1639 let ReceivedEchoPacket {
1640 src_ip: got_src_ip,
1641 dst_ip: got_dst_ip,
1642 socket: got_socket,
1643 id: got_id,
1644 data: got_data,
1645 } = assert_matches!(&received[..], [f] => f);
1646 assert_eq!(got_src_ip, &src_ip.get());
1647 assert_eq!(got_dst_ip, &dst_ip.get());
1648 assert_eq!(got_socket, &sock);
1649 assert_eq!(got_id, &ICMP_ID.get());
1650 assert_eq!(&got_data[..], reply.as_ref());
1651 }
1652
1653 #[ip_test(I)]
1654 fn receive_no_socket<I: TestIpExt + IpExt>() {
1655 let mut ctx = FakeIcmpCtx::<I>::default();
1656 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1657 let sock = api.create();
1658
1659 const BIND_ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1660 const OTHER_ICMP_ID: NonZeroU16 = NonZeroU16::new(16).unwrap();
1661
1662 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(BIND_ICMP_ID))
1663 .unwrap();
1664
1665 let reply = IcmpPacketBuilder::<I, _>::new(
1666 I::UNSPECIFIED_ADDRESS,
1668 I::UNSPECIFIED_ADDRESS,
1669 IcmpZeroCode,
1670 IcmpEchoReply::new(OTHER_ICMP_ID.get(), SEQ_NUM),
1671 )
1672 .wrap_body(EmptyBuf)
1673 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1674 .unwrap();
1675
1676 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1677 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1678 core_ctx,
1679 bindings_ctx,
1680 &FakeDeviceId,
1681 I::RecvSrcAddr::new(I::TEST_ADDRS.remote_ip.get()).unwrap(),
1682 I::TEST_ADDRS.local_ip,
1683 reply,
1684 &mut LocalDeliveryPacketInfo::default(),
1685 None,
1686 )
1687 .unwrap();
1688 assert_matches!(&bindings_ctx.state.received[..], []);
1689 }
1690
1691 #[ip_test(I)]
1692 fn receive_queue_full<I: TestIpExt + IpExt>() {
1693 let mut ctx = FakeIcmpCtx::<I>::default();
1694 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1695 let sock = api.create();
1696
1697 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1698
1699 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1701 bindings_ctx.state.max_size = 0;
1702
1703 let reply = IcmpPacketBuilder::<I, _>::new(
1704 I::UNSPECIFIED_ADDRESS,
1706 I::UNSPECIFIED_ADDRESS,
1707 IcmpZeroCode,
1708 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1709 )
1710 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1711 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1712 .unwrap();
1713
1714 let src_ip = I::TEST_ADDRS.remote_ip;
1715 let dst_ip = I::TEST_ADDRS.local_ip;
1716 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1717 core_ctx,
1718 bindings_ctx,
1719 &FakeDeviceId,
1720 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1721 dst_ip,
1722 reply,
1723 &mut LocalDeliveryPacketInfo::default(),
1724 None,
1725 )
1726 .unwrap();
1727
1728 assert_eq!(core_ctx.counters().queue_full.get(), 1);
1729 }
1730
1731 #[ip_test(I, test = false)]
1732 #[test_case::test_matrix(
1733 [MarkDomain::Mark1, MarkDomain::Mark2],
1734 [None, Some(0), Some(1)]
1735 )]
1736 fn icmp_socket_marks<I: TestIpExt + IpExt>(domain: MarkDomain, mark: Option<u32>) {
1737 let mut ctx = FakeIcmpCtx::<I>::default();
1738 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1739 let socket = api.create();
1740
1741 assert_eq!(api.get_mark(&socket, domain), Mark(None));
1743
1744 let mark = Mark(mark);
1745 api.set_mark(&socket, domain, mark);
1747 assert_eq!(api.get_mark(&socket, domain), mark);
1748 }
1749
1750 #[ip_test(I)]
1756 fn icmp_error_with_inner_icmp_echo_with_id_0<I: TestIpExt + IpExt>() {
1757 let mut ctx = FakeIcmpCtx::<I>::default();
1758
1759 let src_ip = I::TEST_ADDRS.remote_ip;
1760 let dst_ip = I::TEST_ADDRS.local_ip;
1761
1762 let original_body = Buf::new([1, 2, 3, 4], ..)
1763 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1764 *src_ip,
1765 *dst_ip,
1766 IcmpZeroCode,
1767 IcmpEchoRequest::new(0, 1),
1770 ))
1771 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1772 .unwrap()
1773 .unwrap_b();
1774
1775 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1776 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
1777 core_ctx,
1778 bindings_ctx,
1779 &FakeDeviceId,
1780 Some(src_ip),
1781 dst_ip,
1782 original_body.as_ref(),
1783 I::map_ip_out(
1784 (),
1785 |()| {
1786 Icmpv4ErrorCode::DestUnreachable(
1787 Icmpv4DestUnreachableCode::DestNetworkUnreachable,
1788 IcmpDestUnreachable::default(),
1789 )
1790 },
1791 |()| Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::AddrUnreachable),
1792 ),
1793 )
1794 }
1795}