use super::{Error, Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, Result};
use heapless::Vec;
pub struct Header<T: AsRef<[u8]>> {
buffer: T,
}
impl<T: AsRef<[u8]>> Header<T> {
pub const fn new_unchecked(buffer: T) -> Self {
Header { buffer }
}
pub fn new_checked(buffer: T) -> Result<Self> {
let header = Self::new_unchecked(buffer);
header.check_len()?;
Ok(header)
}
pub fn check_len(&self) -> Result<()> {
if self.buffer.as_ref().is_empty() {
return Err(Error);
}
Ok(())
}
pub fn into_inner(self) -> T {
self.buffer
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
pub fn options(&self) -> &'a [u8] {
self.buffer.as_ref()
}
}
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> {
pub fn options_mut(&mut self) -> &mut [u8] {
self.buffer.as_mut()
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
pub options: heapless::Vec<Ipv6OptionRepr<'a>, { crate::config::IPV6_HBH_MAX_OPTIONS }>,
}
impl<'a> Repr<'a> {
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
let mut options = Vec::new();
let iter = Ipv6OptionsIterator::new(header.options());
for option in iter {
let option = option?;
if let Err(e) = options.push(option) {
net_trace!("error when parsing hop-by-hop options: {}", e);
break;
}
}
Ok(Self { options })
}
pub fn buffer_len(&self) -> usize {
self.options.iter().map(|o| o.buffer_len()).sum()
}
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
let mut buffer = header.options_mut();
for opt in &self.options {
opt.emit(&mut Ipv6Option::new_unchecked(
&mut buffer[..opt.buffer_len()],
));
buffer = &mut buffer[opt.buffer_len()..];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::Error;
static REPR_PACKET_PAD4: [u8; 6] = [0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
static REPR_PACKET_PAD12: [u8; 14] = [
0x1, 0x0C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
#[test]
fn test_check_len() {
assert_eq!(
Err(Error),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
);
assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
assert_eq!(
Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
);
}
#[test]
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&REPR_PACKET_PAD4);
let repr = Repr::parse(&header).unwrap();
let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(4)).unwrap();
assert_eq!(repr, Repr { options });
let header = Header::new_unchecked(&REPR_PACKET_PAD12);
let repr = Repr::parse(&header).unwrap();
let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(12)).unwrap();
assert_eq!(repr, Repr { options });
}
#[test]
fn test_repr_emit() {
let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(4)).unwrap();
let repr = Repr { options };
let mut bytes = [0u8; 6];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);
let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(12)).unwrap();
let repr = Repr { options };
let mut bytes = [0u8; 14];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..]);
}
}