Skip to main content

netstack3_core/device/
loopback.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implementations of traits defined in foreign modules for the types defined
6//! in the loopback module.
7
8use alloc::vec::Vec;
9use core::convert::Infallible as Never;
10
11use lock_order::lock::LockLevelFor;
12use lock_order::relation::LockBefore;
13use log::error;
14use netstack3_base::DeviceIdContext;
15use netstack3_device::loopback::{
16    LoopbackDevice, LoopbackDeviceId, LoopbackRxQueueMeta, LoopbackTxQueueMeta,
17    LoopbackWeakDeviceId,
18};
19use netstack3_device::queue::{
20    BufVecU8Allocator, DequeueState, ReceiveDequeContext, ReceiveQueueContext,
21    ReceiveQueueFullError, ReceiveQueueHandler, ReceiveQueueState, ReceiveQueueTypes,
22    TransmitDequeueContext, TransmitQueueCommon, TransmitQueueContext, TransmitQueueState,
23};
24use netstack3_device::socket::{ParseSentFrameError, SentFrame};
25use netstack3_device::{DeviceLayerTypes, DeviceSendFrameError, IpLinkDeviceState, WeakDeviceId};
26use packet::Buf;
27
28use crate::context::WrapLockLevel;
29use crate::context::prelude::*;
30use crate::device::integration;
31use crate::{BindingsContext, BindingsTypes, CoreCtx};
32
33impl<BT: BindingsTypes, L> DeviceIdContext<LoopbackDevice> for CoreCtx<'_, BT, L> {
34    type DeviceId = LoopbackDeviceId<BT>;
35    type WeakDeviceId = LoopbackWeakDeviceId<BT>;
36}
37
38impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackRxQueue>>
39    ReceiveQueueTypes<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
40{
41    type Meta = LoopbackRxQueueMeta<WeakDeviceId<BC>, BC>;
42    type Buffer = Buf<Vec<u8>>;
43}
44
45impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackRxQueue>>
46    ReceiveQueueContext<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
47{
48    fn with_receive_queue_mut<
49        O,
50        F: FnOnce(&mut ReceiveQueueState<Self::Meta, Buf<Vec<u8>>>) -> O,
51    >(
52        &mut self,
53        device_id: &LoopbackDeviceId<BC>,
54        cb: F,
55    ) -> O {
56        let mut state = integration::device_state(self, device_id);
57        let mut x = state.lock::<crate::lock_ordering::LoopbackRxQueue>();
58        cb(&mut x)
59    }
60}
61
62impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackRxDequeue>>
63    ReceiveDequeContext<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
64{
65    type ReceiveQueueCtx<'a> =
66        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::LoopbackRxDequeue>>;
67
68    fn with_dequed_frames_and_rx_queue_ctx<
69        O,
70        F: FnOnce(&mut DequeueState<Self::Meta, Buf<Vec<u8>>>, &mut Self::ReceiveQueueCtx<'_>) -> O,
71    >(
72        &mut self,
73        device_id: &LoopbackDeviceId<BC>,
74        cb: F,
75    ) -> O {
76        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
77        let (mut x, mut locked) = core_ctx_and_resource
78            .lock_with_and::<crate::lock_ordering::LoopbackRxDequeue, _>(|c| c.right());
79        cb(&mut x, &mut locked.cast_core_ctx())
80    }
81}
82
83impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackTxQueue>>
84    TransmitQueueCommon<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
85{
86    type Meta = LoopbackTxQueueMeta<WeakDeviceId<BC>, BC>;
87    type DequeueContext = Never;
88
89    fn parse_outgoing_frame<'a, 'b>(
90        buf: &'a [u8],
91        _meta: &'b Self::Meta,
92    ) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
93        SentFrame::try_parse_as_ethernet(buf)
94    }
95}
96
97impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackTxQueue>>
98    TransmitQueueContext<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
99{
100    fn with_transmit_queue_mut<
101        O,
102        F: FnOnce(&mut TransmitQueueState<Self::Meta, Buf<Vec<u8>>, BufVecU8Allocator>) -> O,
103    >(
104        &mut self,
105        device_id: &LoopbackDeviceId<BC>,
106        cb: F,
107    ) -> O {
108        let mut state = integration::device_state(self, device_id);
109        let mut x = state.lock::<crate::lock_ordering::LoopbackTxQueue>();
110        cb(&mut x)
111    }
112
113    fn with_transmit_queue<
114        O,
115        F: FnOnce(&TransmitQueueState<Self::Meta, Buf<Vec<u8>>, BufVecU8Allocator>) -> O,
116    >(
117        &mut self,
118        device_id: &LoopbackDeviceId<BC>,
119        cb: F,
120    ) -> O {
121        let mut state = integration::device_state(self, device_id);
122        let x = state.lock::<crate::lock_ordering::LoopbackTxQueue>();
123        cb(&x)
124    }
125
126    fn send_frame(
127        &mut self,
128        bindings_ctx: &mut BC,
129        device_id: &Self::DeviceId,
130        dequeue_context: Option<&mut Never>,
131        meta: Self::Meta,
132        buf: Buf<Vec<u8>>,
133    ) -> Result<(), DeviceSendFrameError> {
134        // Loopack does not support dequeueing context from bindings.
135        match dequeue_context {
136            Some(never) => match *never {},
137            None => (),
138        };
139        // Never handle frames synchronously with the send path - always queue
140        // the frame to be received by the loopback device into a queue which
141        // a dedicated RX task will kick to handle the queued packet.
142        //
143        // This is done so that a socket lock may be held while sending a packet
144        // which may need to be delivered to the sending socket itself. Without
145        // this decoupling of RX/TX paths, sending a packet while holding onto
146        // the socket lock will result in a deadlock.
147        match ReceiveQueueHandler::queue_rx_frame(self, bindings_ctx, device_id, meta.into(), buf) {
148            Ok(()) => {}
149            Err(ReceiveQueueFullError((_meta, _frame))) => {
150                // RX queue is full - there is nothing further we can do here.
151                error!("dropped RX frame on loopback device due to full RX queue")
152            }
153        }
154
155        Ok(())
156    }
157}
158
159impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::LoopbackTxDequeue>>
160    TransmitDequeueContext<LoopbackDevice, BC> for CoreCtx<'_, BC, L>
161{
162    type TransmitQueueCtx<'a> =
163        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::LoopbackTxDequeue>>;
164
165    fn with_dequed_packets_and_tx_queue_ctx<
166        O,
167        F: FnOnce(&mut DequeueState<Self::Meta, Buf<Vec<u8>>>, &mut Self::TransmitQueueCtx<'_>) -> O,
168    >(
169        &mut self,
170        device_id: &Self::DeviceId,
171        cb: F,
172    ) -> O {
173        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
174        let (mut x, mut locked) = core_ctx_and_resource
175            .lock_with_and::<crate::lock_ordering::LoopbackTxDequeue, _>(|c| c.right());
176        cb(&mut x, &mut locked.cast_core_ctx())
177    }
178}
179
180impl<BT: DeviceLayerTypes> LockLevelFor<IpLinkDeviceState<LoopbackDevice, BT>>
181    for crate::lock_ordering::LoopbackRxQueue
182{
183    type Data = ReceiveQueueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>;
184}
185
186impl<BT: DeviceLayerTypes> LockLevelFor<IpLinkDeviceState<LoopbackDevice, BT>>
187    for crate::lock_ordering::LoopbackRxDequeue
188{
189    type Data = DequeueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>;
190}
191
192impl<BT: DeviceLayerTypes> LockLevelFor<IpLinkDeviceState<LoopbackDevice, BT>>
193    for crate::lock_ordering::LoopbackTxQueue
194{
195    type Data = TransmitQueueState<
196        LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>,
197        Buf<Vec<u8>>,
198        BufVecU8Allocator,
199    >;
200}
201
202impl<BT: DeviceLayerTypes> LockLevelFor<IpLinkDeviceState<LoopbackDevice, BT>>
203    for crate::lock_ordering::LoopbackTxDequeue
204{
205    type Data = DequeueState<LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>;
206}