1use alloc::vec::Vec;
8use core::convert::Infallible as Never;
9use core::fmt::Debug;
10use derivative::Derivative;
11
12use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
13use log::trace;
14use net_types::ethernet::Mac;
15use net_types::ip::{Ipv4, Ipv6, Mtu};
16use netstack3_base::sync::Mutex;
17use netstack3_base::{
18 AnyDevice, BroadcastIpExt, ChecksumOffloadSpec, ChecksumRxOffloading, CoreTimerContext, Device,
19 DeviceIdAnyCompatContext, DeviceIdContext, FrameDestination, NetworkParsingContext,
20 NetworkSerializer, RecvFrameContext, RecvIpFrameMeta, ResourceCounterContext, SendFrameError,
21 SendFrameErrorReason, SendableFrameMeta, StrongDeviceIdentifier, TimerContext,
22 TxMetadataBindingsTypes, WeakDeviceIdentifier,
23};
24use netstack3_ip::{DeviceIpLayerMetadata, IpCounters, IpPacketDestination};
25use packet::{Buf, Buffer as _, BufferMut, FragmentedBuffer as _, NestablePacketBuilder as _};
26use packet_formats::ethernet::{
27 EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck, EthernetIpExt,
28};
29
30use crate::internal::base::{
31 DeviceCounters, DeviceLayerTypes, DeviceReceiveFrameSpec, EthernetDeviceCounters,
32};
33use crate::internal::id::{BaseDeviceId, BasePrimaryDeviceId, BaseWeakDeviceId, WeakDeviceId};
34use crate::internal::queue::rx::{
35 ReceiveDequeFrameContext, ReceiveQueue, ReceiveQueueState, ReceiveQueueTypes,
36};
37use crate::internal::queue::tx::{
38 BufVecU8Allocator, TransmitQueue, TransmitQueueHandler, TransmitQueueState,
39 TxQueuePacketMetadataCommon,
40};
41use crate::internal::queue::{DequeueState, DeviceBufferSpec, TransmitQueueFrameError};
42use crate::internal::socket::{
43 DeviceSocketHandler, DeviceSocketMetadata, DeviceSocketSendTypes, EthernetHeaderParams,
44 ReceivedFrame,
45};
46use crate::internal::state::{DeviceStateSpec, IpLinkDeviceState};
47
48const LOOPBACK_MAC: Mac = Mac::UNSPECIFIED;
50
51pub type LoopbackWeakDeviceId<BT> = BaseWeakDeviceId<LoopbackDevice, BT>;
58
59pub type LoopbackDeviceId<BT> = BaseDeviceId<LoopbackDevice, BT>;
65
66pub type LoopbackPrimaryDeviceId<BT> = BasePrimaryDeviceId<LoopbackDevice, BT>;
68
69#[derive(Copy, Clone)]
71pub enum LoopbackDevice {}
72
73impl Device for LoopbackDevice {}
74
75impl<BT> DeviceBufferSpec<BT> for LoopbackDevice {
76 type TxBuffer = Buf<Vec<u8>>;
77 type TxAllocator = BufVecU8Allocator;
78}
79
80impl DeviceStateSpec for LoopbackDevice {
81 type State<BT: DeviceLayerTypes> = LoopbackDeviceState<WeakDeviceId<BT>, BT>;
82 type External<BT: DeviceLayerTypes> = BT::LoopbackDeviceState;
83 type CreationProperties = LoopbackCreationProperties;
84 type Counters = EthernetDeviceCounters;
85 type TimerId<D: WeakDeviceIdentifier> = Never;
86
87 fn new_device_state<
88 CC: CoreTimerContext<Self::TimerId<CC::WeakDeviceId>, BC> + DeviceIdContext<Self>,
89 BC: DeviceLayerTypes + TimerContext,
90 >(
91 _bindings_ctx: &mut BC,
92 _self_id: CC::WeakDeviceId,
93 LoopbackCreationProperties { mtu }: Self::CreationProperties,
94 tx_allocator: <Self as DeviceBufferSpec<BC>>::TxAllocator,
95 ) -> Self::State<BC>
96 where
97 Self: DeviceBufferSpec<BC>,
98 {
99 LoopbackDeviceState {
100 counters: Default::default(),
101 mtu,
102 rx_queue: Default::default(),
103 tx_queue: TransmitQueue::new(tx_allocator, ChecksumOffloadSpec::generic()),
104 }
105 }
106
107 const IS_LOOPBACK: bool = true;
108 const DEBUG_TYPE: &'static str = "Loopback";
109
110 fn tx_offload_spec<BT: DeviceLayerTypes>(
111 state: &Self::State<BT>,
112 ) -> Option<ChecksumOffloadSpec> {
113 Some(state.tx_queue.tx_offload_spec())
114 }
115}
116
117#[derive(Debug)]
119pub struct LoopbackCreationProperties {
120 pub mtu: Mtu,
122}
123
124pub struct LoopbackDeviceState<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
126 pub counters: EthernetDeviceCounters,
128 pub mtu: Mtu,
130 pub rx_queue: ReceiveQueue<LoopbackRxQueueMeta<D, BT>, Buf<Vec<u8>>>,
132 pub tx_queue: TransmitQueue<
134 LoopbackTxQueueMeta<D, BT>,
135 <LoopbackDevice as DeviceBufferSpec<BT>>::TxBuffer,
136 <LoopbackDevice as DeviceBufferSpec<BT>>::TxAllocator,
137 >,
138}
139
140#[derive(Derivative)]
141#[derivative(Default(bound = ""))]
142pub struct LoopbackTxQueueMeta<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
144 target_device: Option<D>,
147 ip_layer_metadata: DeviceIpLayerMetadata<BT>,
150}
151
152impl<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> TxQueuePacketMetadataCommon
153 for LoopbackTxQueueMeta<D, BT>
154{
155 fn set_checksum_offload_result(
156 &mut self,
157 _result: Option<netstack3_base::ChecksumOffloadResult>,
158 ) {
159 }
162}
163
164#[derive(Derivative)]
166#[derivative(Debug(bound = ""))]
167pub struct LoopbackRxQueueMeta<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
168 target_device: Option<D>,
171 ip_layer_metadata: DeviceIpLayerMetadata<BT>,
174}
175
176impl<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> From<LoopbackTxQueueMeta<D, BT>>
177 for LoopbackRxQueueMeta<D, BT>
178{
179 fn from(
180 LoopbackTxQueueMeta { target_device, ip_layer_metadata }: LoopbackTxQueueMeta<D, BT>,
181 ) -> Self {
182 Self { target_device, ip_layer_metadata }
183 }
184}
185
186impl<BT: DeviceLayerTypes>
187 OrderedLockAccess<ReceiveQueueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
188 for IpLinkDeviceState<LoopbackDevice, BT>
189{
190 type Lock = Mutex<ReceiveQueueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
191 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
192 OrderedLockRef::new(&self.link.rx_queue.queue)
193 }
194}
195
196impl<BT: DeviceLayerTypes>
197 OrderedLockAccess<DequeueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
198 for IpLinkDeviceState<LoopbackDevice, BT>
199{
200 type Lock = Mutex<DequeueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
201 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
202 OrderedLockRef::new(&self.link.rx_queue.deque)
203 }
204}
205
206impl<BT: DeviceLayerTypes>
207 OrderedLockAccess<
208 TransmitQueueState<
209 LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>,
210 Buf<Vec<u8>>,
211 BufVecU8Allocator,
212 >,
213 > for IpLinkDeviceState<LoopbackDevice, BT>
214{
215 type Lock = Mutex<
216 TransmitQueueState<
217 LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>,
218 Buf<Vec<u8>>,
219 BufVecU8Allocator,
220 >,
221 >;
222 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
223 OrderedLockRef::new(&self.link.tx_queue.queue)
224 }
225}
226
227impl<BT: DeviceLayerTypes>
228 OrderedLockAccess<DequeueState<LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
229 for IpLinkDeviceState<LoopbackDevice, BT>
230{
231 type Lock = Mutex<DequeueState<LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
232 fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
233 OrderedLockRef::new(&self.link.tx_queue.deque)
234 }
235}
236
237impl DeviceSocketSendTypes for LoopbackDevice {
238 type Metadata = Option<EthernetHeaderParams>;
241}
242
243impl<CC, BC> ReceiveDequeFrameContext<LoopbackDevice, BC> for CC
244where
245 CC: DeviceIdContext<LoopbackDevice>
246 + ResourceCounterContext<Self::DeviceId, EthernetDeviceCounters>
247 + ReceiveQueueTypes<
248 LoopbackDevice,
249 BC,
250 Meta = LoopbackRxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
251 >,
252 CC: DeviceIdAnyCompatContext<LoopbackDevice>
254 + RecvFrameContext<
255 RecvIpFrameMeta<
256 <CC as DeviceIdContext<AnyDevice>>::DeviceId,
257 DeviceIpLayerMetadata<BC>,
258 Ipv4,
259 >,
260 BC,
261 > + RecvFrameContext<
262 RecvIpFrameMeta<
263 <CC as DeviceIdContext<AnyDevice>>::DeviceId,
264 DeviceIpLayerMetadata<BC>,
265 Ipv6,
266 >,
267 BC,
268 > + ResourceCounterContext<<CC as DeviceIdContext<AnyDevice>>::DeviceId, DeviceCounters>
269 + ResourceCounterContext<<CC as DeviceIdContext<AnyDevice>>::DeviceId, IpCounters<Ipv4>>
270 + ResourceCounterContext<<CC as DeviceIdContext<AnyDevice>>::DeviceId, IpCounters<Ipv6>>
271 + DeviceSocketHandler<AnyDevice, BC>,
272 CC::Buffer: BufferMut + Debug,
273 BC: DeviceLayerTypes,
274{
275 fn handle_frame(
276 &mut self,
277 bindings_ctx: &mut BC,
278 device_id: &Self::DeviceId,
279 rx_meta: Self::Meta,
280 mut buf: Self::Buffer,
281 ) {
282 let mut buf = Buf::new(buf.as_mut(), ..);
291 let buflen = buf.len();
292
293 let (frame, whole_body) =
294 match buf.parse_with_view::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::NoCheck) {
295 Err(e) => {
296 self.increment_both(&device_id.clone().into(), |counters: &DeviceCounters| {
297 &counters.recv_parse_error
298 });
299 trace!("dropping invalid ethernet frame over loopback: {:?}", e);
300 return;
301 }
302 Ok(e) => e,
303 };
304
305 let LoopbackRxQueueMeta { target_device, ip_layer_metadata } = rx_meta;
306 let target_device: <CC as DeviceIdContext<AnyDevice>>::DeviceId =
307 match target_device.map(|d| d.upgrade()) {
308 Some(Some(dev)) => dev,
310
311 Some(None) => return,
313
314 None => device_id.clone().into(),
316 };
317
318 self.add_both_usize(&target_device, buflen, |counters: &DeviceCounters| {
319 &counters.recv_bytes
320 });
321 self.increment_both(&target_device, |counters: &DeviceCounters| &counters.recv_frame);
322
323 let frame_dest = FrameDestination::from_dest(frame.dst_mac(), Mac::UNSPECIFIED);
324 let ethertype = frame.ethertype();
325
326 DeviceSocketHandler::<AnyDevice, _>::handle_frame(
327 self,
328 bindings_ctx,
329 &target_device,
330 ReceivedFrame::from_ethernet(frame, frame_dest).into(),
331 whole_body,
332 );
333
334 match ethertype {
335 Some(EtherType::Ipv4) => {
336 let local_frame_dst = match frame_dest.check_local() {
337 Some(dst) => dst,
338 None => {
339 self.increment_both(&target_device, |counters: &IpCounters<Ipv4>| {
340 &counters.drop_ip_packet_other_host
341 });
342 return;
343 }
344 };
345 self.increment_both(&target_device, |counters: &DeviceCounters| {
346 &counters.recv_ipv4_delivered
347 });
348 self.receive_frame(
349 bindings_ctx,
350 RecvIpFrameMeta::<_, _, Ipv4>::new(
351 target_device,
352 Some(local_frame_dst),
353 ip_layer_metadata,
354 NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded),
355 ),
356 buf,
357 );
358 }
359 Some(EtherType::Ipv6) => {
360 let local_frame_dst = match frame_dest.check_local() {
361 Some(dst) => dst,
362 None => {
363 self.increment_both(&target_device, |counters: &IpCounters<Ipv6>| {
364 &counters.drop_ip_packet_other_host
365 });
366 return;
367 }
368 };
369 self.increment_both(&target_device, |counters: &DeviceCounters| {
370 &counters.recv_ipv6_delivered
371 });
372 self.receive_frame(
373 bindings_ctx,
374 RecvIpFrameMeta::<_, _, Ipv6>::new(
375 target_device,
376 Some(local_frame_dst),
377 ip_layer_metadata,
378 NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded),
379 ),
380 buf,
381 );
382 }
383 Some(ethertype @ (EtherType::Arp | EtherType::Other(_))) => {
384 self.increment_both(device_id, |counters: &EthernetDeviceCounters| {
385 &counters.recv_unsupported_ethertype
386 });
387 trace!("not handling loopback frame of type {:?}", ethertype)
388 }
389 None => {
390 self.increment_both(device_id, |counters: &EthernetDeviceCounters| {
391 &counters.recv_no_ethertype
392 });
393 trace!("dropping ethernet frame without ethertype");
394 }
395 }
396 }
397}
398
399impl<CC, BC> SendableFrameMeta<CC, BC>
400 for DeviceSocketMetadata<LoopbackDevice, <CC as DeviceIdContext<LoopbackDevice>>::DeviceId>
401where
402 CC: TransmitQueueHandler<
403 LoopbackDevice,
404 BC,
405 Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
406 > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
407 + DeviceIdContext<AnyDevice>,
408 BC: DeviceLayerTypes,
409{
410 fn send_meta<S>(
411 self,
412 core_ctx: &mut CC,
413 bindings_ctx: &mut BC,
414 body: S,
415 ) -> Result<(), SendFrameError<S>>
416 where
417 S: NetworkSerializer,
418 S::Buffer: BufferMut,
419 {
420 let Self { device_id, metadata } = self;
421 let tx_meta = LoopbackTxQueueMeta::default();
422 match metadata {
423 Some(EthernetHeaderParams { dest_addr, protocol }) => send_as_ethernet_frame_to_dst(
424 core_ctx,
425 bindings_ctx,
426 &device_id,
427 body,
428 protocol,
429 dest_addr,
430 tx_meta,
431 ),
432 None => send_ethernet_frame(core_ctx, bindings_ctx, &device_id, body, tx_meta),
433 }
434 }
435}
436
437pub fn send_ip_frame<CC, BC, I, S>(
439 core_ctx: &mut CC,
440 bindings_ctx: &mut BC,
441 device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
442 destination: IpPacketDestination<I, &<CC as DeviceIdContext<AnyDevice>>::DeviceId>,
443 ip_layer_metadata: DeviceIpLayerMetadata<BC>,
444 packet: S,
445) -> Result<(), SendFrameError<S>>
446where
447 CC: TransmitQueueHandler<
448 LoopbackDevice,
449 BC,
450 Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
451 > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
452 + DeviceIdContext<AnyDevice>,
453 BC: DeviceLayerTypes,
454 I: EthernetIpExt + BroadcastIpExt,
455 S: NetworkSerializer,
456 S::Buffer: BufferMut,
457{
458 core_ctx.increment_both(device_id, DeviceCounters::send_frame::<I>);
459
460 let target_device = match destination {
461 IpPacketDestination::Loopback(device) => Some(device.downgrade()),
462 IpPacketDestination::Broadcast(_)
463 | IpPacketDestination::Multicast(_)
464 | IpPacketDestination::Neighbor(_) => None,
465 };
466 send_as_ethernet_frame_to_dst(
467 core_ctx,
468 bindings_ctx,
469 device_id,
470 packet,
471 I::ETHER_TYPE,
472 LOOPBACK_MAC,
473 LoopbackTxQueueMeta { target_device, ip_layer_metadata },
474 )
475}
476
477fn send_as_ethernet_frame_to_dst<CC, BC, S>(
478 core_ctx: &mut CC,
479 bindings_ctx: &mut BC,
480 device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
481 packet: S,
482 protocol: EtherType,
483 dst_mac: Mac,
484 meta: LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
485) -> Result<(), SendFrameError<S>>
486where
487 CC: TransmitQueueHandler<
488 LoopbackDevice,
489 BC,
490 Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
491 > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
492 + DeviceIdContext<AnyDevice>,
493 BC: DeviceLayerTypes,
494 S: NetworkSerializer,
495 S::Buffer: BufferMut,
496{
497 const MIN_BODY_LEN: usize = 0;
503
504 let frame =
505 EthernetFrameBuilder::new(LOOPBACK_MAC, dst_mac, protocol, MIN_BODY_LEN).wrap_body(packet);
506
507 send_ethernet_frame(core_ctx, bindings_ctx, device_id, frame, meta)
508 .map_err(|err| err.into_inner())
509}
510
511fn send_ethernet_frame<CC, BC, S>(
512 core_ctx: &mut CC,
513 bindings_ctx: &mut BC,
514 device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
515 frame: S,
516 meta: LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
517) -> Result<(), SendFrameError<S>>
518where
519 CC: TransmitQueueHandler<
520 LoopbackDevice,
521 BC,
522 Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
523 > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
524 + DeviceIdContext<AnyDevice>,
525 S: NetworkSerializer,
526 S::Buffer: BufferMut,
527 BC: DeviceLayerTypes,
528{
529 core_ctx.increment_both(device_id, |counters: &DeviceCounters| &counters.send_total_frames);
530 match TransmitQueueHandler::<LoopbackDevice, _>::queue_tx_frame(
531 core_ctx,
532 bindings_ctx,
533 device_id,
534 meta,
535 frame,
536 ) {
537 Ok(len) => {
538 core_ctx.add_both_usize(device_id, len, |counters| &counters.send_bytes);
539 core_ctx.increment_both(device_id, |counters: &DeviceCounters| &counters.send_frame);
540 Ok(())
541 }
542 Err(TransmitQueueFrameError::NoQueue(err)) => {
543 unreachable!("loopback never fails to send a frame: {err:?}")
544 }
545 Err(TransmitQueueFrameError::QueueFull(serializer)) => {
546 core_ctx
547 .increment_both(device_id, |counters: &DeviceCounters| &counters.send_queue_full);
548 Err(SendFrameError { serializer, error: SendFrameErrorReason::QueueFull })
549 }
550 Err(TransmitQueueFrameError::SerializeError(err)) => {
551 core_ctx.increment_both(device_id, |counters: &DeviceCounters| {
552 &counters.send_serialize_error
553 });
554 Err(err.err_into())
555 }
556 }
557}
558
559impl DeviceReceiveFrameSpec for LoopbackDevice {
560 type FrameMetadata<D> = Never;
563}