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