ingot_types/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Primitive types and core traits needed to generate and use `ingot` packets.
6
7#![no_std]
8#![deny(missing_docs)]
9
10use alloc::vec;
11#[cfg(feature = "alloc")]
12pub use alloc::vec::Vec;
13use core::{
14    marker::PhantomData,
15    mem::MaybeUninit,
16    ops::{Deref, DerefMut},
17};
18use zerocopy::{
19    FromBytes, Immutable, IntoByteSliceMut, IntoBytes, KnownLayout, Ref,
20};
21
22pub use zerocopy::{
23    ByteSlice, ByteSliceMut, IntoByteSlice, SplitByteSlice, SplitByteSliceMut,
24};
25
26#[cfg(feature = "alloc")]
27extern crate alloc;
28
29mod accessor;
30mod emit;
31mod error;
32pub mod field;
33pub mod header;
34pub mod ip;
35pub mod primitives;
36pub mod util;
37
38// Defines relevant packetish traits (mainly Emit) on tuples of
39// size 1--16.
40ingot_macros::define_tuple_trait_impls!();
41
42pub use accessor::Accessor;
43pub use emit::*;
44pub use error::*;
45pub use field::*;
46pub use header::*;
47pub use ip::*;
48
49/// Converts a borrowed view of a header into an owned version, possibly
50/// reparsing to do so.
51///
52/// This trait is used to support cases which are ambiguous on their own,
53/// such as [`Repeated`] views over extension headers.
54///
55/// [`Repeated`]: util::Repeated
56pub trait ToOwnedPacket: NextLayer {
57    /// The output type of this conversion.
58    type Target;
59
60    /// Converts a borrowed view of a header into an owned version, possibly
61    /// reparsing to do so with the aid of `hint`.
62    fn to_owned(&self, hint: Option<Self::Hint>) -> ParseResult<Self::Target>;
63}
64
65/// Base trait for header/packet types.
66pub trait HeaderLen {
67    /// The minimum number of bytes a packet of this kind occupies
68    /// when serialised.
69    const MINIMUM_LENGTH: usize;
70
71    /// The number of bytes which this packet would occupy when serialised.
72    ///
73    /// This should always return a value greater than or equal to
74    /// [`Header::MINIMUM_LENGTH`].
75    fn packet_length(&self) -> usize;
76}
77
78impl<H: HeaderLen> HeaderLen for &H {
79    const MINIMUM_LENGTH: usize = H::MINIMUM_LENGTH;
80
81    #[inline]
82    fn packet_length(&self) -> usize {
83        H::packet_length(self)
84    }
85}
86
87impl<T: HeaderLen> HeaderLen for Vec<T> {
88    const MINIMUM_LENGTH: usize = 0;
89
90    #[inline]
91    fn packet_length(&self) -> usize {
92        self.iter().map(|v| v.packet_length()).sum()
93    }
94}
95
96impl<T: HeaderLen> HeaderLen for Option<T> {
97    const MINIMUM_LENGTH: usize = 0;
98
99    #[inline]
100    fn packet_length(&self) -> usize {
101        self.as_ref().map(|v| v.packet_length()).unwrap_or_default()
102    }
103}
104
105impl HeaderLen for Vec<u8> {
106    const MINIMUM_LENGTH: usize = 0;
107
108    #[inline]
109    fn packet_length(&self) -> usize {
110        self.len()
111    }
112}
113
114/// A type which has a corresponding view-type over any buffer `B`.
115pub trait HasView<B> {
116    /// The type containing an equivalent to `Self`, as a network packet
117    /// in a buffer `B`.
118    type ViewType;
119}
120
121/// A type which has a corresponding static/owned representation type.
122pub trait HasRepr {
123    /// The type containing an equivalent to `Self`, as an owned struct.
124    type ReprType;
125}
126
127impl<T: HasView<B>, B> HasView<B> for Option<T> {
128    type ViewType = T;
129}
130
131impl<T: HasRepr> HasRepr for Option<T> {
132    type ReprType = T;
133}
134
135/// A header/packet type which can be unconditionally parsed from any
136/// buffer `B`.
137pub trait HeaderParse<B: SplitByteSlice>: NextLayer + Sized {
138    /// Parse a view-type from a given buffer.
139    fn parse(from: B) -> ParseResult<Success<Self, B>> {
140        Self::parse_choice(from, None)
141    }
142    /// Parse a view-type from a given buffer, using a hint
143    fn parse_choice(
144        data: B,
145        hint: Option<Self::Hint>,
146    ) -> ParseResult<Success<Self, B>>;
147}
148
149/// An iterator over contiguous byte slices which can be parsed
150/// as a packet.
151pub trait Read {
152    /// The type of each byte slice.
153    type Chunk: SplitByteSlice;
154
155    /// Attempts to fetch the next available byte slice from `self`.
156    fn next_chunk(&mut self) -> ParseResult<Self::Chunk>;
157
158    /// Returns the number of segments remaining.
159    fn chunks_len(&self) -> usize;
160
161    /// Returns whether there are any segments remaining.
162    fn is_empty(&self) -> bool {
163        self.chunks_len() == 0
164    }
165}
166
167#[cfg(feature = "alloc")]
168impl<'a> Read for alloc::collections::linked_list::Iter<'a, Vec<u8>> {
169    type Chunk = &'a [u8];
170
171    #[inline]
172    fn next_chunk(&mut self) -> ParseResult<Self::Chunk> {
173        self.next().ok_or(ParseError::TooSmall).map(|v| v.as_ref())
174    }
175
176    #[inline]
177    fn chunks_len(&self) -> usize {
178        ExactSizeIterator::len(self)
179    }
180}
181
182#[cfg(feature = "alloc")]
183impl<'a> Read for alloc::collections::linked_list::IterMut<'a, Vec<u8>> {
184    type Chunk = &'a mut [u8];
185
186    #[inline]
187    fn next_chunk(&mut self) -> ParseResult<Self::Chunk> {
188        self.next().ok_or(ParseError::TooSmall).map(|v| v.as_mut())
189    }
190
191    #[inline]
192    fn chunks_len(&self) -> usize {
193        ExactSizeIterator::len(self)
194    }
195}
196
197impl HeaderLen for &[u8] {
198    const MINIMUM_LENGTH: usize = 0;
199
200    #[inline]
201    fn packet_length(&self) -> usize {
202        self.len()
203    }
204}
205
206/// Helper alias for methods which return tuples of
207/// `(header, next_layer_hint, buf_remainder)`.
208pub type Success<T, B> = (T, Option<<T as NextLayer>::Denom>, B);
209
210/// Headers which can be queried for a hint, used to select the
211/// next layer in a packet.
212pub trait NextLayer {
213    /// The type of this header's next-layer hint.
214    type Denom: Copy + Eq;
215
216    /// A type used to help parse the header
217    type Hint: Copy + Eq;
218
219    /// Retrieve this header's next-layer hint, if possible.
220    #[inline]
221    fn next_layer(&self) -> Option<Self::Denom> {
222        self.next_layer_choice(None)
223    }
224
225    /// Try to retrieve this header's next-layer hint, using a provided hint
226    #[inline]
227    fn next_layer_choice(
228        &self,
229        _hint: Option<Self::Hint>,
230    ) -> Option<Self::Denom> {
231        None
232    }
233}
234
235/// Action to be taken as part of an `#[ingot(control)]` block
236/// during packet parsing.
237#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
238pub enum ParseControl {
239    /// Proceeds with parsing the remaining layers of a packet.
240    Continue,
241    /// Accepts the packet if all remaining fields are `Option`al,
242    /// terminating parsing.
243    Accept,
244    /// Explicitly rejects the packet.
245    Reject,
246}
247
248/// Types which can be converted to and from bitstrings and byte arrays
249/// for serialisation as fields of network packets.
250///
251/// This can be used for better type-checking (e.g., `bitfield`s or newtypes).
252/// We might represent a next-header type using a primitive:
253/// ```rust
254/// # use ingot_types::{primitives::u16be, NetworkRepr};
255/// #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, Ord, PartialOrd, Default)]
256/// struct Ethertype(u16);
257///
258/// impl NetworkRepr<u16be> for Ethertype {
259///     #[inline]
260///     fn to_network(self) -> u16be {
261///         self.0
262///     }
263///
264///     #[inline]
265///     fn from_network(val: u16be) -> Self {
266///         Self(val)
267///     }
268/// }
269/// ```
270///
271/// ...or, a byte array (such as `[u8; 16]`).
272pub trait NetworkRepr<U: Copy> {
273    /// Converts a local value into raw bytes or integer type.
274    fn to_network(self) -> U;
275    /// Converts a raw value into a local type.
276    fn from_network(val: U) -> Self;
277}
278
279impl NetworkRepr<[u8; 6]> for macaddr::MacAddr6 {
280    #[inline]
281    fn to_network(self) -> [u8; 6] {
282        self.into_array()
283    }
284
285    #[inline]
286    fn from_network(val: [u8; 6]) -> Self {
287        macaddr::MacAddr6::from(val)
288    }
289}
290
291/// Successful return value from parsing a full packet header stack
292/// over a base packet buffer which is [`Read`].
293pub struct Parsed<Stack, RawPkt: Read> {
294    /// A fully-parsed header stack.
295    pub headers: Stack,
296    /// The remainder of the last chunk accessed during parsing.
297    pub last_chunk: Option<RawPkt::Chunk>,
298    /// The leftover packet cursor.
299    ///
300    /// Remaining bytes can be accessed using [`Read`].
301    pub data: RawPkt,
302}
303
304/// Convert a byte slice into a pointer to its base.
305///
306/// # Safety
307/// This requires that the invariants expressed on zerocopy's
308/// [`ByteSlice`] and [`IntoByteSlice`] around stability are upheld.
309pub unsafe trait IntoBufPointer<'a>: IntoByteSlice<'a> {
310    /// Convert a buffer into the *most exclusive pointer type
311    /// permitted*, to be read by an [`Accessor`].
312    ///
313    /// The pointer must be cast to a `*mut u8` regardless of
314    /// the source's mutability. Mutability of this buffer type
315    /// ([`ByteSlice`]/[`ByteSliceMut`]) is then used to determine
316    /// whether the pointer is in fact used as a `&mut T` or `&T`.
317    ///
318    /// # Safety
319    /// This requires that the invariants expressed on zerocopy's
320    /// [`ByteSlice`] and [`IntoByteSlice`] around stability are upheld,
321    /// and the pointer *must* be derived from [`IntoByteSlice::into_byte_slice`]
322    /// or [`IntoByteSliceMut::into_byte_slice_mut`].
323    unsafe fn into_buf_ptr(self) -> *mut u8;
324}
325
326unsafe impl<'a> IntoBufPointer<'a> for &'a [u8] {
327    #[inline(always)]
328    unsafe fn into_buf_ptr(self) -> *mut u8 {
329        self.into_byte_slice().as_ptr() as *mut _
330    }
331}
332
333unsafe impl<'a> IntoBufPointer<'a> for &'a mut [u8] {
334    #[inline(always)]
335    unsafe fn into_buf_ptr(self) -> *mut u8 {
336        self.into_byte_slice_mut().as_mut_ptr()
337    }
338}
339
340// Used to gate impls on BoxedHeader in downstream derives.
341#[cfg(feature = "alloc")]
342#[doc(hidden)]
343#[macro_export]
344macro_rules! __cfg_alloc {
345    ( $( $tok:tt )* ) => { $( $tok )* }
346}
347
348#[cfg(not(feature = "alloc"))]
349#[doc(hidden)]
350#[macro_export]
351macro_rules! __cfg_alloc {
352    ( $( $tok:tt )* ) => {};
353}
354
355/// Needed to compute MINIMUM_LENGTH for choices.
356#[doc(hidden)]
357pub const fn min(a: usize, b: usize) -> usize {
358    if a < b {
359        a
360    } else {
361        b
362    }
363}