use super::address::Ipv6Address;
use super::builder::Ipv6PacketBuildError;
use super::next_header::Ipv6NextHeader;
use crate::error::ParseError;
pub(super) const HEADER_LENGTH: usize = 40;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Ipv6PayloadLength {
Declared(u16),
Unspecified,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Ipv6Packet<'a> {
bytes: &'a [u8],
}
impl<'a> Ipv6Packet<'a> {
#[inline]
pub fn parse(bytes: &'a [u8]) -> Result<Self, ParseError> {
let packet_length = validate(bytes)?;
Ok(Self {
bytes: &bytes[..packet_length],
})
}
#[inline]
pub fn traffic_class(&self) -> u8 {
((self.bytes[0] & 0x0f) << 4) | (self.bytes[1] >> 4)
}
#[inline]
pub fn flow_label(&self) -> u32 {
(u32::from(self.bytes[1] & 0x0f) << 16)
| (u32::from(self.bytes[2]) << 8)
| u32::from(self.bytes[3])
}
#[inline]
pub fn payload_length(&self) -> Ipv6PayloadLength {
match self.raw_payload_length() {
0 => Ipv6PayloadLength::Unspecified,
value => Ipv6PayloadLength::Declared(value),
}
}
#[inline]
pub fn raw_payload_length(&self) -> u16 {
u16::from_be_bytes(self.bytes[4..6].try_into().expect("validated IPv6 header"))
}
#[inline]
pub fn next_header(&self) -> Ipv6NextHeader {
Ipv6NextHeader::new(self.bytes[6])
}
#[inline]
pub fn hop_limit(&self) -> u8 {
self.bytes[7]
}
#[inline]
pub fn source(&self) -> Ipv6Address {
Ipv6Address::new(self.bytes[8..24].try_into().expect("validated IPv6 header"))
}
#[inline]
pub fn destination(&self) -> Ipv6Address {
Ipv6Address::new(
self.bytes[24..40]
.try_into()
.expect("validated IPv6 header"),
)
}
#[inline]
pub fn payload(&self) -> &'a [u8] {
&self.bytes[HEADER_LENGTH..]
}
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Ipv6PacketMut<'a> {
bytes: &'a mut [u8],
}
impl<'a> Ipv6PacketMut<'a> {
#[inline]
pub fn parse(bytes: &'a mut [u8]) -> Result<Self, ParseError> {
let packet_length = validate(bytes)?;
Ok(Self::from_validated(&mut bytes[..packet_length]))
}
pub(super) fn from_validated(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
#[inline]
pub fn traffic_class(&self) -> u8 {
((self.bytes[0] & 0x0f) << 4) | (self.bytes[1] >> 4)
}
#[inline]
pub fn flow_label(&self) -> u32 {
(u32::from(self.bytes[1] & 0x0f) << 16)
| (u32::from(self.bytes[2]) << 8)
| u32::from(self.bytes[3])
}
#[inline]
pub fn payload_length(&self) -> Ipv6PayloadLength {
match self.raw_payload_length() {
0 => Ipv6PayloadLength::Unspecified,
value => Ipv6PayloadLength::Declared(value),
}
}
#[inline]
pub fn raw_payload_length(&self) -> u16 {
u16::from_be_bytes(self.bytes[4..6].try_into().expect("validated IPv6 header"))
}
#[inline]
pub fn next_header(&self) -> Ipv6NextHeader {
Ipv6NextHeader::new(self.bytes[6])
}
#[inline]
pub fn hop_limit(&self) -> u8 {
self.bytes[7]
}
#[inline]
pub fn source(&self) -> Ipv6Address {
Ipv6Address::new(self.bytes[8..24].try_into().expect("validated IPv6 header"))
}
#[inline]
pub fn destination(&self) -> Ipv6Address {
Ipv6Address::new(
self.bytes[24..40]
.try_into()
.expect("validated IPv6 header"),
)
}
#[inline]
pub fn payload(&self) -> &[u8] {
&self.bytes[HEADER_LENGTH..]
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
&mut self.bytes[HEADER_LENGTH..]
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.bytes
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
self.bytes
}
#[inline]
pub fn set_traffic_class(&mut self, value: u8) {
self.bytes[0] = (self.bytes[0] & 0xf0) | (value >> 4);
self.bytes[1] = (self.bytes[1] & 0x0f) | (value << 4);
}
#[inline]
pub fn set_flow_label(&mut self, value: u32) -> Result<(), Ipv6PacketBuildError> {
if value > 0x000f_ffff {
return Err(Ipv6PacketBuildError::FlowLabelTooLarge);
}
self.bytes[1] = (self.bytes[1] & 0xf0) | ((value >> 16) as u8);
self.bytes[2] = (value >> 8) as u8;
self.bytes[3] = value as u8;
Ok(())
}
#[inline]
pub fn set_next_header(&mut self, value: Ipv6NextHeader) {
self.bytes[6] = value.raw();
}
#[inline]
pub fn set_hop_limit(&mut self, value: u8) {
self.bytes[7] = value;
}
#[inline]
pub fn set_source(&mut self, value: Ipv6Address) {
self.bytes[8..24].copy_from_slice(&value.octets());
}
#[inline]
pub fn set_destination(&mut self, value: Ipv6Address) {
self.bytes[24..40].copy_from_slice(&value.octets());
}
}
#[inline]
fn validate(bytes: &[u8]) -> Result<usize, ParseError> {
if bytes.len() < HEADER_LENGTH {
return Err(ParseError::Truncated {
minimum: HEADER_LENGTH,
available: bytes.len(),
});
}
let version = bytes[0] >> 4;
if version != 6 {
return Err(ParseError::InvalidVersion {
expected: 6,
actual: version,
});
}
let payload_length = usize::from(u16::from_be_bytes(
bytes[4..6].try_into().expect("validated IPv6 header"),
));
if payload_length == 0 {
return Ok(bytes.len());
}
let packet_length = HEADER_LENGTH + payload_length;
if bytes.len() < packet_length {
return Err(ParseError::Truncated {
minimum: packet_length,
available: bytes.len(),
});
}
Ok(packet_length)
}