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