use crate::ietf::ip::Protocol;
use crate::{Buf, BufMut, BufResult, Codec, Cursor};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HopByHopOptionsHeader {
pub next_header: Protocol,
pub hdr_ext_len: u8,
pub options: Vec<u8>,
}
impl Codec for HopByHopOptionsHeader {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.next_header.encode(writer, ())?;
self.hdr_ext_len.encode(writer, ())?;
writer.write_slice(&self.options)
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let next_header = Protocol::decode(reader, ())?;
let hdr_ext_len = u8::decode(reader, ())?;
let data_len = (hdr_ext_len as usize) * 8;
let mut options = vec![0u8; data_len];
if data_len > 0 {
reader.read_into(&mut options)?;
}
Ok(Self {
next_header,
hdr_ext_len,
options,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoutingHeader {
pub next_header: Protocol,
pub hdr_ext_len: u8,
pub routing_type: u8,
pub segments_left: u8,
pub data: Vec<u8>,
}
impl Codec for RoutingHeader {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.next_header.encode(writer, ())?;
self.hdr_ext_len.encode(writer, ())?;
self.routing_type.encode(writer, ())?;
self.segments_left.encode(writer, ())?;
0u32.encode(writer, ())?; writer.write_slice(&self.data)
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let next_header = Protocol::decode(reader, ())?;
let hdr_ext_len = u8::decode(reader, ())?;
let routing_type = u8::decode(reader, ())?;
let segments_left = u8::decode(reader, ())?;
let _reserved = u32::decode(reader, ())?;
let data_len = (hdr_ext_len as usize) * 8 - 4; let mut data = vec![0u8; data_len];
if data_len > 0 {
reader.read_into(&mut data)?;
}
Ok(Self {
next_header,
hdr_ext_len,
routing_type,
segments_left,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FragmentHeader {
pub next_header: Protocol,
pub reserved: u8,
pub fragment_offset: u16,
pub m: bool,
pub identification: u32,
}
impl Codec for FragmentHeader {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.next_header.encode(writer, ())?;
self.reserved.encode(writer, ())?;
let frag_off_res_m: u16 = ((self.fragment_offset & 0x1FFF) << 3) | ((self.m as u16) & 0x01);
frag_off_res_m.encode(writer, ())?;
self.identification.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let next_header = Protocol::decode(reader, ())?;
let reserved = u8::decode(reader, ())?;
let frag_off_res_m = reader.read_u16_be()?;
let fragment_offset = frag_off_res_m >> 3;
let m = (frag_off_res_m & 0x01) != 0;
let identification = reader.read_u32_be()?;
Ok(Self {
next_header,
reserved,
fragment_offset,
m,
identification,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DestinationOptionsHeader {
pub next_header: Protocol,
pub hdr_ext_len: u8,
pub options: Vec<u8>,
}
impl Codec for DestinationOptionsHeader {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.next_header.encode(writer, ())?;
self.hdr_ext_len.encode(writer, ())?;
writer.write_slice(&self.options)
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let next_header = Protocol::decode(reader, ())?;
let hdr_ext_len = u8::decode(reader, ())?;
let data_len = (hdr_ext_len as usize) * 8;
let mut options = vec![0u8; data_len];
if data_len > 0 {
reader.read_into(&mut options)?;
}
Ok(Self {
next_header,
hdr_ext_len,
options,
})
}
}
#[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 hop_by_hop_options() {
let etalon_bytes = &[0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; let etalon_struct = HopByHopOptionsHeader {
next_header: Protocol::TCP,
hdr_ext_len: 1,
options: vec![0; 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn routing_header() {
let etalon_bytes = &[
0x06, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let etalon_struct = RoutingHeader {
next_header: Protocol::TCP,
hdr_ext_len: 2, routing_type: 0,
segments_left: 1,
data: vec![0; 12],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn fragment_header() {
let etalon_bytes = &[
0x06, 0x00, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78, ];
let etalon_struct = FragmentHeader {
next_header: Protocol::TCP,
reserved: 0,
fragment_offset: 0,
m: true,
identification: 0x12345678,
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn destination_options() {
let etalon_bytes = &[0x06, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let etalon_struct = DestinationOptionsHeader {
next_header: Protocol::TCP,
hdr_ext_len: 1,
options: vec![1, 2, 3, 4, 5, 6, 7, 8],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
}