| 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<Ipv6HeaderSlice<'a>, 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); |
| } |
| } |
| } |