use crate::ietf::ip::Protocol;
use crate::ietf::ipv6::Address;
use crate::{Buf, BufError, BufMut, BufResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HeaderMapping<T> {
buffer: T,
}
impl<T> HeaderMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
}
impl<T: Buf> HeaderMapping<T> {
pub fn read_version(&self) -> u8 {
unsafe { (self.buffer.get_u32_be_unchecked(0) >> 28) as u8 }
}
pub fn read_traffic_class(&self) -> u8 {
unsafe { ((self.buffer.get_u32_be_unchecked(0) >> 20) & 0xFF) as u8 }
}
pub fn read_flow_label(&self) -> u32 {
unsafe { self.buffer.get_u32_be_unchecked(0) & 0x000F_FFFF }
}
pub fn read_payload_length(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(4) }
}
pub fn read_next_header(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(6)) }
}
pub fn read_hop_limit(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(7) }
}
pub fn read_source_address(&self) -> Address {
unsafe {
let mut octets = [0u8; 16];
for i in 0..16 {
octets[i] = self.buffer.get_u8_unchecked(8 + i);
}
Address::from(octets)
}
}
pub fn read_destination_address(&self) -> Address {
unsafe {
let mut octets = [0u8; 16];
for i in 0..16 {
octets[i] = self.buffer.get_u8_unchecked(24 + i);
}
Address::from(octets)
}
}
}
impl<T: BufMut> HeaderMapping<T> {
pub fn write_version_tc_fl(&mut self, version: u8, tc: u8, fl: u32) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::UnexpectedEof);
}
let val = ((version as u32 & 0x0F) << 28) | ((tc as u32 & 0xFF) << 20) | (fl & 0x000F_FFFF);
unsafe { self.buffer.set_u32_be_unchecked(0, val) }
Ok(())
}
pub fn write_payload_length(&mut self, length: u16) -> BufResult<()> {
if self.buffer.length() < 6 {
return Err(BufError::UnexpectedEof);
}
unsafe { self.buffer.set_u16_be_unchecked(4, length) }
Ok(())
}
pub fn write_next_header(&mut self, protocol: Protocol) -> BufResult<()> {
if self.buffer.length() < 7 {
return Err(BufError::UnexpectedEof);
}
unsafe { self.buffer.set_u8_unchecked(6, u8::from(protocol)) }
Ok(())
}
pub fn write_hop_limit(&mut self, limit: u8) -> BufResult<()> {
if self.buffer.length() < 8 {
return Err(BufError::UnexpectedEof);
}
unsafe { self.buffer.set_u8_unchecked(7, limit) }
Ok(())
}
pub fn write_source_address(&mut self, addr: Address) -> BufResult<()> {
if self.buffer.length() < 24 {
return Err(BufError::UnexpectedEof);
}
let octets = addr.octets();
unsafe {
for i in 0..16 {
self.buffer.set_u8_unchecked(8 + i, octets[i]);
}
}
Ok(())
}
pub fn write_destination_address(&mut self, addr: Address) -> BufResult<()> {
if self.buffer.length() < 40 {
return Err(BufError::UnexpectedEof);
}
let octets = addr.octets();
unsafe {
for i in 0..16 {
self.buffer.set_u8_unchecked(24 + i, octets[i]);
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HopByHopOptionsMapping<T> {
buffer: T,
}
impl<T> HopByHopOptionsMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
}
impl<T: Buf> HopByHopOptionsMapping<T> {
pub fn read_next_header(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
}
pub fn read_hdr_ext_len(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RoutingHeaderMapping<T> {
buffer: T,
}
impl<T> RoutingHeaderMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
}
impl<T: Buf> RoutingHeaderMapping<T> {
pub fn read_next_header(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
}
pub fn read_hdr_ext_len(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_routing_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(2) }
}
pub fn read_segments_left(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(3) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FragmentHeaderMapping<T> {
buffer: T,
}
impl<T> FragmentHeaderMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
}
impl<T: Buf> FragmentHeaderMapping<T> {
pub fn read_next_header(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
}
pub fn read_reserved(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_fragment_offset(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(2) >> 3 }
}
pub fn read_m(&self) -> bool {
unsafe { (self.buffer.get_u16_be_unchecked(2) & 0x01) != 0 }
}
pub fn read_identification(&self) -> u32 {
unsafe { self.buffer.get_u32_be_unchecked(4) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DestinationOptionsMapping<T> {
buffer: T,
}
impl<T> DestinationOptionsMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
}
impl<T: Buf> DestinationOptionsMapping<T> {
pub fn read_next_header(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
}
pub fn read_hdr_ext_len(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
}
#[cfg(test)]
mod tests {
use super::{Address, HeaderMapping, Protocol};
#[test]
fn read_ipv6_fields() {
let buffer = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
let mapping = HeaderMapping::new(&buffer[..]);
assert_eq!(mapping.read_version(), 6);
assert_eq!(mapping.read_traffic_class(), 0);
assert_eq!(mapping.read_flow_label(), 0);
assert_eq!(mapping.read_payload_length(), 32);
assert_eq!(mapping.read_next_header(), Protocol::TCP);
assert_eq!(mapping.read_hop_limit(), 64);
assert_eq!(mapping.read_source_address().octets()[0], 0x20);
assert_eq!(mapping.read_destination_address().octets()[15], 0x02);
}
#[test]
fn write_ipv6_fields() {
let mut buffer = [0u8; 40];
{
let mut mapping = HeaderMapping::new(&mut buffer[..]);
mapping.write_version_tc_fl(6, 0, 0).unwrap();
mapping.write_payload_length(32).unwrap();
mapping.write_next_header(Protocol::TCP).unwrap();
mapping.write_hop_limit(64).unwrap();
mapping
.write_source_address(Address::from([
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
]))
.unwrap();
mapping
.write_destination_address(Address::from([
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02,
]))
.unwrap();
}
assert_eq!(&buffer[0..4], &[0x60, 0x00, 0x00, 0x00]);
assert_eq!(buffer[6], 0x06);
}
}