123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- 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<T: AsRef<[u8]>> {
- 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<T: AsRef<[u8]>> Header<T> {
- /// Create a raw octet buffer with an IPv6 Hop-by-Hop Options Header structure.
- pub const fn new_unchecked(buffer: T) -> Header<T> {
- 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<Header<T>> {
- 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<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
- /// 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<T>(header: &Header<&'a T>) -> Result<Repr<'a>>
- 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<T: AsRef<[u8]> + 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());
- }
- }
|