blob: eea301e169f8085de443aff5f08d516d61bb8ab3 [file] [log] [blame]
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);
}
}
}