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