llc_rs/
lib.rs

1#![no_std]
2
3use core::marker::PhantomData;
4
5use macro_bits::{bit, check_bit};
6use scroll::{
7    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
8    Endian, Pread, Pwrite,
9};
10
11pub use ether_type::EtherType;
12
13const SNAP_CODE: u8 = 0xaa;
14
15/// An unnumbered LLC frame, with the SNAP extension.
16///
17/// ## Structure
18/// This struct represents an unnumbered LLC frame with a SNAP header.
19/// This means, that DSAP and SSAP are equal to `0xaa`.
20/// It is also expected, that the first two bits of the control field are set to one.
21/// If any of these assumptions aren't met, an error will be returned.
22#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
23pub struct SnapLlcFrame<'a, Payload = &'a [u8]> {
24    /// The organizationally unique identifier.
25    ///
26    /// In most cases this is just all zeroes.
27    pub oui: [u8; 3],
28    /// The type of the protocol carried in [Self::payload].
29    ///
30    /// NOTE: If the oui isn't all zeroes, this isn't actually an ether type.
31    /// However this is quite uncommon, and an ether type is usually correct.
32    /// If this isn't the case, use `self.ether_type.into_bits()` to get the raw [u16] protocol ID.
33    pub ether_type: EtherType,
34    /// The payload of the LLC frame.
35    pub payload: Payload,
36    pub _phantom: PhantomData<&'a ()>,
37}
38impl<'a> TryFromCtx<'a> for SnapLlcFrame<'a> {
39    type Error = scroll::Error;
40    fn try_from_ctx(from: &'a [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
41        let mut offset = 0;
42        let dsap = from.gread::<u8>(&mut offset)?;
43        let ssap = from.gread::<u8>(&mut offset)?;
44        if dsap != SNAP_CODE || ssap != SNAP_CODE {
45            return Err(scroll::Error::BadInput {
46                size: offset,
47                msg: "DSAP/SSAP wasn't set to 0xaa (SNAP).",
48            });
49        }
50        let control = from.gread::<u8>(&mut offset)?;
51        if !check_bit!(control, bit!(0, 1)) {
52            return Err(scroll::Error::BadInput {
53                size: offset,
54                msg: "LLC control field wasn't set to unnumbered.",
55            });
56        }
57        let oui = from.gread(&mut offset)?;
58        let ether_type = EtherType::from_bits(from.gread_with(&mut offset, Endian::Big)?);
59        let payload = &from[offset..];
60        Ok((
61            Self {
62                oui,
63                ether_type,
64                payload,
65                _phantom: PhantomData,
66            },
67            offset,
68        ))
69    }
70}
71impl<Payload: MeasureWith<()>> MeasureWith<()> for SnapLlcFrame<'_, Payload> {
72    fn measure_with(&self, ctx: &()) -> usize {
73        7 + self.payload.measure_with(ctx)
74    }
75}
76impl<Payload: TryIntoCtx<Error = scroll::Error>> TryIntoCtx for SnapLlcFrame<'_, Payload> {
77    type Error = scroll::Error;
78    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
79        let mut offset = 0;
80        buf.gwrite(SNAP_CODE, &mut offset)?;
81        buf.gwrite(SNAP_CODE, &mut offset)?;
82        buf.gwrite(0b11u8, &mut offset)?;
83        buf.gwrite(self.oui.as_slice(), &mut offset)?;
84        buf.gwrite_with(self.ether_type.into_bits(), &mut offset, Endian::Big)?;
85        buf.gwrite(self.payload, &mut offset)?;
86        Ok(offset)
87    }
88}