Skip to main content

netstack3_device/
loopback.rs

1// Copyright 2022 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//! The loopback device.
6
7use 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, 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};
40use crate::internal::queue::{DequeueState, DeviceBufferSpec, TransmitQueueFrameError};
41use crate::internal::socket::{
42    DeviceSocketHandler, DeviceSocketMetadata, DeviceSocketSendTypes, EthernetHeaderParams,
43    ReceivedFrame,
44};
45use crate::internal::state::{DeviceStateSpec, IpLinkDeviceState};
46
47/// The MAC address corresponding to the loopback interface.
48const LOOPBACK_MAC: Mac = Mac::UNSPECIFIED;
49
50/// A weak device ID identifying a loopback device.
51///
52/// This device ID is like [`WeakDeviceId`] but specifically for loopback
53/// devices.
54///
55/// [`WeakDeviceId`]: crate::device::WeakDeviceId
56pub type LoopbackWeakDeviceId<BT> = BaseWeakDeviceId<LoopbackDevice, BT>;
57
58/// A strong device ID identifying a loopback device.
59///
60/// This device ID is like [`DeviceId`] but specifically for loopback devices.
61///
62/// [`DeviceId`]: crate::device::DeviceId
63pub type LoopbackDeviceId<BT> = BaseDeviceId<LoopbackDevice, BT>;
64
65/// The primary reference for a loopback device.
66pub type LoopbackPrimaryDeviceId<BT> = BasePrimaryDeviceId<LoopbackDevice, BT>;
67
68/// Loopback device domain.
69#[derive(Copy, Clone)]
70pub enum LoopbackDevice {}
71
72impl Device for LoopbackDevice {}
73
74impl<BT> DeviceBufferSpec<BT> for LoopbackDevice {
75    type TxBuffer = Buf<Vec<u8>>;
76    type TxAllocator = BufVecU8Allocator;
77}
78
79impl DeviceStateSpec for LoopbackDevice {
80    type State<BT: DeviceLayerTypes> = LoopbackDeviceState<WeakDeviceId<BT>, BT>;
81    type External<BT: DeviceLayerTypes> = BT::LoopbackDeviceState;
82    type CreationProperties = LoopbackCreationProperties;
83    type Counters = EthernetDeviceCounters;
84    type TimerId<D: WeakDeviceIdentifier> = Never;
85
86    fn new_device_state<
87        CC: CoreTimerContext<Self::TimerId<CC::WeakDeviceId>, BC> + DeviceIdContext<Self>,
88        BC: DeviceLayerTypes + TimerContext,
89    >(
90        _bindings_ctx: &mut BC,
91        _self_id: CC::WeakDeviceId,
92        LoopbackCreationProperties { mtu }: Self::CreationProperties,
93        tx_allocator: <Self as DeviceBufferSpec<BC>>::TxAllocator,
94    ) -> Self::State<BC>
95    where
96        Self: DeviceBufferSpec<BC>,
97    {
98        LoopbackDeviceState {
99            counters: Default::default(),
100            mtu,
101            rx_queue: Default::default(),
102            tx_queue: TransmitQueue::new(tx_allocator, ChecksumOffloadSpec::generic()),
103        }
104    }
105
106    const IS_LOOPBACK: bool = true;
107    const DEBUG_TYPE: &'static str = "Loopback";
108}
109
110/// Properties used to create a loopback device.
111#[derive(Debug)]
112pub struct LoopbackCreationProperties {
113    /// The device's MTU.
114    pub mtu: Mtu,
115}
116
117/// State for a loopback device.
118pub struct LoopbackDeviceState<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
119    /// Loopback device counters.
120    pub counters: EthernetDeviceCounters,
121    /// The MTU this device was created with (immutable).
122    pub mtu: Mtu,
123    /// Loopback device receive queue.
124    pub rx_queue: ReceiveQueue<LoopbackRxQueueMeta<D, BT>, Buf<Vec<u8>>>,
125    /// Loopback device transmit queue.
126    pub tx_queue: TransmitQueue<
127        LoopbackTxQueueMeta<D, BT>,
128        <LoopbackDevice as DeviceBufferSpec<BT>>::TxBuffer,
129        <LoopbackDevice as DeviceBufferSpec<BT>>::TxAllocator,
130    >,
131}
132
133#[derive(Derivative)]
134#[derivative(Default(bound = ""))]
135/// Metadata associated with a frame in the Loopback TX queue.
136pub struct LoopbackTxQueueMeta<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
137    /// Device that should be used to deliver the packet. If not set then the
138    /// packet delivered as if it came from the loopback device.
139    target_device: Option<D>,
140    /// Metadata that is produced and consumed by the IP layer but which traverses
141    /// the device layer through the loopback device.
142    ip_layer_metadata: DeviceIpLayerMetadata<BT>,
143}
144
145/// Metadata associated with a frame in the Loopback RX queue.
146#[derive(Derivative)]
147#[derivative(Debug(bound = ""))]
148pub struct LoopbackRxQueueMeta<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> {
149    /// Device that should be used to deliver the packet. If not set then the
150    /// packet delivered as if it came from the loopback device.
151    target_device: Option<D>,
152    /// Metadata that is produced and consumed by the IP layer but which traverses
153    /// the device layer through the loopback device.
154    ip_layer_metadata: DeviceIpLayerMetadata<BT>,
155}
156
157impl<D: WeakDeviceIdentifier, BT: TxMetadataBindingsTypes> From<LoopbackTxQueueMeta<D, BT>>
158    for LoopbackRxQueueMeta<D, BT>
159{
160    fn from(
161        LoopbackTxQueueMeta { target_device, ip_layer_metadata }: LoopbackTxQueueMeta<D, BT>,
162    ) -> Self {
163        Self { target_device, ip_layer_metadata }
164    }
165}
166
167impl<BT: DeviceLayerTypes>
168    OrderedLockAccess<ReceiveQueueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
169    for IpLinkDeviceState<LoopbackDevice, BT>
170{
171    type Lock = Mutex<ReceiveQueueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
172    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
173        OrderedLockRef::new(&self.link.rx_queue.queue)
174    }
175}
176
177impl<BT: DeviceLayerTypes>
178    OrderedLockAccess<DequeueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
179    for IpLinkDeviceState<LoopbackDevice, BT>
180{
181    type Lock = Mutex<DequeueState<LoopbackRxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
182    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
183        OrderedLockRef::new(&self.link.rx_queue.deque)
184    }
185}
186
187impl<BT: DeviceLayerTypes>
188    OrderedLockAccess<
189        TransmitQueueState<
190            LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>,
191            Buf<Vec<u8>>,
192            BufVecU8Allocator,
193        >,
194    > for IpLinkDeviceState<LoopbackDevice, BT>
195{
196    type Lock = Mutex<
197        TransmitQueueState<
198            LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>,
199            Buf<Vec<u8>>,
200            BufVecU8Allocator,
201        >,
202    >;
203    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
204        OrderedLockRef::new(&self.link.tx_queue.queue)
205    }
206}
207
208impl<BT: DeviceLayerTypes>
209    OrderedLockAccess<DequeueState<LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>
210    for IpLinkDeviceState<LoopbackDevice, BT>
211{
212    type Lock = Mutex<DequeueState<LoopbackTxQueueMeta<WeakDeviceId<BT>, BT>, Buf<Vec<u8>>>>;
213    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
214        OrderedLockRef::new(&self.link.tx_queue.deque)
215    }
216}
217
218impl DeviceSocketSendTypes for LoopbackDevice {
219    /// When `None`, data will be sent as a raw Ethernet frame without any
220    /// system-applied headers.
221    type Metadata = Option<EthernetHeaderParams>;
222}
223
224impl<CC, BC> ReceiveDequeFrameContext<LoopbackDevice, BC> for CC
225where
226    CC: DeviceIdContext<LoopbackDevice>
227        + ResourceCounterContext<Self::DeviceId, EthernetDeviceCounters>
228        + ReceiveQueueTypes<
229            LoopbackDevice,
230            BC,
231            Meta = LoopbackRxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
232        >,
233    // Loopback needs to deliver messages to `AnyDevice`.
234    CC: DeviceIdAnyCompatContext<LoopbackDevice>
235        + RecvFrameContext<
236            RecvIpFrameMeta<
237                <CC as DeviceIdContext<AnyDevice>>::DeviceId,
238                DeviceIpLayerMetadata<BC>,
239                Ipv4,
240            >,
241            BC,
242        > + RecvFrameContext<
243            RecvIpFrameMeta<
244                <CC as DeviceIdContext<AnyDevice>>::DeviceId,
245                DeviceIpLayerMetadata<BC>,
246                Ipv6,
247            >,
248            BC,
249        > + ResourceCounterContext<<CC as DeviceIdContext<AnyDevice>>::DeviceId, DeviceCounters>
250        + DeviceSocketHandler<AnyDevice, BC>,
251    CC::Buffer: BufferMut + Debug,
252    BC: DeviceLayerTypes,
253{
254    fn handle_frame(
255        &mut self,
256        bindings_ctx: &mut BC,
257        device_id: &Self::DeviceId,
258        rx_meta: Self::Meta,
259        mut buf: Self::Buffer,
260    ) {
261        // NOTE: At the time of writing, netdevice_worker in bindings uses a
262        // Buf<&mut [u8]> to feed receive frames into core. Matching the same
263        // type as bindings gives us the benefit of not generating rx path code
264        // twice.
265        //
266        // TODO(https://fxbug.dev/42051635): This might no longer be true when
267        // we revisit owned netdevice buffers, at which point we must consider
268        // the binary size tradeoff here.
269        let mut buf = Buf::new(buf.as_mut(), ..);
270        let buflen = buf.len();
271
272        let (frame, whole_body) =
273            match buf.parse_with_view::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::NoCheck) {
274                Err(e) => {
275                    self.increment_both(&device_id.clone().into(), |counters: &DeviceCounters| {
276                        &counters.recv_parse_error
277                    });
278                    trace!("dropping invalid ethernet frame over loopback: {:?}", e);
279                    return;
280                }
281                Ok(e) => e,
282            };
283
284        let LoopbackRxQueueMeta { target_device, ip_layer_metadata } = rx_meta;
285        let target_device: <CC as DeviceIdContext<AnyDevice>>::DeviceId =
286            match target_device.map(|d| d.upgrade()) {
287                // This is a packet that should be delivered on `target_device`.
288                Some(Some(dev)) => dev,
289
290                // `target_device` is gone. Drop the packet.
291                Some(None) => return,
292
293                // This is a packet sent to the loopback device.
294                None => device_id.clone().into(),
295            };
296
297        self.add_both_usize(&target_device, buflen, |counters: &DeviceCounters| {
298            &counters.recv_bytes
299        });
300        self.increment_both(&target_device, |counters: &DeviceCounters| &counters.recv_frame);
301
302        let frame_dest = FrameDestination::from_dest(frame.dst_mac(), Mac::UNSPECIFIED);
303        let ethertype = frame.ethertype();
304
305        DeviceSocketHandler::<AnyDevice, _>::handle_frame(
306            self,
307            bindings_ctx,
308            &target_device,
309            ReceivedFrame::from_ethernet(frame, frame_dest).into(),
310            whole_body,
311        );
312
313        match ethertype {
314            Some(EtherType::Ipv4) => {
315                self.increment_both(&target_device, |counters: &DeviceCounters| {
316                    &counters.recv_ipv4_delivered
317                });
318                self.receive_frame(
319                    bindings_ctx,
320                    RecvIpFrameMeta::<_, _, Ipv4>::new(
321                        target_device,
322                        Some(frame_dest),
323                        ip_layer_metadata,
324                        NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded),
325                    ),
326                    buf,
327                );
328            }
329            Some(EtherType::Ipv6) => {
330                self.increment_both(&target_device, |counters: &DeviceCounters| {
331                    &counters.recv_ipv6_delivered
332                });
333                self.receive_frame(
334                    bindings_ctx,
335                    RecvIpFrameMeta::<_, _, Ipv6>::new(
336                        target_device,
337                        Some(frame_dest),
338                        ip_layer_metadata,
339                        NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded),
340                    ),
341                    buf,
342                );
343            }
344            Some(ethertype @ (EtherType::Arp | EtherType::Other(_))) => {
345                self.increment_both(device_id, |counters: &EthernetDeviceCounters| {
346                    &counters.recv_unsupported_ethertype
347                });
348                trace!("not handling loopback frame of type {:?}", ethertype)
349            }
350            None => {
351                self.increment_both(device_id, |counters: &EthernetDeviceCounters| {
352                    &counters.recv_no_ethertype
353                });
354                trace!("dropping ethernet frame without ethertype");
355            }
356        }
357    }
358}
359
360impl<CC, BC> SendableFrameMeta<CC, BC>
361    for DeviceSocketMetadata<LoopbackDevice, <CC as DeviceIdContext<LoopbackDevice>>::DeviceId>
362where
363    CC: TransmitQueueHandler<
364            LoopbackDevice,
365            BC,
366            Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
367        > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
368        + DeviceIdContext<AnyDevice>,
369    BC: DeviceLayerTypes,
370{
371    fn send_meta<S>(
372        self,
373        core_ctx: &mut CC,
374        bindings_ctx: &mut BC,
375        body: S,
376    ) -> Result<(), SendFrameError<S>>
377    where
378        S: NetworkSerializer,
379        S::Buffer: BufferMut,
380    {
381        let Self { device_id, metadata } = self;
382        let tx_meta = LoopbackTxQueueMeta::default();
383        match metadata {
384            Some(EthernetHeaderParams { dest_addr, protocol }) => send_as_ethernet_frame_to_dst(
385                core_ctx,
386                bindings_ctx,
387                &device_id,
388                body,
389                protocol,
390                dest_addr,
391                tx_meta,
392            ),
393            None => send_ethernet_frame(core_ctx, bindings_ctx, &device_id, body, tx_meta),
394        }
395    }
396}
397
398/// Sends an IP frame `packet` over `device_id`.
399pub fn send_ip_frame<CC, BC, I, S>(
400    core_ctx: &mut CC,
401    bindings_ctx: &mut BC,
402    device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
403    destination: IpPacketDestination<I, &<CC as DeviceIdContext<AnyDevice>>::DeviceId>,
404    ip_layer_metadata: DeviceIpLayerMetadata<BC>,
405    packet: S,
406) -> Result<(), SendFrameError<S>>
407where
408    CC: TransmitQueueHandler<
409            LoopbackDevice,
410            BC,
411            Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
412        > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
413        + DeviceIdContext<AnyDevice>,
414    BC: DeviceLayerTypes,
415    I: EthernetIpExt + BroadcastIpExt,
416    S: NetworkSerializer,
417    S::Buffer: BufferMut,
418{
419    core_ctx.increment_both(device_id, DeviceCounters::send_frame::<I>);
420
421    let target_device = match destination {
422        IpPacketDestination::Loopback(device) => Some(device.downgrade()),
423        IpPacketDestination::Broadcast(_)
424        | IpPacketDestination::Multicast(_)
425        | IpPacketDestination::Neighbor(_) => None,
426    };
427    send_as_ethernet_frame_to_dst(
428        core_ctx,
429        bindings_ctx,
430        device_id,
431        packet,
432        I::ETHER_TYPE,
433        LOOPBACK_MAC,
434        LoopbackTxQueueMeta { target_device, ip_layer_metadata },
435    )
436}
437
438fn send_as_ethernet_frame_to_dst<CC, BC, S>(
439    core_ctx: &mut CC,
440    bindings_ctx: &mut BC,
441    device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
442    packet: S,
443    protocol: EtherType,
444    dst_mac: Mac,
445    meta: LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
446) -> Result<(), SendFrameError<S>>
447where
448    CC: TransmitQueueHandler<
449            LoopbackDevice,
450            BC,
451            Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
452        > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
453        + DeviceIdContext<AnyDevice>,
454    BC: DeviceLayerTypes,
455    S: NetworkSerializer,
456    S::Buffer: BufferMut,
457{
458    /// The minimum length of bodies of Ethernet frames sent over the loopback
459    /// device.
460    ///
461    /// Use zero since the frames are never sent out a physical device, so it
462    /// doesn't matter if they are shorter than would be required.
463    const MIN_BODY_LEN: usize = 0;
464
465    let frame =
466        EthernetFrameBuilder::new(LOOPBACK_MAC, dst_mac, protocol, MIN_BODY_LEN).wrap_body(packet);
467
468    send_ethernet_frame(core_ctx, bindings_ctx, device_id, frame, meta)
469        .map_err(|err| err.into_inner())
470}
471
472fn send_ethernet_frame<CC, BC, S>(
473    core_ctx: &mut CC,
474    bindings_ctx: &mut BC,
475    device_id: &<CC as DeviceIdContext<LoopbackDevice>>::DeviceId,
476    frame: S,
477    meta: LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
478) -> Result<(), SendFrameError<S>>
479where
480    CC: TransmitQueueHandler<
481            LoopbackDevice,
482            BC,
483            Meta = LoopbackTxQueueMeta<<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId, BC>,
484        > + ResourceCounterContext<<CC as DeviceIdContext<LoopbackDevice>>::DeviceId, DeviceCounters>
485        + DeviceIdContext<AnyDevice>,
486    S: NetworkSerializer,
487    S::Buffer: BufferMut,
488    BC: DeviceLayerTypes,
489{
490    core_ctx.increment_both(device_id, |counters: &DeviceCounters| &counters.send_total_frames);
491    match TransmitQueueHandler::<LoopbackDevice, _>::queue_tx_frame(
492        core_ctx,
493        bindings_ctx,
494        device_id,
495        meta,
496        frame,
497    ) {
498        Ok(len) => {
499            core_ctx.add_both_usize(device_id, len, |counters| &counters.send_bytes);
500            core_ctx.increment_both(device_id, |counters: &DeviceCounters| &counters.send_frame);
501            Ok(())
502        }
503        Err(TransmitQueueFrameError::NoQueue(err)) => {
504            unreachable!("loopback never fails to send a frame: {err:?}")
505        }
506        Err(TransmitQueueFrameError::QueueFull(serializer)) => {
507            core_ctx
508                .increment_both(device_id, |counters: &DeviceCounters| &counters.send_queue_full);
509            Err(SendFrameError { serializer, error: SendFrameErrorReason::QueueFull })
510        }
511        Err(TransmitQueueFrameError::SerializeError(err)) => {
512            core_ctx.increment_both(device_id, |counters: &DeviceCounters| {
513                &counters.send_serialize_error
514            });
515            Err(err.err_into())
516        }
517    }
518}
519
520impl DeviceReceiveFrameSpec for LoopbackDevice {
521    // Loopback never receives frames from bindings, so make it impossible to
522    // instantiate it.
523    type FrameMetadata<D> = Never;
524}