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