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}