use crate::*; use core::slice::from_raw_parts; /// A slice containing an ipv6 header of a network package. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Ipv6HeaderSlice<'a> { slice: &'a [u8], } impl<'a> Ipv6HeaderSlice<'a> { /// Creates a slice containing an ipv6 header (without header extensions). pub fn from_slice(slice: &'a [u8]) -> Result, err::ipv6::HeaderSliceError> { use err::ipv6::{HeaderError::*, HeaderSliceError::*}; // check length if slice.len() < Ipv6Header::LEN { return Err(Len(err::LenError { required_len: Ipv6Header::LEN, len: slice.len(), len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, })); } // read version & ihl // // SAFETY: // This is safe as the slice len is checked to be // at least 40 bytes at the start of the function. let version_number = unsafe { slice.get_unchecked(0) >> 4 }; // check version if 6 != version_number { return Err(Content(UnexpectedVersion { version_number })); } // all good Ok(Ipv6HeaderSlice { // SAFETY: // This is safe as the slice length is checked to be // at least Ipv6Header::LEN (40) // at the start of the function. slice: unsafe { from_raw_parts(slice.as_ptr(), Ipv6Header::LEN) }, }) } /// Converts the given slice into a ipv6 header slice WITHOUT any /// checks to ensure that the data present is an ipv4 header or that the /// slice length is matching the header length. /// /// If you are not sure what this means, use [`Ipv6HeaderSlice::from_slice`] /// instead. /// /// # Safety /// /// It must ensured that the slice length is at least [`Ipv6Header::LEN`]. #[inline] pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> Ipv6HeaderSlice { Ipv6HeaderSlice { slice } } /// Returns the slice containing the ipv6 header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Read the "version" field from the slice (should be 6). #[inline] pub fn version(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { *self.slice.get_unchecked(0) >> 4 } } /// Read the "traffic class" field from the slice. #[inline] pub fn traffic_class(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { (self.slice.get_unchecked(0) << 4) | (self.slice.get_unchecked(1) >> 4) } } /// Read the "flow label" field from the slice. #[inline] pub fn flow_label(&self) -> Ipv6FlowLabel { unsafe { // SAFETY: // Slice access safe as the slice length is set to Ipv6Header::LEN (40) // during construction of the struct. // Conversion to flow label safe as the bitmask & 0 constant guarantee // that the value does not exceed 20 bits. Ipv6FlowLabel::new_unchecked(u32::from_be_bytes([ 0, *self.slice.get_unchecked(1) & 0xf, *self.slice.get_unchecked(2), *self.slice.get_unchecked(3), ])) } } /// Read the "payload length" field from the slice. The length should contain the length of all extension headers and payload. #[inline] pub fn payload_length(&self) -> u16 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } /// Read the "next header" field from the slice. /// /// The next header value specifies what the next header or transport /// layer protocol is (see [IpNumber] or [ip_number] for a definitions of ids). #[inline] pub fn next_header(&self) -> IpNumber { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. IpNumber(unsafe { *self.slice.get_unchecked(6) }) } /// Read the "hop limit" field from the slice. The hop limit specifies the number of hops the packet can take before it is discarded. #[inline] pub fn hop_limit(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { *self.slice.get_unchecked(7) } } /// Returns a slice containing the IPv6 source address. #[inline] pub fn source(&self) -> [u8; 16] { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(8)) } } /// Return the ipv6 source address as an std::net::Ipv6Addr #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[inline] pub fn source_addr(&self) -> std::net::Ipv6Addr { std::net::Ipv6Addr::from(self.source()) } /// Returns a slice containing the IPv6 destination address. #[inline] pub fn destination(&self) -> [u8; 16] { // SAFETY: // Safe as the slice length is set to // Ipv6Header::LEN (40) during construction // of the struct. unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(24)) } } /// Return the ipv6 destination address as an std::net::Ipv6Addr #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[inline] pub fn destination_addr(&self) -> std::net::Ipv6Addr { std::net::Ipv6Addr::from(self.destination()) } /// Decode all the fields and copy the results to a Ipv6Header struct pub fn to_header(&self) -> Ipv6Header { Ipv6Header { traffic_class: self.traffic_class(), flow_label: self.flow_label(), payload_length: self.payload_length(), next_header: self.next_header(), hop_limit: self.hop_limit(), source: self.source(), destination: self.destination(), } } /// Returns the length of the IPv6 header in bytes (same as [`crate::Ipv6Header::LEN`]). pub const fn header_len(&self) -> usize { Ipv6Header::LEN } } #[cfg(test)] mod test { use crate::{err::ipv6::HeaderError::*, err::ipv6::HeaderSliceError::*, test_gens::*, *}; use alloc::format; use proptest::*; #[test] fn debug() { let header: Ipv6Header = Default::default(); let bytes = header.to_bytes(); let slice = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!( format!("{:?}", slice), format!("Ipv6HeaderSlice {{ slice: {:?} }}", &bytes[..]) ); } proptest! { #[test] fn clone_eq(header in ipv6_any()) { let bytes = header.to_bytes(); let slice = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(slice.clone(), slice); } } proptest! { #[test] fn from_slice( header in ipv6_any(), bad_version in 0..=0b1111u8) { // ok read { let bytes = header.to_bytes(); let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(actual.slice(), &bytes[..]); } // version error if bad_version != 6 { let mut bytes = header.to_bytes(); // inject a bad version number bytes[0] = (0b1111 & bytes[0]) | (bad_version << 4); assert_eq!( Ipv6HeaderSlice::from_slice(&bytes).unwrap_err(), Content(UnexpectedVersion{ version_number: bad_version }) ); } // length error { let bytes = header.to_bytes(); for len in 0..bytes.len() { assert_eq!( Ipv6HeaderSlice::from_slice(&bytes[..len]) .unwrap_err(), Len(err::LenError{ required_len: Ipv6Header::LEN, len: len, len_source: LenSource::Slice, layer: err::Layer::Ipv6Header, layer_start_offset: 0, }) ); } } } } proptest! { #[test] fn from_slice_unchecked(header in ipv6_any()) { let bytes = header.to_bytes(); let actual = unsafe { Ipv6HeaderSlice::from_slice_unchecked(&bytes) }; assert_eq!(actual.slice(), &bytes[..]); } } proptest! { #[test] fn getters(header in ipv6_any()) { let bytes = header.to_bytes(); let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(actual.slice(), &bytes[..]); assert_eq!(actual.version(), 6); assert_eq!(actual.traffic_class(), header.traffic_class); assert_eq!(actual.flow_label(), header.flow_label); assert_eq!(actual.payload_length(), header.payload_length); assert_eq!(actual.next_header(), header.next_header); assert_eq!(actual.hop_limit(), header.hop_limit); assert_eq!(actual.source(), header.source); assert_eq!(actual.destination(), header.destination); } } #[cfg(feature = "std")] proptest! { #[test] fn getters_std(header in ipv6_any()) { let bytes = header.to_bytes(); let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(actual.source_addr(), std::net::Ipv6Addr::from(header.source)); assert_eq!(actual.destination_addr(), std::net::Ipv6Addr::from(header.destination)); } } proptest! { #[test] fn to_header(header in ipv6_any()) { let bytes = header.to_bytes(); let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(actual.to_header(), header); } } }