use crate::ietf::ip::Protocol;
use crate::ietf::ipv6::Address;
use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct TrafficClass(pub u8);
impl From<u8> for TrafficClass {
fn from(value: u8) -> Self {
Self(value)
}
}
impl From<TrafficClass> for u8 {
fn from(value: TrafficClass) -> Self {
value.0
}
}
impl Codec for TrafficClass {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.0.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
Ok(Self(u8::decode(reader, ())?))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct FlowLabel(pub u32);
impl From<u32> for FlowLabel {
fn from(value: u32) -> Self {
Self(value & 0x000F_FFFF)
}
}
impl From<FlowLabel> for u32 {
fn from(value: FlowLabel) -> Self {
value.0 & 0x000F_FFFF
}
}
impl Codec for FlowLabel {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(self.0 & 0x000F_FFFF).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let val = u32::decode(reader, ())?;
Ok(Self(val & 0x000F_FFFF))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Header {
pub traffic_class: TrafficClass,
pub flow_label: FlowLabel,
pub payload_length: u16,
pub next_header: Protocol,
pub hop_limit: u8,
pub source_address: Address,
pub destination_address: Address,
}
impl Header {
pub const VERSION: u8 = 6;
}
impl Codec for Header {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let v_tc_fl = ((Self::VERSION as u32) << 28)
| ((self.traffic_class.0 as u32) << 20)
| (self.flow_label.0 & 0x000F_FFFF);
writer.write_u32_be(v_tc_fl)?;
self.payload_length.encode(writer, ())?;
self.next_header.encode(writer, ())?;
self.hop_limit.encode(writer, ())?;
self.source_address.encode(writer, ())?;
self.destination_address.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let v_tc_fl = reader.read_u32_be()?;
let version = v_tc_fl >> 28;
if version != Self::VERSION as u32 {
return Err(BufError::UnexpectedValue);
}
let traffic_class = TrafficClass(((v_tc_fl >> 20) & 0xFF) as u8);
let flow_label = FlowLabel(v_tc_fl & 0x000F_FFFF);
let payload_length = reader.read_u16_be()?;
let next_header = Protocol::decode(reader, ())?;
let hop_limit = reader.read_u8()?;
let source_address = Address::decode(reader, ())?;
let destination_address = Address::decode(reader, ())?;
Ok(Self {
traffic_class,
flow_label,
payload_length,
next_header,
hop_limit,
source_address,
destination_address,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Cursor;
use core::fmt::Debug;
fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
etalon_struct: T,
etalon_bytes: &[u8],
context: C,
) {
let mut encoded_bytes = vec![];
{
let writer = &mut Cursor::new(&mut encoded_bytes);
etalon_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
let decoded_struct = {
let reader = &mut Cursor::new(&encoded_bytes);
T::decode(reader, context).unwrap()
};
assert_eq!(etalon_struct, decoded_struct);
}
#[test]
fn header_roundtrip() {
let etalon_bytes = &[
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 etalon_struct = Header {
traffic_class: TrafficClass(0),
flow_label: FlowLabel(0),
payload_length: 32,
next_header: Protocol::TCP,
hop_limit: 64,
source_address: Address::from([
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]),
destination_address: Address::from([
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
]),
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
}