use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::wire::{Error, Result, ip::Protocol};
#[derive(Debug, PartialEq)]
pub struct Header<T: AsRef<[u8]>> {
buffer: T
}
mod field {
use crate::wire::field::Field;
pub(crate) const NXT_HDR: usize = 0;
pub(crate) const RESERVED: usize = 1;
pub(crate) const FR_OF_M: Field = 2..4;
pub(crate) const IDENT: Field = 4..8;
}
impl<T: AsRef<[u8]>> Header<T> {
pub fn new_unchecked(buffer: T) -> Header<T> {
Header { buffer }
}
pub fn new_checked(buffer: T) -> Result<Header<T>> {
let header = Self::new_unchecked(buffer);
header.check_len()?;
Ok(header)
}
pub fn check_len(&self) -> Result<()> {
let data = self.buffer.as_ref();
let len = data.len();
if len < field::IDENT.end {
Err(Error::Truncated)
} else {
Ok(())
}
}
pub fn into_inner(self) -> T {
self.buffer
}
#[inline]
pub fn next_header(&self) -> Protocol {
let data = self.buffer.as_ref();
Protocol::from(data[field::NXT_HDR])
}
#[inline]
pub fn frag_offset(&self) -> u16 {
let data = self.buffer.as_ref();
NetworkEndian::read_u16(&data[field::FR_OF_M]) >> 3
}
#[inline]
pub fn more_frags(&self) -> bool {
let data = self.buffer.as_ref();
(data[3] & 0x1) == 1
}
#[inline]
pub fn ident(&self) -> u32 {
let data = self.buffer.as_ref();
NetworkEndian::read_u32(&data[field::IDENT])
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
#[inline]
pub fn set_next_header(&mut self, value: Protocol) {
let data = self.buffer.as_mut();
data[field::NXT_HDR] = value.into();
}
#[inline]
pub fn clear_reserved(&mut self) {
let data = self.buffer.as_mut();
data[field::RESERVED] = 0;
data[3] = data[3] & 0xf9;
}
#[inline]
pub fn set_frag_offset(&mut self, value: u16) {
let data = self.buffer.as_mut();
let raw = ((value & 0x1fff) << 3) | ((data[3] & 0x7) as u16);
NetworkEndian::write_u16(&mut data[field::FR_OF_M], raw);
}
#[inline]
pub fn set_more_frags(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = (data[3] & 0xfe) | (value as u8 & 0x1);
data[3] = raw;
}
#[inline]
pub fn set_ident(&mut self, value: u32) {
let data = self.buffer.as_mut();
NetworkEndian::write_u32(&mut data[field::IDENT], value);
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
Ok(repr) => write!(f, "{}", repr),
Err(err) => {
write!(f, "IPv6 Fragment ({})", err)?;
Ok(())
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Repr {
pub next_header: Protocol,
pub frag_offset: u16,
pub more_frags: bool,
pub ident: u32,
}
impl Repr {
pub fn parse<T>(header: &Header<&T>) -> Result<Repr> where T: AsRef<[u8]> + ?Sized {
Ok(Repr {
next_header: header.next_header(),
frag_offset: header.frag_offset(),
more_frags: header.more_frags(),
ident: header.ident()
})
}
pub fn buffer_len(&self) -> usize {
field::IDENT.end
}
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
header.set_next_header(self.next_header);
header.clear_reserved();
header.set_frag_offset(self.frag_offset);
header.set_more_frags(self.more_frags);
header.set_ident(self.ident);
}
}
impl<'a> fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}",
self.next_header, self.frag_offset, self.more_frags, self.ident)
}
}
#[cfg(test)]
mod test {
use super::*;
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1,
0x0, 0x0, 0x30, 0x39];
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0,
0x0, 0x1, 0x9, 0x32];
#[test]
fn test_check_len() {
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len());
assert_eq!(Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len());
}
#[test]
fn test_header_deconstruct() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
assert_eq!(header.next_header(), Protocol::Tcp);
assert_eq!(header.frag_offset(), 0);
assert_eq!(header.more_frags(), true);
assert_eq!(header.ident(), 12345);
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
assert_eq!(header.next_header(), Protocol::Tcp);
assert_eq!(header.frag_offset(), 320);
assert_eq!(header.more_frags(), false);
assert_eq!(header.ident(), 67890);
}
#[test]
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 });
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 });
}
#[test]
fn test_repr_emit() {
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 };
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]);
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 };
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_HEADER_LAST_FRAG[0..8]);
}
#[test]
fn test_buffer_len() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr.buffer_len(), BYTES_HEADER_MORE_FRAG.len());
}
}