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