Skip to main content

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