netstack3_base/
frame.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//! Common traits and types for dealing with abstracted frames.
6
7use net_types::ethernet::Mac;
8use net_types::ip::{Ip, IpVersionMarker};
9use net_types::{BroadcastAddr, MulticastAddr};
10
11use core::convert::Infallible as Never;
12use core::fmt::Debug;
13use packet::{BufferMut, SerializeError, Serializer};
14use thiserror::Error;
15
16use crate::error::ErrorAndSerializer;
17use crate::socket::SocketCookie;
18
19/// A context for receiving frames.
20///
21/// Note: Use this trait as trait bounds, but always implement
22/// [`ReceivableFrameMeta`] instead, which generates a `RecvFrameContext`
23/// implementation.
24pub trait RecvFrameContext<Meta, BC> {
25    /// Receive a frame.
26    ///
27    /// `receive_frame` receives a frame with the given metadata.
28    fn receive_frame<B: BufferMut + Debug>(
29        &mut self,
30        bindings_ctx: &mut BC,
31        metadata: Meta,
32        frame: B,
33    );
34}
35
36impl<CC, BC> ReceivableFrameMeta<CC, BC> for Never {
37    fn receive_meta<B: BufferMut + Debug>(
38        self,
39        _core_ctx: &mut CC,
40        _bindings_ctx: &mut BC,
41        _frame: B,
42    ) {
43        match self {}
44    }
45}
46
47/// A trait providing the receive implementation for some frame identified by a
48/// metadata type.
49///
50/// This trait sidesteps orphan rules by allowing [`RecvFrameContext`] to be
51/// implemented by the multiple core crates, given it can always be implemented
52/// for a local metadata type. `ReceivableFrameMeta` should always be used for
53/// trait implementations, while [`RecvFrameContext`] is used for trait bounds.
54pub trait ReceivableFrameMeta<CC, BC> {
55    /// Receives this frame using the provided contexts.
56    fn receive_meta<B: BufferMut + Debug>(self, core_ctx: &mut CC, bindings_ctx: &mut BC, frame: B);
57}
58
59impl<CC, BC, Meta> RecvFrameContext<Meta, BC> for CC
60where
61    Meta: ReceivableFrameMeta<CC, BC>,
62{
63    fn receive_frame<B: BufferMut + Debug>(
64        &mut self,
65        bindings_ctx: &mut BC,
66        metadata: Meta,
67        frame: B,
68    ) {
69        metadata.receive_meta(self, bindings_ctx, frame)
70    }
71}
72
73/// The error type for [`SendFrameError`].
74#[derive(Error, Debug, PartialEq)]
75pub enum SendFrameErrorReason {
76    /// Serialization failed due to failed size constraints.
77    #[error("size constraints violated")]
78    SizeConstraintsViolation,
79    /// Couldn't allocate space to serialize the frame.
80    #[error("failed to allocate")]
81    Alloc,
82    /// The transmit queue is full.
83    #[error("transmit queue is full")]
84    QueueFull,
85}
86
87impl<A> From<SerializeError<A>> for SendFrameErrorReason {
88    fn from(e: SerializeError<A>) -> Self {
89        match e {
90            SerializeError::Alloc(_) => Self::Alloc,
91            SerializeError::SizeLimitExceeded => Self::SizeConstraintsViolation,
92        }
93    }
94}
95
96/// Errors returned by [`SendFrameContext::send_frame`].
97pub type SendFrameError<S> = ErrorAndSerializer<SendFrameErrorReason, S>;
98
99/// A context for sending frames.
100pub trait SendFrameContext<BC, Meta> {
101    /// Send a frame.
102    ///
103    /// `send_frame` sends a frame with the given metadata. The frame itself is
104    /// passed as a [`Serializer`] which `send_frame` is responsible for
105    /// serializing. If serialization fails for any reason, the original,
106    /// unmodified `Serializer` is returned.
107    ///
108    /// [`Serializer`]: packet::Serializer
109    fn send_frame<S>(
110        &mut self,
111        bindings_ctx: &mut BC,
112        metadata: Meta,
113        frame: S,
114    ) -> Result<(), SendFrameError<S>>
115    where
116        S: Serializer,
117        S::Buffer: BufferMut;
118}
119
120/// A trait providing the send implementation for some frame identified by a
121/// metadata type.
122///
123/// This trait sidesteps orphan rules by allowing [`SendFrameContext`] to be
124/// implemented by the multiple core crates, given it can always be implemented
125/// for a local metadata type. `SendableFrameMeta` should always be used for
126/// trait implementations, while [`SendFrameContext`] is used for trait bounds.
127pub trait SendableFrameMeta<CC, BC> {
128    /// Sends this frame metadata to the provided contexts.
129    fn send_meta<S>(
130        self,
131        core_ctx: &mut CC,
132        bindings_ctx: &mut BC,
133        frame: S,
134    ) -> Result<(), SendFrameError<S>>
135    where
136        S: Serializer,
137        S::Buffer: BufferMut;
138}
139
140impl<CC, BC, Meta> SendFrameContext<BC, Meta> for CC
141where
142    Meta: SendableFrameMeta<CC, BC>,
143{
144    fn send_frame<S>(
145        &mut self,
146        bindings_ctx: &mut BC,
147        metadata: Meta,
148        frame: S,
149    ) -> Result<(), SendFrameError<S>>
150    where
151        S: Serializer,
152        S::Buffer: BufferMut,
153    {
154        metadata.send_meta(self, bindings_ctx, frame)
155    }
156}
157
158/// The type of address used as the destination address in a device-layer frame.
159///
160/// `FrameDestination` is used to implement RFC 1122 section 3.2.2 and RFC 4443
161/// section 2.4.e, which govern when to avoid sending an ICMP error message for
162/// ICMP and ICMPv6 respectively.
163#[derive(Copy, Clone, Debug, Eq, PartialEq)]
164pub enum FrameDestination {
165    /// A unicast address - one which is neither multicast nor broadcast.
166    Individual {
167        /// Whether the frame's destination address belongs to the receiver.
168        local: bool,
169    },
170    /// A multicast address; if the addressing scheme supports overlap between
171    /// multicast and broadcast, then broadcast addresses should use the
172    /// `Broadcast` variant.
173    Multicast,
174    /// A broadcast address; if the addressing scheme supports overlap between
175    /// multicast and broadcast, then broadcast addresses should use the
176    /// `Broadcast` variant.
177    Broadcast,
178}
179
180impl FrameDestination {
181    /// Is this `FrameDestination::Broadcast`?
182    pub fn is_broadcast(self) -> bool {
183        self == FrameDestination::Broadcast
184    }
185
186    /// Creates a `FrameDestination` from a `mac` and `local_mac` destination.
187    pub fn from_dest(destination: Mac, local_mac: Mac) -> Self {
188        BroadcastAddr::new(destination)
189            .map(Into::into)
190            .or_else(|| MulticastAddr::new(destination).map(Into::into))
191            .unwrap_or_else(|| FrameDestination::Individual { local: destination == local_mac })
192    }
193}
194
195impl From<BroadcastAddr<Mac>> for FrameDestination {
196    fn from(_value: BroadcastAddr<Mac>) -> Self {
197        Self::Broadcast
198    }
199}
200
201impl From<MulticastAddr<Mac>> for FrameDestination {
202    fn from(_value: MulticastAddr<Mac>) -> Self {
203        Self::Multicast
204    }
205}
206
207/// The metadata required for a packet to get into the IP layer.
208pub struct RecvIpFrameMeta<D, M, I: Ip> {
209    /// The device on which the IP frame was received.
210    pub device: D,
211    /// The link-layer destination address from the link-layer frame, if any.
212    /// `None` if the IP frame originated above the link-layer (e.g. pure IP
213    /// devices).
214    // NB: In the future, this field may also be `None` to represent link-layer
215    // protocols without destination addresses (i.e. PPP), but at the moment no
216    // such protocols are supported.
217    pub frame_dst: Option<FrameDestination>,
218    /// Metadata that is produced and consumed by the IP layer but which traverses
219    /// the device layer through the loopback device.
220    pub ip_layer_metadata: M,
221    /// A marker for the Ip version in this frame.
222    pub marker: IpVersionMarker<I>,
223}
224
225impl<D, M, I: Ip> RecvIpFrameMeta<D, M, I> {
226    /// Creates a new `RecvIpFrameMeta` originating from `device` and `frame_dst`
227    /// option.
228    pub fn new(
229        device: D,
230        frame_dst: Option<FrameDestination>,
231        ip_layer_metadata: M,
232    ) -> RecvIpFrameMeta<D, M, I> {
233        RecvIpFrameMeta { device, frame_dst, ip_layer_metadata, marker: IpVersionMarker::new() }
234    }
235}
236
237/// A trait for the metadata associated with a TX frame.
238///
239/// The `Default` impl yields the default, i.e. unspecified, metadata
240/// instance.
241pub trait TxMetadata: Default + Debug + Send + Sync + 'static {
242    /// Returns [`SocketCookie`] for the socket associate with the packet.
243    /// `None` is returned if the packet is not associated with a local socket
244    /// or the socket has been destroyed.
245    fn socket_cookie(&self) -> Option<SocketCookie>;
246}
247
248/// A trait abstracting TX frame metadata when traversing the stack.
249///
250/// This trait allows for stack integration crate to define a single concrete
251/// enumeration for all the types of transport metadata that a socket can
252/// generate. Metadata is carried with all TX frames until they hit the device
253/// layer.
254///
255/// NOTE: This trait is implemented by *bindings*. Although the tx metadata
256/// never really leaves core, abstraction over bindings types are substantially
257/// more common so delegating this implementation to bindings avoids type
258/// parameter explosion.
259pub trait TxMetadataBindingsTypes {
260    /// The metadata associated with a TX frame.
261    type TxMetadata: TxMetadata;
262}
263
264/// A core context providing tx metadata type conversion.
265///
266/// This trait is used to convert from a core-internal tx metadata type `T` to
267/// the metadata supported by bindings in `BT::TxMetadata`.
268pub trait CoreTxMetadataContext<T, BT: TxMetadataBindingsTypes> {
269    /// Converts the tx metadata `T` into the type set by bindings.
270    ///
271    /// Note that this method takes a `self` receiver so it's easily
272    /// implementable with uninstantiable types. The conversion is expected to
273    /// be stateless otherwise in all implementers.
274    fn convert_tx_meta(&self, tx_meta: T) -> BT::TxMetadata;
275}
276
277#[cfg(any(test, feature = "testutils"))]
278pub(crate) mod testutil {
279    use super::*;
280    use alloc::boxed::Box;
281    use alloc::vec::Vec;
282
283    use crate::testutil::FakeBindingsCtx;
284
285    /// A fake [`FrameContext`].
286    pub struct FakeFrameCtx<Meta> {
287        frames: Vec<(Meta, Vec<u8>)>,
288        should_error_for_frame:
289            Option<Box<dyn FnMut(&Meta) -> Option<SendFrameErrorReason> + Send>>,
290    }
291
292    impl<Meta> FakeFrameCtx<Meta> {
293        /// Closure which can decide to cause an error to be thrown when
294        /// handling a frame, based on the metadata.
295        pub fn set_should_error_for_frame<
296            F: Fn(&Meta) -> Option<SendFrameErrorReason> + Send + 'static,
297        >(
298            &mut self,
299            f: F,
300        ) {
301            self.should_error_for_frame = Some(Box::new(f));
302        }
303    }
304
305    impl<Meta> Default for FakeFrameCtx<Meta> {
306        fn default() -> FakeFrameCtx<Meta> {
307            FakeFrameCtx { frames: Vec::new(), should_error_for_frame: None }
308        }
309    }
310
311    impl<Meta> FakeFrameCtx<Meta> {
312        /// Take all frames sent so far.
313        pub fn take_frames(&mut self) -> Vec<(Meta, Vec<u8>)> {
314            core::mem::take(&mut self.frames)
315        }
316
317        /// Get the frames sent so far.
318        pub fn frames(&self) -> &[(Meta, Vec<u8>)] {
319            self.frames.as_slice()
320        }
321
322        /// Pushes a frame to the context.
323        pub fn push(&mut self, meta: Meta, frame: Vec<u8>) {
324            self.frames.push((meta, frame))
325        }
326    }
327
328    impl<Meta, BC> SendableFrameMeta<FakeFrameCtx<Meta>, BC> for Meta {
329        fn send_meta<S>(
330            self,
331            core_ctx: &mut FakeFrameCtx<Meta>,
332            _bindings_ctx: &mut BC,
333            frame: S,
334        ) -> Result<(), SendFrameError<S>>
335        where
336            S: Serializer,
337            S::Buffer: BufferMut,
338        {
339            if let Some(error) = core_ctx.should_error_for_frame.as_mut().and_then(|f| f(&self)) {
340                return Err(SendFrameError { serializer: frame, error });
341            }
342
343            let buffer = frame
344                .serialize_vec_outer()
345                .map_err(|(e, serializer)| SendFrameError { error: e.into(), serializer })?;
346            core_ctx.push(self, buffer.as_ref().to_vec());
347            Ok(())
348        }
349    }
350
351    /// A trait for abstracting contexts that may contain a [`FakeFrameCtx`].
352    pub trait WithFakeFrameContext<SendMeta> {
353        /// Calls the callback with a mutable reference to the [`FakeFrameCtx`].
354        fn with_fake_frame_ctx_mut<O, F: FnOnce(&mut FakeFrameCtx<SendMeta>) -> O>(
355            &mut self,
356            f: F,
357        ) -> O;
358    }
359
360    impl<SendMeta> WithFakeFrameContext<SendMeta> for FakeFrameCtx<SendMeta> {
361        fn with_fake_frame_ctx_mut<O, F: FnOnce(&mut FakeFrameCtx<SendMeta>) -> O>(
362            &mut self,
363            f: F,
364        ) -> O {
365            f(self)
366        }
367    }
368
369    impl<TimerId, Event: Debug, State, FrameMeta> TxMetadataBindingsTypes
370        for FakeBindingsCtx<TimerId, Event, State, FrameMeta>
371    {
372        type TxMetadata = FakeTxMetadata;
373    }
374
375    /// The fake metadata supported by [`FakeBindingsCtx`].
376    #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
377    pub struct FakeTxMetadata;
378
379    impl TxMetadata for FakeTxMetadata {
380        fn socket_cookie(&self) -> Option<SocketCookie> {
381            None
382        }
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use super::*;
389
390    use net_declare::net_mac;
391    use net_types::{UnicastAddr, Witness as _};
392
393    #[test]
394    fn frame_destination_from_dest() {
395        const LOCAL_ADDR: Mac = net_mac!("88:88:88:88:88:88");
396
397        assert_eq!(
398            FrameDestination::from_dest(
399                UnicastAddr::new(net_mac!("00:11:22:33:44:55")).unwrap().get(),
400                LOCAL_ADDR
401            ),
402            FrameDestination::Individual { local: false }
403        );
404        assert_eq!(
405            FrameDestination::from_dest(LOCAL_ADDR, LOCAL_ADDR),
406            FrameDestination::Individual { local: true }
407        );
408        assert_eq!(
409            FrameDestination::from_dest(Mac::BROADCAST, LOCAL_ADDR),
410            FrameDestination::Broadcast,
411        );
412        assert_eq!(
413            FrameDestination::from_dest(
414                MulticastAddr::new(net_mac!("11:11:11:11:11:11")).unwrap().get(),
415                LOCAL_ADDR
416            ),
417            FrameDestination::Multicast
418        );
419    }
420}