use super::{Error, Result}; use core::fmt; pub use super::IpProtocol as Protocol; use crate::wire::ipv6option::Ipv6OptionsIterator; /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T, } // Format of the Hop-by-Hop Options Header // // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Next Header | Hdr Ext Len | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | // . . // . Options . // . . // | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // // See https://tools.ietf.org/html/rfc8200#section-4.3 for details. mod field { #![allow(non_snake_case)] use crate::wire::field::*; // Minimum size of the header. pub const MIN_HEADER_SIZE: usize = 8; // 8-bit identifier of the header immediately following this header. pub const NXT_HDR: usize = 0; // 8-bit unsigned integer. Length of the OPTIONS field in 8-octet units, // not including the first 8 octets. pub const LENGTH: usize = 1; // Variable-length field. Option-Type-specific data. // // Length of the header is in 8-octet units, not including the first 8 octets. The first two // octets are the next header type and the header length. pub const fn OPTIONS(length_field: u8) -> Field { let bytes = length_field as usize * 8 + 8; 2..bytes } } impl> Header { /// Create a raw octet buffer with an IPv6 Hop-by-Hop Options Header structure. pub const fn new_unchecked(buffer: T) -> Header { Header { buffer } } /// Shorthand for a combination of [new_unchecked] and [check_len]. /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len pub fn new_checked(buffer: T) -> Result> { let header = Self::new_unchecked(buffer); header.check_len()?; Ok(header) } /// Ensure that no accessor method will panic if called. /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_header_len]. /// /// [set_header_len]: #method.set_header_len pub fn check_len(&self) -> Result<()> { let data = self.buffer.as_ref(); let len = data.len(); if len < field::MIN_HEADER_SIZE { return Err(Error); } let of = field::OPTIONS(data[field::LENGTH]); if len < of.end { return Err(Error); } Ok(()) } /// Consume the header, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } /// Return the next header field. #[inline] pub fn next_header(&self) -> Protocol { let data = self.buffer.as_ref(); Protocol::from(data[field::NXT_HDR]) } /// Return length of the Hop-by-Hop Options header in 8-octet units, not including the first /// 8 octets. #[inline] pub fn header_len(&self) -> u8 { let data = self.buffer.as_ref(); data[field::LENGTH] } } impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> { /// Return the option data. #[inline] pub fn options(&self) -> &'a [u8] { let data = self.buffer.as_ref(); &data[field::OPTIONS(data[field::LENGTH])] } } impl + AsMut<[u8]>> Header { /// Set the next header field. #[inline] pub fn set_next_header(&mut self, value: Protocol) { let data = self.buffer.as_mut(); data[field::NXT_HDR] = value.into(); } /// Set the option data length. Length of the Hop-by-Hop Options header in 8-octet units, /// not including the first 8 octets. #[inline] pub fn set_header_len(&mut self, value: u8) { let data = self.buffer.as_mut(); data[field::LENGTH] = value; } } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> { /// Return a mutable pointer to the option data. #[inline] pub fn options_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); let len = data[field::LENGTH]; &mut data[field::OPTIONS(len)] } } impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { Ok(repr) => write!(f, "{repr}"), Err(err) => { write!(f, "IPv6 Hop-by-Hop Options ({err})")?; Ok(()) } } } } /// A high-level representation of an IPv6 Hop-by-Hop Options header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { /// The type of header immediately following the Hop-by-Hop Options header. pub next_header: Protocol, /// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets. pub length: u8, /// The options contained in the Hop-by-Hop Options header. pub options: &'a [u8], } impl<'a> Repr<'a> { /// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation. pub fn parse(header: &Header<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized, { Ok(Repr { next_header: header.next_header(), length: header.header_len(), options: header.options(), }) } /// Return the length, in bytes, of a header that will be emitted from this high-level /// representation. pub const fn buffer_len(&self) -> usize { field::OPTIONS(self.length).end } /// Emit a high-level representation into an IPv6 Hop-by-Hop Options Header. pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { header.set_next_header(self.next_header); header.set_header_len(self.length); header.options_mut().copy_from_slice(self.options); } /// Return an `Iterator` for the contained options. pub fn options(&self) -> Ipv6OptionsIterator { Ipv6OptionsIterator::new(self.options, self.buffer_len() - 2) } } impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "IPv6 Hop-by-Hop Options next_hdr={} length={} ", self.next_header, self.length ) } } #[cfg(test)] mod test { use super::*; // A Hop-by-Hop Option header with a PadN option of option data length 4. static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0x0]; // A Hop-by-Hop Option header with a PadN option of option data length 12. static REPR_PACKET_PAD12: [u8; 16] = [ 0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ]; #[test] fn test_check_len() { // zero byte buffer assert_eq!( Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len() ); // no length field assert_eq!( Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len() ); // less than 8 bytes assert_eq!( Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len() ); // valid assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len()); // valid assert_eq!( Ok(()), Header::new_unchecked(&REPR_PACKET_PAD12).check_len() ); // length field value greater than number of bytes let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; assert_eq!(Err(Error), Header::new_unchecked(&header).check_len()); } #[test] fn test_header_deconstruct() { let header = Header::new_unchecked(&REPR_PACKET_PAD4); assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.header_len(), 0); assert_eq!(header.options(), &REPR_PACKET_PAD4[2..]); let header = Header::new_unchecked(&REPR_PACKET_PAD12); assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.header_len(), 1); assert_eq!(header.options(), &REPR_PACKET_PAD12[2..]); } #[test] fn test_overlong() { let mut bytes = vec![]; bytes.extend(&REPR_PACKET_PAD4[..]); bytes.push(0); assert_eq!( Header::new_unchecked(&bytes).options().len(), REPR_PACKET_PAD4[2..].len() ); assert_eq!( Header::new_unchecked(&mut bytes).options_mut().len(), REPR_PACKET_PAD4[2..].len() ); let mut bytes = vec![]; bytes.extend(&REPR_PACKET_PAD12[..]); bytes.push(0); assert_eq!( Header::new_unchecked(&bytes).options().len(), REPR_PACKET_PAD12[2..].len() ); assert_eq!( Header::new_unchecked(&mut bytes).options_mut().len(), REPR_PACKET_PAD12[2..].len() ); } #[test] fn test_header_len_overflow() { let mut bytes = vec![]; bytes.extend(REPR_PACKET_PAD4); let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error); let mut bytes = vec![]; bytes.extend(REPR_PACKET_PAD12); let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error); } #[test] fn test_repr_parse_valid() { let header = Header::new_unchecked(&REPR_PACKET_PAD4); let repr = Repr::parse(&header).unwrap(); assert_eq!( repr, Repr { next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] } ); let header = Header::new_unchecked(&REPR_PACKET_PAD12); let repr = Repr::parse(&header).unwrap(); assert_eq!( repr, Repr { next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] } ); } #[test] fn test_repr_emit() { let repr = Repr { next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..], }; let mut bytes = [0u8; 8]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]); let repr = Repr { next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..], }; let mut bytes = [0u8; 16]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..]); } #[test] fn test_buffer_len() { let header = Header::new_unchecked(&REPR_PACKET_PAD4); let repr = Repr::parse(&header).unwrap(); assert_eq!(repr.buffer_len(), REPR_PACKET_PAD4.len()); let header = Header::new_unchecked(&REPR_PACKET_PAD12); let repr = Repr::parse(&header).unwrap(); assert_eq!(repr.buffer_len(), REPR_PACKET_PAD12.len()); } }