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