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