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::{BindingsSyncTypes, 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>, BT::RwLock>,
60 all_sockets: RwLock<IcmpSocketSet<I, D, BT>, BT::RwLock>,
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>, BT::RwLock>;
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>, BT::RwLock>;
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>, BT::RwLock> {
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> BindingsSyncTypes for Icmp<BT> {
389 type Mutex = BT::Mutex;
390 type RwLock = BT::RwLock;
391 type Once = BT::Once;
392}
393
394impl<BT: IcmpEchoBindingsTypes> DatagramSocketSpec for Icmp<BT> {
395 const NAME: &'static str = "ICMP_ECHO";
396 type AddrSpec = IcmpAddrSpec;
397
398 type SocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = IcmpSocketId<I, D, BT>;
399 type WeakSocketId<I: datagram::IpExt, D: WeakDeviceIdentifier> = WeakIcmpSocketId<I, D, BT>;
400
401 type OtherStackIpOptions<I: datagram::IpExt, D: WeakDeviceIdentifier> = ();
402
403 type SharingState = ();
404
405 type SocketMapSpec<I: datagram::IpExt + datagram::DualStackIpExt, D: WeakDeviceIdentifier> =
406 IcmpSocketMapStateSpec<I, D, BT>;
407
408 fn ip_proto<I: IpProtoExt>() -> I::Proto {
409 I::map_ip((), |()| Ipv4Proto::Icmp, |()| Ipv6Proto::Icmpv6)
410 }
411
412 fn make_bound_socket_map_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
413 s: &Self::SocketId<I, D>,
414 ) -> <Self::SocketMapSpec<I, D> as datagram::DatagramSocketMapSpec<
415 I,
416 D,
417 Self::AddrSpec,
418 >>::BoundSocketId{
419 s.clone()
420 }
421
422 type Serializer<I: datagram::IpExt, B: BufferMut> =
423 packet::Nested<B, IcmpPacketBuilder<I, IcmpEchoRequest>>;
424 type SerializeError = packet_formats::error::ParseError;
425
426 type ExternalData<I: Ip> = BT::ExternalData<I>;
427 type SendToken = BT::SendToken;
428
429 type Counters<I: Ip> = ();
431
432 fn make_packet<I: datagram::IpExt, B: BufferMut>(
433 mut body: B,
434 addr: &socket::ConnIpAddr<
435 I::Addr,
436 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
437 <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
438 >,
439 ) -> Result<Self::Serializer<I, B>, Self::SerializeError> {
440 let ConnIpAddr { local: (local_ip, id), remote: (remote_ip, ()) } = addr;
441 let icmp_echo: packet_formats::icmp::IcmpPacketRaw<I, &[u8], IcmpEchoRequest> =
442 body.parse()?;
443 debug!(
444 "preparing ICMP echo request {local_ip} to {remote_ip}: id={}, seq={}",
445 id,
446 icmp_echo.message().seq()
447 );
448 let icmp_builder = IcmpPacketBuilder::<I, _>::new(
449 local_ip.addr(),
450 remote_ip.addr(),
451 packet_formats::icmp::IcmpZeroCode,
452 IcmpEchoRequest::new(id.get(), icmp_echo.message().seq()),
453 );
454 Ok(icmp_builder.wrap_body(body))
455 }
456
457 fn try_alloc_listen_identifier<I: datagram::IpExt, D: WeakDeviceIdentifier>(
458 bindings_ctx: &mut impl RngContext,
459 is_available: impl Fn(
460 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
461 ) -> Result<(), datagram::InUseError>,
462 ) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
463 let mut port = IcmpPortAlloc::<I, D, BT>::rand_ephemeral(&mut bindings_ctx.rng());
464 for _ in IcmpPortAlloc::<I, D, BT>::EPHEMERAL_RANGE {
465 let tryport = NonZeroU16::new(port.get()).unwrap();
468 match is_available(tryport) {
469 Ok(()) => return Some(tryport),
470 Err(datagram::InUseError {}) => port.next(),
471 }
472 }
473 None
474 }
475
476 type ListenerIpAddr<I: datagram::IpExt> = socket::ListenerIpAddr<I::Addr, NonZeroU16>;
477
478 type ConnIpAddr<I: datagram::IpExt> = ConnIpAddr<
479 I::Addr,
480 <Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
481 <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
482 >;
483
484 type ConnState<I: datagram::IpExt, D: WeakDeviceIdentifier> = datagram::ConnState<I, D, Self>;
485 type ConnStateExtra = u16;
489
490 fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
491 state: &Self::ConnState<I, D>,
492 ) -> datagram::ConnInfo<I::Addr, D> {
493 let ConnAddr { ip, device } = state.addr();
494 let extra = state.extra();
495 let ConnInfoAddr { local: (local_ip, local_identifier), remote: (remote_ip, ()) } =
496 ip.clone().into();
497 datagram::ConnInfo::new(local_ip, local_identifier, remote_ip, *extra, || {
498 device.clone().expect("device must be bound for addresses that require zones")
500 })
501 }
502
503 fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
504 bound: &IcmpBoundSockets<I, D, BT>,
505 bindings_ctx: &mut BC,
506 flow: datagram::DatagramFlowId<I::Addr, ()>,
507 ) -> Option<NonZeroU16> {
508 let mut rng = bindings_ctx.rng();
509 netstack3_base::simple_randomized_port_alloc(&mut rng, &flow, &IcmpPortAlloc(bound), &())
510 .map(|p| NonZeroU16::new(p).expect("ephemeral ports should be non-zero"))
511 }
512
513 fn upgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
514 id: &Self::WeakSocketId<I, D>,
515 ) -> Option<Self::SocketId<I, D>> {
516 id.upgrade()
517 }
518
519 fn downgrade_socket_id<I: datagram::IpExt, D: WeakDeviceIdentifier>(
520 id: &Self::SocketId<I, D>,
521 ) -> Self::WeakSocketId<I, D> {
522 IcmpSocketId::downgrade(id)
523 }
524}
525
526pub enum IcmpAddrSpec {}
528
529impl SocketMapAddrSpec for IcmpAddrSpec {
530 type RemoteIdentifier = ();
531 type LocalIdentifier = NonZeroU16;
532}
533
534type IcmpBoundSockets<I, D, BT> = datagram::BoundDatagramSocketMap<I, D, Icmp<BT>>;
535
536struct IcmpPortAlloc<'a, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>(
537 &'a IcmpBoundSockets<I, D, BT>,
538);
539
540impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> PortAllocImpl
541 for IcmpPortAlloc<'_, I, D, BT>
542{
543 const EPHEMERAL_RANGE: core::ops::RangeInclusive<u16> = 1..=u16::MAX;
544 type Id = DatagramFlowId<I::Addr, ()>;
545 type PortAvailableArg = ();
546
547 fn is_port_available(&self, id: &Self::Id, port: u16, (): &()) -> bool {
548 let Self(socketmap) = self;
549 let port = NonZeroU16::new(port).unwrap();
552 let conn = ConnAddr {
553 ip: ConnIpAddr { local: (id.local_ip, port), remote: (id.remote_ip, ()) },
554 device: None,
555 };
556
557 AddrVec::from(conn).iter_shadows().all(|a| match &a {
560 AddrVec::Listen(l) => socketmap.listeners().get_by_addr(&l).is_none(),
561 AddrVec::Conn(c) => socketmap.conns().get_by_addr(&c).is_none(),
562 } && socketmap.get_shadower_counts(&a) == 0)
563 }
564}
565
566#[derive(Derivative)]
568#[derivative(Default(bound = ""))]
569pub struct BoundSockets<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> {
570 pub(crate) socket_map: IcmpBoundSockets<I, D, BT>,
571}
572
573impl<I, BC, CC> NonDualStackDatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
574where
575 I: IpExt + datagram::DualStackIpExt,
576 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
577 CC: DeviceIdContext<AnyDevice> + IcmpEchoContextMarker,
578{
579 fn nds_converter(_core_ctx: &CC) -> impl NonDualStackConverter<I, CC::WeakDeviceId, Self> {
580 ()
581 }
582}
583
584impl<I, BC, CC> DatagramSpecBoundStateContext<I, CC, BC> for Icmp<BC>
585where
586 I: IpExt + datagram::DualStackIpExt,
587 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
588 CC: IcmpEchoBoundStateContext<I, BC> + IcmpEchoContextMarker,
589{
590 type IpSocketsCtx<'a> = CC::IpSocketsCtx<'a>;
591
592 type DualStackContext = UninstantiableWrapper<CC>;
594
595 type NonDualStackContext = CC;
596
597 fn with_bound_sockets<
598 O,
599 F: FnOnce(&mut Self::IpSocketsCtx<'_>, &IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
600 >(
601 core_ctx: &mut CC,
602 cb: F,
603 ) -> O {
604 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
605 core_ctx,
606 |ctx, BoundSockets { socket_map }| cb(ctx, &socket_map),
607 )
608 }
609
610 fn with_bound_sockets_mut<
611 O,
612 F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut IcmpBoundSockets<I, CC::WeakDeviceId, BC>) -> O,
613 >(
614 core_ctx: &mut CC,
615 cb: F,
616 ) -> O {
617 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(
618 core_ctx,
619 |ctx, BoundSockets { socket_map }| cb(ctx, socket_map),
620 )
621 }
622
623 fn dual_stack_context(
624 core_ctx: &CC,
625 ) -> MaybeDualStack<&Self::DualStackContext, &Self::NonDualStackContext> {
626 MaybeDualStack::NotDualStack(core_ctx)
627 }
628
629 fn dual_stack_context_mut(
630 core_ctx: &mut CC,
631 ) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
632 MaybeDualStack::NotDualStack(core_ctx)
633 }
634
635 fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
636 core_ctx: &mut CC,
637 cb: F,
638 ) -> O {
639 IcmpEchoBoundStateContext::with_icmp_ctx_and_sockets_mut(core_ctx, |ctx, _sockets| cb(ctx))
640 }
641}
642
643impl<I, BC, CC> DatagramSpecStateContext<I, CC, BC> for Icmp<BC>
644where
645 I: IpExt + datagram::DualStackIpExt,
646 BC: IcmpEchoBindingsContext<I, CC::DeviceId>,
647 CC: IcmpEchoStateContext<I, BC>,
648{
649 type SocketsStateCtx<'a> = CC::SocketStateCtx<'a>;
650
651 fn with_all_sockets_mut<O, F: FnOnce(&mut IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
652 core_ctx: &mut CC,
653 cb: F,
654 ) -> O {
655 IcmpEchoStateContext::with_all_sockets_mut(core_ctx, cb)
656 }
657
658 fn with_all_sockets<O, F: FnOnce(&IcmpSocketSet<I, CC::WeakDeviceId, BC>) -> O>(
659 core_ctx: &mut CC,
660 cb: F,
661 ) -> O {
662 IcmpEchoStateContext::with_all_sockets(core_ctx, cb)
663 }
664
665 fn with_socket_state<
666 O,
667 F: FnOnce(&mut Self::SocketsStateCtx<'_>, &IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
668 >(
669 core_ctx: &mut CC,
670 id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
671 cb: F,
672 ) -> O {
673 IcmpEchoStateContext::with_socket_state(core_ctx, id, cb)
674 }
675
676 fn with_socket_state_mut<
677 O,
678 F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut IcmpSocketState<I, CC::WeakDeviceId, BC>) -> O,
679 >(
680 core_ctx: &mut CC,
681 id: &IcmpSocketId<I, CC::WeakDeviceId, BC>,
682 cb: F,
683 ) -> O {
684 IcmpEchoStateContext::with_socket_state_mut(core_ctx, id, cb)
685 }
686
687 fn for_each_socket<
688 F: FnMut(
689 &mut Self::SocketsStateCtx<'_>,
690 &IcmpSocketId<I, CC::WeakDeviceId, BC>,
691 &IcmpSocketState<I, CC::WeakDeviceId, BC>,
692 ),
693 >(
694 core_ctx: &mut CC,
695 cb: F,
696 ) {
697 IcmpEchoStateContext::for_each_socket(core_ctx, cb)
698 }
699}
700
701pub struct IcmpSocketMapStateSpec<I, D, BT>(PhantomData<(I, D, BT)>, !);
704
705impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapStateSpec
706 for IcmpSocketMapStateSpec<I, D, BT>
707{
708 type ListenerId = IcmpSocketId<I, D, BT>;
709 type ConnId = IcmpSocketId<I, D, BT>;
710
711 type AddrVecTag = ();
712
713 type ListenerSharingState = ();
714 type ConnSharingState = ();
715
716 type ListenerAddrState = Self::ListenerId;
717
718 type ConnAddrState = Self::ConnId;
719 fn listener_tag(
720 ListenerAddrInfo { has_device: _, specified_addr: _ }: ListenerAddrInfo,
721 _state: &Self::ListenerAddrState,
722 ) -> Self::AddrVecTag {
723 ()
724 }
725 fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
726 ()
727 }
728}
729
730impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> SocketMapAddrStateSpec
731 for IcmpSocketId<I, D, BT>
732{
733 type Id = Self;
734
735 type SharingState = ();
736
737 type Inserter<'a>
738 = !
739 where
740 Self: 'a;
741
742 fn new(_new_sharing_state: &Self::SharingState, id: Self::Id) -> Self {
743 id
744 }
745
746 fn contains_id(&self, id: &Self::Id) -> bool {
747 self == id
748 }
749
750 fn try_get_inserter<'a, 'b>(
751 &'b mut self,
752 _new_sharing_state: &'a Self::SharingState,
753 ) -> Result<Self::Inserter<'b>, IncompatibleError> {
754 Err(IncompatibleError)
755 }
756
757 fn could_insert(
758 &self,
759 _new_sharing_state: &Self::SharingState,
760 ) -> Result<(), IncompatibleError> {
761 Err(IncompatibleError)
762 }
763
764 fn remove_by_id(&mut self, _id: Self::Id) -> socket::RemoveResult {
765 socket::RemoveResult::IsLast
766 }
767
768 fn sharing_state(&self) -> Self::SharingState {
769 ()
770 }
771}
772
773impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
774 DatagramSocketMapSpec<I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
775{
776 type BoundSocketId = IcmpSocketId<I, D, BT>;
777}
778
779impl<AA, I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes>
780 SocketMapConflictPolicy<AA, (), I, D, IcmpAddrSpec> for IcmpSocketMapStateSpec<I, D, BT>
781where
782 AA: Into<AddrVec<I, D, IcmpAddrSpec>> + Clone,
783{
784 fn check_insert_conflicts(
785 _new_sharing_state: &(),
786 addr: &AA,
787 socketmap: &SocketMap<AddrVec<I, D, IcmpAddrSpec>, socket::Bound<Self>>,
788 ) -> Result<(), socket::InsertError> {
789 let addr: AddrVec<_, _, _> = addr.clone().into();
790 if addr.iter_shadows().any(|a| socketmap.get(&a).is_some()) {
792 return Err(InsertError::ShadowAddrExists);
793 }
794
795 if socketmap.descendant_counts(&addr).len() != 0 {
798 return Err(InsertError::WouldShadowExisting);
799 }
800 Ok(())
801 }
802}
803
804pub struct IcmpEchoSocketApi<I: Ip, C>(C, IpVersionMarker<I>);
806
807impl<I: Ip, C> IcmpEchoSocketApi<I, C> {
808 pub fn new(ctx: C) -> Self {
810 Self(ctx, IpVersionMarker::new())
811 }
812}
813
814type IcmpApiSocketId<I, C> = IcmpSocketId<
819 I,
820 <<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
821 <C as ContextPair>::BindingsContext,
822>;
823
824impl<I, C> IcmpEchoSocketApi<I, C>
825where
826 I: datagram::IpExt,
827 C: ContextPair,
828 C::CoreContext: IcmpEchoStateContext<I, C::BindingsContext>
829 + DatagramStateContext<I, C::BindingsContext, Icmp<C::BindingsContext>>,
832 C::BindingsContext:
833 IcmpEchoBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
834{
835 fn core_ctx(&mut self) -> &mut C::CoreContext {
836 let Self(pair, IpVersionMarker { .. }) = self;
837 pair.core_ctx()
838 }
839
840 fn datagram(&mut self) -> &mut DatagramApi<I, C, Icmp<C::BindingsContext>> {
841 let Self(pair, IpVersionMarker { .. }) = self;
842 DatagramApi::wrap(pair)
843 }
844
845 pub fn create(&mut self) -> IcmpApiSocketId<I, C>
847 where
848 <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>: Default,
849 {
850 self.create_with(Default::default())
851 }
852
853 pub fn create_with(
855 &mut self,
856 external_data: <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
857 ) -> IcmpApiSocketId<I, C> {
858 self.datagram().create(external_data)
859 }
860
861 pub fn connect(
865 &mut self,
866 id: &IcmpApiSocketId<I, C>,
867 remote_ip: Option<
868 ZonedAddr<
869 SpecifiedAddr<I::Addr>,
870 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
871 >,
872 >,
873 remote_id: u16,
874 ) -> Result<(), datagram::ConnectError> {
875 self.datagram().connect(id, remote_ip, (), remote_id)
876 }
877
878 pub fn bind(
883 &mut self,
884 id: &IcmpApiSocketId<I, C>,
885 local_ip: Option<
886 ZonedAddr<
887 SpecifiedAddr<I::Addr>,
888 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
889 >,
890 >,
891 icmp_id: Option<NonZeroU16>,
892 ) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
893 self.datagram().listen(id, local_ip, icmp_id)
894 }
895
896 pub fn get_info(
898 &mut self,
899 id: &IcmpApiSocketId<I, C>,
900 ) -> datagram::SocketInfo<I::Addr, <C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>
901 {
902 self.datagram().get_info(id)
903 }
904
905 pub fn set_device(
911 &mut self,
912 id: &IcmpApiSocketId<I, C>,
913 device_id: Option<&<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
914 ) -> Result<(), SocketError> {
915 self.datagram().set_device(id, device_id)
916 }
917
918 pub fn get_bound_device(
920 &mut self,
921 id: &IcmpApiSocketId<I, C>,
922 ) -> Option<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId> {
923 self.datagram().get_bound_device(id)
924 }
925
926 pub fn disconnect(
928 &mut self,
929 id: &IcmpApiSocketId<I, C>,
930 ) -> Result<(), datagram::ExpectedConnError> {
931 self.datagram().disconnect_connected(id)
932 }
933
934 pub fn shutdown(
936 &mut self,
937 id: &IcmpApiSocketId<I, C>,
938 shutdown_type: ShutdownType,
939 ) -> Result<(), datagram::ExpectedConnError> {
940 self.datagram().shutdown_connected(id, shutdown_type)
941 }
942
943 pub fn get_shutdown(&mut self, id: &IcmpApiSocketId<I, C>) -> Option<ShutdownType> {
945 self.datagram().get_shutdown_connected(id)
946 }
947
948 pub fn close(
950 &mut self,
951 id: IcmpApiSocketId<I, C>,
952 ) -> RemoveResourceResultWithContext<
953 <C::BindingsContext as IcmpEchoBindingsTypes>::ExternalData<I>,
954 C::BindingsContext,
955 > {
956 self.datagram().close(id, |state| {
957 let (_state, external_data) = state.into_state_and_external_data();
958 external_data
959 })
960 }
961
962 pub fn get_unicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
964 self.datagram().get_ip_hop_limits(id).unicast
965 }
966
967 pub fn get_multicast_hop_limit(&mut self, id: &IcmpApiSocketId<I, C>) -> NonZeroU8 {
969 self.datagram().get_ip_hop_limits(id).multicast
970 }
971
972 pub fn set_unicast_hop_limit(
974 &mut self,
975 id: &IcmpApiSocketId<I, C>,
976 hop_limit: Option<NonZeroU8>,
977 ) {
978 self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_unicast(hop_limit))
979 }
980
981 pub fn set_multicast_hop_limit(
983 &mut self,
984 id: &IcmpApiSocketId<I, C>,
985 hop_limit: Option<NonZeroU8>,
986 ) {
987 self.datagram().update_ip_hop_limit(id, SocketHopLimits::set_multicast(hop_limit))
988 }
989
990 pub fn get_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>) -> bool {
992 self.datagram().get_multicast_loop(id)
993 }
994
995 pub fn set_multicast_loop(&mut self, id: &IcmpApiSocketId<I, C>, value: bool) {
997 self.datagram().set_multicast_loop(id, value);
998 }
999
1000 pub fn set_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain, mark: Mark) {
1002 self.datagram().set_mark(id, domain, mark)
1003 }
1004
1005 pub fn get_mark(&mut self, id: &IcmpApiSocketId<I, C>, domain: MarkDomain) -> Mark {
1007 self.datagram().get_mark(id, domain)
1008 }
1009
1010 pub fn send<B: BufferMut>(
1014 &mut self,
1015 id: &IcmpApiSocketId<I, C>,
1016 body: B,
1017 send_token: <C::BindingsContext as IcmpEchoBindingsTypes>::SendToken,
1018 ) -> Result<(), datagram::SendError<packet_formats::error::ParseError>> {
1019 self.datagram().send_conn(id, body, send_token)
1020 }
1021
1022 pub fn send_to<B: BufferMut>(
1026 &mut self,
1027 id: &IcmpApiSocketId<I, C>,
1028 remote_ip: Option<
1029 ZonedAddr<
1030 SpecifiedAddr<I::Addr>,
1031 <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
1032 >,
1033 >,
1034 body: B,
1035 send_token: <C::BindingsContext as IcmpEchoBindingsTypes>::SendToken,
1036 ) -> Result<(), datagram::SendToError<packet_formats::error::ParseError>> {
1037 self.datagram().send_to(id, remote_ip, (), body, send_token)
1038 }
1039
1040 pub fn collect_all_sockets(&mut self) -> Vec<IcmpApiSocketId<I, C>> {
1043 self.datagram().collect_all_sockets()
1044 }
1045
1046 pub fn inspect<N>(&mut self, inspector: &mut N)
1048 where
1049 N: Inspector
1050 + InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1051 for<'a> N::ChildInspector<'a>:
1052 InspectorDeviceExt<<C::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
1053 {
1054 DatagramStateContext::for_each_socket(self.core_ctx(), |_ctx, socket_id, socket_state| {
1055 inspector.record_debug_child(socket_id, |inspector| {
1056 socket_state.record_common_info(inspector);
1057 });
1058 });
1059 }
1060}
1061
1062pub enum IcmpEchoIpTransportContext {}
1068
1069impl EchoTransportContextMarker for IcmpEchoIpTransportContext {}
1070
1071impl<I: IpExt, BC: IcmpEchoBindingsContext<I, CC::DeviceId>, CC: IcmpEchoBoundStateContext<I, BC>>
1072 IpTransportContext<I, BC, CC> for IcmpEchoIpTransportContext
1073{
1074 type EarlyDemuxSocket = !;
1075
1076 fn early_demux<B: ParseBuffer>(
1077 _core_ctx: &mut CC,
1078 _device: &CC::DeviceId,
1079 _src_ip: I::Addr,
1080 _dst_ip: I::Addr,
1081 _buffer: B,
1082 ) -> Option<Self::EarlyDemuxSocket> {
1083 None
1084 }
1085
1086 fn receive_icmp_error(
1087 core_ctx: &mut CC,
1088 _bindings_ctx: &mut BC,
1089 _device: &CC::DeviceId,
1090 original_src_ip: Option<SpecifiedAddr<I::Addr>>,
1091 original_dst_ip: SpecifiedAddr<I::Addr>,
1092 mut original_body: &[u8],
1093 err: I::ErrorCode,
1094 ) {
1095 let echo_request = original_body
1096 .parse::<IcmpPacketRaw<I, _, IcmpEchoRequest>>()
1097 .expect("received non-echo request");
1098
1099 let original_src_ip = match original_src_ip {
1100 Some(ip) => ip,
1101 None => {
1102 trace!("IcmpIpTransportContext::receive_icmp_error: unspecified source IP address");
1103 return;
1104 }
1105 };
1106 let original_src_ip: SocketIpAddr<_> = match original_src_ip.try_into() {
1107 Ok(ip) => ip,
1108 Err(AddrIsMappedError {}) => {
1109 trace!("IcmpIpTransportContext::receive_icmp_error: mapped source IP address");
1110 return;
1111 }
1112 };
1113 let original_dst_ip: SocketIpAddr<_> = match original_dst_ip.try_into() {
1114 Ok(ip) => ip,
1115 Err(AddrIsMappedError {}) => {
1116 trace!("IcmpIpTransportContext::receive_icmp_error: mapped destination IP address");
1117 return;
1118 }
1119 };
1120
1121 let Some(id) = NonZeroU16::new(echo_request.message().id()) else {
1124 debug!(
1125 "ICMP received ICMP error {:?} from {:?}, to {:?} with an ID of 0",
1126 err, original_dst_ip, original_src_ip,
1127 );
1128 return;
1129 };
1130
1131 core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1132 if let Some(conn) = sockets.socket_map.conns().get_by_addr(&ConnAddr {
1133 ip: ConnIpAddr { local: (original_src_ip, id), remote: (original_dst_ip, ()) },
1134 device: None,
1135 }) {
1136 debug!(
1139 "ICMP received ICMP error {:?} from {:?}, to {:?} on socket {:?}",
1140 err, original_dst_ip, original_src_ip, conn
1141 );
1142 CounterContext::<IcmpRxCounters<I>>::counters(core_ctx)
1143 .error_delivered_to_socket
1144 .increment()
1145 } else {
1146 trace!(
1147 "IcmpIpTransportContext::receive_icmp_error: Got ICMP error message for \
1148 nonexistent ICMP echo socket; either the socket responsible has since been \
1149 removed, or the error message was sent in error or corrupted"
1150 );
1151 }
1152 })
1153 }
1154
1155 fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
1156 core_ctx: &mut CC,
1157 bindings_ctx: &mut BC,
1158 device: &CC::DeviceId,
1159 src_ip: I::RecvSrcAddr,
1160 dst_ip: SpecifiedAddr<I::Addr>,
1161 mut buffer: B,
1162 info: &mut LocalDeliveryPacketInfo<I, H>,
1163 _early_demux_socket: Option<!>,
1164 ) -> Result<(), (B, I::IcmpError)> {
1165 let LocalDeliveryPacketInfo { meta, header_info: _, marks: _, packet_type: _, gso_info: _ } =
1166 info;
1167 let ReceiveIpPacketMeta { broadcast: _, transparent_override, parsing_context: _ } = meta;
1168 if let Some(delivery) = transparent_override.as_ref() {
1169 unreachable!(
1170 "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1171 transparent proxy rules can only be configured for TCP and UDP packets"
1172 );
1173 }
1174 let echo_reply =
1178 buffer.parse::<IcmpPacketRaw<I, _, IcmpEchoReply>>().expect("received non-echo reply");
1179 let Some(id) = NonZeroU16::new(echo_reply.message().id()) else { return Ok(()) };
1181
1182 let meta = echo_reply.parse_metadata();
1184 buffer.undo_parse(meta);
1185
1186 let src_ip = match SpecifiedAddr::new(src_ip.into_addr()) {
1187 Some(src_ip) => src_ip,
1188 None => {
1189 trace!("receive_icmp_echo_reply: unspecified source address");
1190 return Ok(());
1191 }
1192 };
1193 let src_ip: SocketIpAddr<_> = match src_ip.try_into() {
1194 Ok(src_ip) => src_ip,
1195 Err(AddrIsMappedError {}) => {
1196 trace!("receive_icmp_echo_reply: mapped source address");
1197 return Ok(());
1198 }
1199 };
1200 let dst_ip: SocketIpAddr<_> = match dst_ip.try_into() {
1201 Ok(dst_ip) => dst_ip,
1202 Err(AddrIsMappedError {}) => {
1203 trace!("receive_icmp_echo_reply: mapped destination address");
1204 return Ok(());
1205 }
1206 };
1207
1208 core_ctx.with_icmp_ctx_and_sockets_mut(|core_ctx, sockets| {
1209 let mut addrs_to_search = AddrVecIter::<I, CC::WeakDeviceId, IcmpAddrSpec>::with_device(
1210 ConnIpAddr { local: (dst_ip, id), remote: (src_ip, ()) }.into(),
1211 device.downgrade(),
1212 );
1213 let socket = match addrs_to_search.try_for_each(|addr_vec| {
1214 match addr_vec {
1215 AddrVec::Conn(c) => {
1216 if let Some(id) = sockets.socket_map.conns().get_by_addr(&c) {
1217 return ControlFlow::Break(id);
1218 }
1219 }
1220 AddrVec::Listen(l) => {
1221 if let Some(id) = sockets.socket_map.listeners().get_by_addr(&l) {
1222 return ControlFlow::Break(id);
1223 }
1224 }
1225 }
1226 ControlFlow::Continue(())
1227 }) {
1228 ControlFlow::Continue(()) => None,
1229 ControlFlow::Break(id) => Some(id),
1230 };
1231 if let Some(socket) = socket {
1232 trace!("receive_icmp_echo_reply: Received echo reply for local socket");
1233 match bindings_ctx.receive_icmp_echo_reply(
1234 socket,
1235 device,
1236 src_ip.addr(),
1237 dst_ip.addr(),
1238 id.get(),
1239 buffer,
1240 ) {
1241 Ok(()) => {}
1242 Err(ReceiveIcmpEchoError::QueueFull) => {
1243 core_ctx.counters().queue_full.increment();
1244 }
1245 }
1246 return;
1247 }
1248 trace!("receive_icmp_echo_reply: Received echo reply with no local socket");
1265 });
1266 Ok(())
1267 }
1268}
1269
1270#[cfg(test)]
1271mod tests {
1272 use alloc::rc::Rc;
1273 use alloc::vec;
1274 use core::cell::RefCell;
1275 use core::ops::{Deref, DerefMut};
1276
1277 use assert_matches::assert_matches;
1278 use ip_test_macro::ip_test;
1279 use net_declare::net_ip_v6;
1280 use net_types::Witness;
1281 use net_types::ip::Ipv6;
1282 use netstack3_base::socket::StrictlyZonedAddr;
1283 use netstack3_base::testutil::{
1284 FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeSendToken, FakeWeakDeviceId, TestIpExt,
1285 };
1286 use netstack3_base::{CtxPair, Icmpv4ErrorCode, Icmpv6ErrorCode, NetworkSerializationContext};
1287 use netstack3_ip::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx, InnerFakeIpSocketCtx};
1288 use netstack3_ip::{LocalDeliveryPacketInfo, SendIpPacketMeta};
1289 use packet::{Buf, EmptyBuf, NestableSerializer as _, Serializer};
1290 use packet_formats::icmp::{
1291 IcmpDestUnreachable, IcmpPacket, IcmpParseArgs, IcmpZeroCode, Icmpv4DestUnreachableCode,
1292 Icmpv6DestUnreachableCode,
1293 };
1294
1295 use super::*;
1296
1297 const REMOTE_ID: u16 = 27;
1298 const ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1299 const SEQ_NUM: u16 = 0xF0;
1300
1301 impl<I: IpExt, D: WeakDeviceIdentifier, BT: IcmpEchoBindingsTypes> IcmpSocketId<I, D, BT> {
1303 fn get(&self) -> impl Deref<Target = IcmpSocketState<I, D, BT>> + '_ {
1304 self.state().read()
1305 }
1306
1307 fn get_mut(&self) -> impl DerefMut<Target = IcmpSocketState<I, D, BT>> + '_ {
1308 self.state().write()
1309 }
1310 }
1311
1312 struct FakeIcmpCoreCtxState<I: IpExt> {
1313 bound_sockets:
1314 Rc<RefCell<BoundSockets<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>>>,
1315 all_sockets: IcmpSocketSet<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1316 ip_socket_ctx: FakeIpSocketCtx<I, FakeDeviceId>,
1317 rx_counters: IcmpRxCounters<I>,
1318 }
1319
1320 impl<I: IpExt> InnerFakeIpSocketCtx<I, FakeDeviceId> for FakeIcmpCoreCtxState<I> {
1321 fn fake_ip_socket_ctx_mut(&mut self) -> &mut FakeIpSocketCtx<I, FakeDeviceId> {
1322 &mut self.ip_socket_ctx
1323 }
1324 }
1325
1326 impl<I: IpExt + TestIpExt> Default for FakeIcmpCoreCtxState<I> {
1327 fn default() -> Self {
1328 Self {
1329 bound_sockets: Default::default(),
1330 all_sockets: Default::default(),
1331 ip_socket_ctx: FakeIpSocketCtx::new(core::iter::once(FakeDeviceConfig {
1332 device: FakeDeviceId,
1333 local_ips: vec![I::TEST_ADDRS.local_ip],
1334 remote_ips: vec![I::TEST_ADDRS.remote_ip],
1335 })),
1336 rx_counters: Default::default(),
1337 }
1338 }
1339 }
1340
1341 type FakeIcmpCoreCtx<I> = FakeCoreCtx<
1342 FakeIcmpCoreCtxState<I>,
1343 SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
1344 FakeDeviceId,
1345 >;
1346 type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<(), (), FakeIcmpBindingsCtxState<I>, ()>;
1347 type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
1348
1349 #[derive(Derivative)]
1350 #[derivative(Default)]
1351
1352 struct FakeIcmpBindingsCtxState<I: IpExt> {
1353 received: Vec<ReceivedEchoPacket<I>>,
1354 #[derivative(Default(value = "usize::MAX"))]
1355 max_size: usize,
1356 }
1357
1358 #[derive(Debug)]
1359 struct ReceivedEchoPacket<I: IpExt> {
1360 src_ip: I::Addr,
1361 dst_ip: I::Addr,
1362 socket: IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1363 id: u16,
1364 data: Vec<u8>,
1365 }
1366
1367 impl<I: IpExt> IcmpEchoContextMarker for FakeIcmpCoreCtx<I> {}
1368
1369 impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtxState<I> {
1370 fn counters(&self) -> &IcmpRxCounters<I> {
1371 &self.rx_counters
1372 }
1373 }
1374
1375 impl<I: IpExt> IcmpEchoBoundStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1376 type IpSocketsCtx<'a> = Self;
1377
1378 fn with_icmp_ctx_and_sockets_mut<
1379 O,
1380 F: FnOnce(
1381 &mut Self::IpSocketsCtx<'_>,
1382 &mut BoundSockets<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1383 ) -> O,
1384 >(
1385 &mut self,
1386 cb: F,
1387 ) -> O {
1388 let bound_sockets = self.state.bound_sockets.clone();
1389 let mut bound_sockets = bound_sockets.borrow_mut();
1390 cb(self, &mut bound_sockets)
1391 }
1392 }
1393
1394 impl<I: IpExt> IcmpEchoStateContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
1395 type SocketStateCtx<'a> = Self;
1396
1397 fn with_all_sockets_mut<
1398 O,
1399 F: FnOnce(&mut IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1400 >(
1401 &mut self,
1402 cb: F,
1403 ) -> O {
1404 cb(&mut self.state.all_sockets)
1405 }
1406
1407 fn with_all_sockets<
1408 O,
1409 F: FnOnce(&IcmpSocketSet<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>) -> O,
1410 >(
1411 &mut self,
1412 cb: F,
1413 ) -> O {
1414 cb(&self.state.all_sockets)
1415 }
1416
1417 fn with_socket_state<
1418 O,
1419 F: FnOnce(
1420 &mut Self::SocketStateCtx<'_>,
1421 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1422 ) -> O,
1423 >(
1424 &mut self,
1425 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1426 cb: F,
1427 ) -> O {
1428 cb(self, &id.get())
1429 }
1430
1431 fn with_socket_state_mut<
1432 O,
1433 F: FnOnce(
1434 &mut Self::SocketStateCtx<'_>,
1435 &mut IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1436 ) -> O,
1437 >(
1438 &mut self,
1439 id: &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1440 cb: F,
1441 ) -> O {
1442 cb(self, &mut id.get_mut())
1443 }
1444
1445 fn with_bound_state_context<O, F: FnOnce(&mut Self::SocketStateCtx<'_>) -> O>(
1446 &mut self,
1447 cb: F,
1448 ) -> O {
1449 cb(self)
1450 }
1451
1452 fn for_each_socket<
1453 F: FnMut(
1454 &mut Self::SocketStateCtx<'_>,
1455 &IcmpSocketId<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1456 &IcmpSocketState<I, Self::WeakDeviceId, FakeIcmpBindingsCtx<I>>,
1457 ),
1458 >(
1459 &mut self,
1460 mut cb: F,
1461 ) {
1462 let socks = self
1463 .state
1464 .all_sockets
1465 .keys()
1466 .map(|id| IcmpSocketId::from(id.clone()))
1467 .collect::<Vec<_>>();
1468 for id in socks {
1469 cb(self, &id, &id.get());
1470 }
1471 }
1472 }
1473
1474 impl<I: IpExt> IcmpEchoBindingsContext<I, FakeDeviceId> for FakeIcmpBindingsCtx<I> {
1475 fn receive_icmp_echo_reply<B: BufferMut>(
1476 &mut self,
1477 socket: &IcmpSocketId<I, FakeWeakDeviceId<FakeDeviceId>, FakeIcmpBindingsCtx<I>>,
1478 _device_id: &FakeDeviceId,
1479 src_ip: I::Addr,
1480 dst_ip: I::Addr,
1481 id: u16,
1482 data: B,
1483 ) -> Result<(), ReceiveIcmpEchoError> {
1484 if self.state.received.len() < self.state.max_size {
1485 self.state.received.push(ReceivedEchoPacket {
1486 src_ip,
1487 dst_ip,
1488 id,
1489 data: data.to_flattened_vec(),
1490 socket: socket.clone(),
1491 });
1492 Ok(())
1493 } else {
1494 Err(ReceiveIcmpEchoError::QueueFull)
1495 }
1496 }
1497 }
1498
1499 impl<I: IpExt> IcmpEchoBindingsTypes for FakeIcmpBindingsCtx<I> {
1500 type ExternalData<II: Ip> = ();
1501 type SendToken = FakeSendToken;
1502 }
1503
1504 #[test]
1505 fn test_connect_dual_stack_fails() {
1506 let mut ctx = FakeIcmpCtx::<Ipv6>::default();
1509 let mut api = IcmpEchoSocketApi::<Ipv6, _>::new(ctx.as_mut());
1510 let conn = api.create();
1511 assert_eq!(
1512 api.connect(
1513 &conn,
1514 Some(ZonedAddr::Unzoned(
1515 SpecifiedAddr::new(net_ip_v6!("::ffff:192.0.2.1")).unwrap(),
1516 )),
1517 REMOTE_ID,
1518 ),
1519 Err(datagram::ConnectError::RemoteUnexpectedlyMapped)
1520 );
1521 }
1522
1523 #[ip_test(I)]
1524 fn send_invalid_icmp_echo<I: TestIpExt + IpExt>() {
1525 let mut ctx = FakeIcmpCtx::<I>::default();
1526 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1527 let conn = api.create();
1528 api.connect(&conn, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1529
1530 let pb = IcmpPacketBuilder::<I, _>::new(
1531 I::TEST_ADDRS.local_ip.get(),
1532 I::TEST_ADDRS.remote_ip.get(),
1533 IcmpZeroCode,
1534 packet_formats::icmp::IcmpEchoReply::new(0, 1),
1535 );
1536 let buf = pb
1537 .wrap_body(Buf::new(Vec::new(), ..))
1538 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1539 .unwrap()
1540 .into_inner();
1541 assert_matches!(
1542 api.send(&conn, buf, FakeSendToken::default()),
1543 Err(datagram::SendError::SerializeError(
1544 packet_formats::error::ParseError::NotExpected
1545 ))
1546 );
1547 }
1548
1549 #[ip_test(I)]
1550 fn get_info<I: TestIpExt + IpExt>() {
1551 let mut ctx = FakeIcmpCtx::<I>::default();
1552 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1553
1554 let id = api.create();
1555 assert_eq!(api.get_info(&id), datagram::SocketInfo::Unbound);
1556
1557 api.bind(&id, None, Some(ICMP_ID)).unwrap();
1558 assert_eq!(
1559 api.get_info(&id),
1560 datagram::SocketInfo::Listener(datagram::ListenerInfo {
1561 local_ip: None,
1562 local_identifier: ICMP_ID
1563 })
1564 );
1565
1566 api.connect(&id, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1567 assert_eq!(
1568 api.get_info(&id),
1569 datagram::SocketInfo::Connected(datagram::ConnInfo {
1570 local_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.local_ip),
1571 local_identifier: ICMP_ID,
1572 remote_ip: StrictlyZonedAddr::new_unzoned_or_panic(I::TEST_ADDRS.remote_ip),
1573 remote_identifier: REMOTE_ID,
1574 })
1575 );
1576 }
1577
1578 #[ip_test(I)]
1579 fn send<I: TestIpExt + IpExt>() {
1580 let mut ctx = FakeIcmpCtx::<I>::default();
1581 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1582 let sock = api.create();
1583
1584 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1585 api.connect(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), REMOTE_ID).unwrap();
1586
1587 let packet = Buf::new([1u8, 2, 3, 4], ..)
1588 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1589 I::UNSPECIFIED_ADDRESS,
1590 I::UNSPECIFIED_ADDRESS,
1591 IcmpZeroCode,
1592 IcmpEchoRequest::new(0, SEQ_NUM),
1594 ))
1595 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1596 .unwrap()
1597 .unwrap_b();
1598 api.send(&sock, Buf::new(packet, ..), FakeSendToken::default()).unwrap();
1599 let frames = ctx.core_ctx.frames.take_frames();
1600 let (SendIpPacketMeta { device: _, src_ip, dst_ip, .. }, body) =
1601 assert_matches!(&frames[..], [f] => f);
1602 assert_eq!(dst_ip, &I::TEST_ADDRS.remote_ip);
1603
1604 let mut body = &body[..];
1605 let echo_req: IcmpPacket<I, _, IcmpEchoRequest> =
1606 body.parse_with(IcmpParseArgs::new(src_ip.get(), dst_ip.get())).unwrap();
1607 assert_eq!(echo_req.message().id(), ICMP_ID.get());
1608 assert_eq!(echo_req.message().seq(), SEQ_NUM);
1609 }
1610
1611 #[ip_test(I)]
1612 fn receive<I: TestIpExt + IpExt>() {
1613 let mut ctx = FakeIcmpCtx::<I>::default();
1614 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1615 let sock = api.create();
1616
1617 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1618
1619 let reply = IcmpPacketBuilder::<I, _>::new(
1620 I::UNSPECIFIED_ADDRESS,
1622 I::UNSPECIFIED_ADDRESS,
1623 IcmpZeroCode,
1624 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1625 )
1626 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1627 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1628 .unwrap();
1629
1630 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1631 let src_ip = I::TEST_ADDRS.remote_ip;
1632 let dst_ip = I::TEST_ADDRS.local_ip;
1633 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1634 core_ctx,
1635 bindings_ctx,
1636 &FakeDeviceId,
1637 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1638 dst_ip,
1639 reply.clone(),
1640 &mut LocalDeliveryPacketInfo::default(),
1641 None,
1642 )
1643 .unwrap();
1644
1645 let received = core::mem::take(&mut bindings_ctx.state.received);
1646 let ReceivedEchoPacket {
1647 src_ip: got_src_ip,
1648 dst_ip: got_dst_ip,
1649 socket: got_socket,
1650 id: got_id,
1651 data: got_data,
1652 } = assert_matches!(&received[..], [f] => f);
1653 assert_eq!(got_src_ip, &src_ip.get());
1654 assert_eq!(got_dst_ip, &dst_ip.get());
1655 assert_eq!(got_socket, &sock);
1656 assert_eq!(got_id, &ICMP_ID.get());
1657 assert_eq!(&got_data[..], reply.as_ref());
1658 }
1659
1660 #[ip_test(I)]
1661 fn receive_no_socket<I: TestIpExt + IpExt>() {
1662 let mut ctx = FakeIcmpCtx::<I>::default();
1663 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1664 let sock = api.create();
1665
1666 const BIND_ICMP_ID: NonZeroU16 = NonZeroU16::new(10).unwrap();
1667 const OTHER_ICMP_ID: NonZeroU16 = NonZeroU16::new(16).unwrap();
1668
1669 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(BIND_ICMP_ID))
1670 .unwrap();
1671
1672 let reply = IcmpPacketBuilder::<I, _>::new(
1673 I::UNSPECIFIED_ADDRESS,
1675 I::UNSPECIFIED_ADDRESS,
1676 IcmpZeroCode,
1677 IcmpEchoReply::new(OTHER_ICMP_ID.get(), SEQ_NUM),
1678 )
1679 .wrap_body(EmptyBuf)
1680 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1681 .unwrap();
1682
1683 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1684 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1685 core_ctx,
1686 bindings_ctx,
1687 &FakeDeviceId,
1688 I::RecvSrcAddr::new(I::TEST_ADDRS.remote_ip.get()).unwrap(),
1689 I::TEST_ADDRS.local_ip,
1690 reply,
1691 &mut LocalDeliveryPacketInfo::default(),
1692 None,
1693 )
1694 .unwrap();
1695 assert_matches!(&bindings_ctx.state.received[..], []);
1696 }
1697
1698 #[ip_test(I)]
1699 fn receive_queue_full<I: TestIpExt + IpExt>() {
1700 let mut ctx = FakeIcmpCtx::<I>::default();
1701 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1702 let sock = api.create();
1703
1704 api.bind(&sock, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(ICMP_ID)).unwrap();
1705
1706 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1708 bindings_ctx.state.max_size = 0;
1709
1710 let reply = IcmpPacketBuilder::<I, _>::new(
1711 I::UNSPECIFIED_ADDRESS,
1713 I::UNSPECIFIED_ADDRESS,
1714 IcmpZeroCode,
1715 IcmpEchoReply::new(ICMP_ID.get(), SEQ_NUM),
1716 )
1717 .wrap_body(Buf::new([1u8, 2, 3, 4], ..))
1718 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1719 .unwrap();
1720
1721 let src_ip = I::TEST_ADDRS.remote_ip;
1722 let dst_ip = I::TEST_ADDRS.local_ip;
1723 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_ip_packet(
1724 core_ctx,
1725 bindings_ctx,
1726 &FakeDeviceId,
1727 I::RecvSrcAddr::new(src_ip.get()).unwrap(),
1728 dst_ip,
1729 reply,
1730 &mut LocalDeliveryPacketInfo::default(),
1731 None,
1732 )
1733 .unwrap();
1734
1735 assert_eq!(core_ctx.counters().queue_full.get(), 1);
1736 }
1737
1738 #[ip_test(I, test = false)]
1739 #[test_case::test_matrix(
1740 [MarkDomain::Mark1, MarkDomain::Mark2],
1741 [None, Some(0), Some(1)]
1742 )]
1743 fn icmp_socket_marks<I: TestIpExt + IpExt>(domain: MarkDomain, mark: Option<u32>) {
1744 let mut ctx = FakeIcmpCtx::<I>::default();
1745 let mut api = IcmpEchoSocketApi::<I, _>::new(ctx.as_mut());
1746 let socket = api.create();
1747
1748 assert_eq!(api.get_mark(&socket, domain), Mark(None));
1750
1751 let mark = Mark(mark);
1752 api.set_mark(&socket, domain, mark);
1754 assert_eq!(api.get_mark(&socket, domain), mark);
1755 }
1756
1757 #[ip_test(I)]
1763 fn icmp_error_with_inner_icmp_echo_with_id_0<I: TestIpExt + IpExt>() {
1764 let mut ctx = FakeIcmpCtx::<I>::default();
1765
1766 let src_ip = I::TEST_ADDRS.remote_ip;
1767 let dst_ip = I::TEST_ADDRS.local_ip;
1768
1769 let original_body = Buf::new([1, 2, 3, 4], ..)
1770 .wrap_in(IcmpPacketBuilder::<I, _>::new(
1771 *src_ip,
1772 *dst_ip,
1773 IcmpZeroCode,
1774 IcmpEchoRequest::new(0, 1),
1777 ))
1778 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1779 .unwrap()
1780 .unwrap_b();
1781
1782 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
1783 <IcmpEchoIpTransportContext as IpTransportContext<I, _, _>>::receive_icmp_error(
1784 core_ctx,
1785 bindings_ctx,
1786 &FakeDeviceId,
1787 Some(src_ip),
1788 dst_ip,
1789 original_body.as_ref(),
1790 I::map_ip_out(
1791 (),
1792 |()| {
1793 Icmpv4ErrorCode::DestUnreachable(
1794 Icmpv4DestUnreachableCode::DestNetworkUnreachable,
1795 IcmpDestUnreachable::default(),
1796 )
1797 },
1798 |()| Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::AddrUnreachable),
1799 ),
1800 )
1801 }
1802}