use super::super::*;
use std::net::Ipv4Addr;
use std::fmt::{Debug, Formatter};
extern crate byteorder;
use self::byteorder::{ByteOrder, BigEndian, ReadBytesExt, WriteBytesExt};
#[derive(Clone)]
pub struct Ipv4Header {
pub differentiated_services_code_point: u8,
pub explicit_congestion_notification: u8,
pub payload_len: u16,
pub identification: u16,
pub dont_fragment: bool,
pub more_fragments: bool,
pub fragments_offset: u16,
pub time_to_live: u8,
pub protocol: u8,
pub header_checksum: u16,
pub source: [u8;4],
pub destination: [u8;4],
options_len: u8,
options_buffer: [u8;40]
}
impl SerializedSize for Ipv4Header {
const SERIALIZED_SIZE:usize = 20;
}
const IPV4_MAX_OPTIONS_LENGTH: usize = 10*4;
impl Ipv4Header {
pub fn new(payload_len: u16, time_to_live: u8, protocol: IpTrafficClass, source: [u8;4], destination: [u8;4]) -> Ipv4Header {
Ipv4Header {
differentiated_services_code_point: 0,
explicit_congestion_notification: 0,
payload_len,
identification: 0,
dont_fragment: true,
more_fragments: false,
fragments_offset: 0,
time_to_live,
protocol: protocol as u8,
header_checksum: 0,
source,
destination,
options_len: 0,
options_buffer: [0;40]
}
}
pub fn ihl(&self) -> u8 {
(self.options_len/4) + 5
}
pub fn options(&self) -> &[u8] {
&self.options_buffer[..usize::from(self.options_len)]
}
pub fn header_len(&self) -> usize {
Ipv4Header::SERIALIZED_SIZE + usize::from(self.options_len)
}
pub fn total_len(&self) -> u16 {
self.payload_len + (Ipv4Header::SERIALIZED_SIZE as u16) + u16::from(self.options_len)
}
pub fn set_payload_len(&mut self, value: usize) -> Result<(), ValueError> {
if usize::from(self.max_payload_len()) < value {
use crate::ValueError::*;
Err(Ipv4PayloadLengthTooLarge(value))
} else {
self.payload_len = value as u16;
Ok(())
}
}
pub fn max_payload_len(&self) -> u16 {
std::u16::MAX - u16::from(self.options_len) - (Ipv4Header::SERIALIZED_SIZE as u16)
}
pub fn set_options(&mut self, data: &[u8]) -> Result<(), ValueError> {
use crate::ValueError::*;
if (IPV4_MAX_OPTIONS_LENGTH < data.len()) ||
(0 != data.len() % 4)
{
Err(Ipv4OptionsLengthBad(data.len()))
} else {
self.options_buffer[..data.len()].copy_from_slice(data);
self.options_len = data.len() as u8;
Ok(())
}
}
pub fn read_from_slice(slice: &[u8]) -> Result<(Ipv4Header, &[u8]), ReadError> {
let header = Ipv4HeaderSlice::from_slice(slice)?.to_header();
let rest = &slice[header.header_len()..];
Ok((
header,
rest
))
}
pub fn read<T: io::Read + io::Seek + Sized>(reader: &mut T) -> Result<Ipv4Header, ReadError> {
let value = reader.read_u8()?;
let version = value >> 4;
if 4 != version {
return Err(ReadError::Ipv4UnexpectedVersion(version));
}
Ipv4Header::read_without_version(reader, value & 0xf)
}
pub fn read_without_version<T: io::Read + io::Seek + Sized>(reader: &mut T, version_rest: u8) -> Result<Ipv4Header, ReadError> {
let ihl = version_rest;
if ihl < 5 {
use crate::ReadError::*;
return Err(Ipv4HeaderLengthBad(ihl));
}
let (dscp, ecn) = {
let value = reader.read_u8()?;
(value >> 2, value & 0x3)
};
let header_length = u16::from(ihl)*4;
let total_length = reader.read_u16::<BigEndian>()?;
if total_length < header_length {
use crate::ReadError::*;
return Err(Ipv4TotalLengthTooSmall(total_length));
}
let identification = reader.read_u16::<BigEndian>()?;
let (dont_fragment, more_fragments, fragments_offset) = {
let mut values: [u8; 2] = [0;2];
reader.read_exact(&mut values)?;
(0 != (values[0] & 0x40),
0 != (values[0] & 0x20),
{
let buf = [values[0] & 0x1f, values[1]];
let mut cursor = io::Cursor::new(&buf);
cursor.read_u16::<BigEndian>()?
})
};
Ok(Ipv4Header{
differentiated_services_code_point: dscp,
explicit_congestion_notification: ecn,
payload_len: total_length - header_length,
identification,
dont_fragment,
more_fragments,
fragments_offset,
time_to_live: reader.read_u8()?,
protocol: reader.read_u8()?,
header_checksum: reader.read_u16::<BigEndian>()?,
source: {
let mut values: [u8;4] = [0;4];
reader.read_exact(&mut values)?;
values
},
destination: {
let mut values: [u8;4] = [0;4];
reader.read_exact(&mut values)?;
values
},
options_len: (ihl - 5)*4,
options_buffer: {
let mut values: [u8;40] = [0;40];
let options_len = usize::from(ihl - 5)*4;
if options_len > 0 {
reader.read_exact(&mut values[..options_len])?;
}
values
},
})
}
pub fn check_ranges(&self) -> Result<(), ValueError> {
use crate::ErrorField::*;
max_check_u8(self.differentiated_services_code_point, 0x3f, Ipv4Dscp)?;
max_check_u8(self.explicit_congestion_notification, 0x3, Ipv4Ecn)?;
max_check_u16(self.fragments_offset, 0x1fff, Ipv4FragmentsOffset)?;
max_check_u16(self.payload_len, self.max_payload_len(), Ipv4PayloadLength)?;
Ok(())
}
pub fn write<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), WriteError> {
self.check_ranges()?;
self.write_ipv4_header_internal(writer, self.calc_header_checksum_unchecked())
}
pub fn write_raw<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), WriteError> {
self.check_ranges()?;
self.write_ipv4_header_internal(writer, self.header_checksum)
}
fn write_ipv4_header_internal<T: io::Write>(&self, write: &mut T, header_checksum: u16) -> Result<(), WriteError> {
write.write_u8((4 << 4) | self.ihl())?;
write.write_u8((self.differentiated_services_code_point << 2) | self.explicit_congestion_notification)?;
write.write_u16::<BigEndian>(self.total_len())?;
write.write_u16::<BigEndian>(self.identification)?;
{
let mut buf: [u8;2] = [0;2];
BigEndian::write_u16(&mut buf, self.fragments_offset);
let flags = {
let mut result = 0;
if self.dont_fragment {
result |= 64;
}
if self.more_fragments {
result |= 32;
}
result
};
write.write_u8(
flags |
(buf[0] & 0x1f),
)?;
write.write_u8(
buf[1]
)?;
}
write.write_u8(self.time_to_live)?;
write.write_u8(self.protocol)?;
write.write_u16::<BigEndian>(header_checksum)?;
write.write_all(&self.source)?;
write.write_all(&self.destination)?;
write.write_all(&self.options())?;
Ok(())
}
pub fn calc_header_checksum(&self) -> Result<u16, ValueError> {
self.check_ranges()?;
Ok(self.calc_header_checksum_unchecked())
}
fn calc_header_checksum_unchecked(&self) -> u16 {
let mut sum: u32 = [
BigEndian::read_u16(&[ (4 << 4) | self.ihl(),
(self.differentiated_services_code_point << 2) | self.explicit_congestion_notification ]),
self.total_len(),
self.identification,
{
let mut buf: [u8;2] = [0;2];
BigEndian::write_u16(&mut buf, self.fragments_offset);
let flags = {
let mut result = 0;
if self.dont_fragment {
result |= 64;
}
if self.more_fragments {
result |= 32;
}
result
};
BigEndian::read_u16(&[flags | (buf[0] & 0x1f), buf[1]])
},
BigEndian::read_u16(&[self.time_to_live, self.protocol]),
BigEndian::read_u16(&self.source[0..2]),
BigEndian::read_u16(&self.source[2..4]),
BigEndian::read_u16(&self.destination[0..2]),
BigEndian::read_u16(&self.destination[2..4])
].into_iter().map(|x| u32::from(*x)).sum();
let options = self.options();
for i in 0..(options.len()/2) {
sum += u32::from( BigEndian::read_u16(&options[i*2..i*2 + 2]) );
}
let carry_add = (sum & 0xffff) + (sum >> 16);
!( ((carry_add & 0xffff) + (carry_add >> 16)) as u16 )
}
}
impl Default for Ipv4Header {
fn default() -> Ipv4Header {
Ipv4Header {
differentiated_services_code_point: 0,
explicit_congestion_notification: 0,
payload_len: 0,
identification: 0,
dont_fragment: true,
more_fragments: false,
fragments_offset: 0,
time_to_live: 0,
protocol: 0,
header_checksum: 0,
source: [0;4],
destination: [0;4],
options_len: 0,
options_buffer: [0;40]
}
}
}
impl Debug for Ipv4Header {
fn fmt(&self, fotmatter: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(fotmatter, "Ipv4Header {{ ihl: {}, differentiated_services_code_point: {}, explicit_congestion_notification: {}, payload_len: {}, identification: {}, dont_fragment: {}, more_fragments: {}, fragments_offset: {}, time_to_live: {}, protocol: {}, header_checksum: {}, source: {:?}, destination: {:?}, options: {:?} }}",
self.ihl(),
self.differentiated_services_code_point,
self.explicit_congestion_notification,
self.payload_len,
self.identification,
self.dont_fragment,
self.more_fragments,
self.fragments_offset,
self.time_to_live,
self.protocol,
self.header_checksum,
self.source,
self.destination,
self.options())
}
}
impl std::cmp::PartialEq for Ipv4Header {
fn eq(&self, other: &Ipv4Header) -> bool {
self.differentiated_services_code_point == other.differentiated_services_code_point &&
self.explicit_congestion_notification == other.explicit_congestion_notification &&
self.payload_len == other.payload_len &&
self.identification == other.identification &&
self.dont_fragment == other.dont_fragment &&
self.more_fragments == other.more_fragments &&
self.fragments_offset == other.fragments_offset &&
self.time_to_live == other.time_to_live &&
self.protocol == other.protocol &&
self.header_checksum == other.header_checksum &&
self.source == other.source &&
self.destination == other.destination &&
self.options_len == other.options_len &&
self.options() == other.options()
}
}
impl std::cmp::Eq for Ipv4Header {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Ipv4HeaderSlice<'a> {
slice: &'a [u8]
}
impl<'a> Ipv4HeaderSlice<'a> {
pub fn from_slice(slice: &'a[u8]) -> Result<Ipv4HeaderSlice<'a>, ReadError> {
use crate::ReadError::*;
if slice.len() < Ipv4Header::SERIALIZED_SIZE {
return Err(UnexpectedEndOfSlice(Ipv4Header::SERIALIZED_SIZE));
}
let (version, ihl) = {
let value = slice[0];
(value >> 4, value & 0xf)
};
if 4 != version {
return Err(Ipv4UnexpectedVersion(version));
}
if ihl < 5 {
use crate::ReadError::*;
return Err(Ipv4HeaderLengthBad(ihl));
}
let header_length = (usize::from(ihl))*4;
if slice.len() < header_length {
return Err(UnexpectedEndOfSlice(header_length));
}
let total_length = BigEndian::read_u16(&slice[2..4]);
if total_length < header_length as u16 {
return Err(Ipv4TotalLengthTooSmall(total_length))
}
Ok(Ipv4HeaderSlice {
slice: &slice[..header_length]
})
}
#[inline]
pub fn slice(&self) -> &'a [u8] {
self.slice
}
pub fn version(&self) -> u8 {
self.slice[0] >> 4
}
pub fn ihl(&self) -> u8 {
self.slice[0] & 0xf
}
pub fn dcp(&self) -> u8 {
self.slice[1] >> 2
}
pub fn ecn(&self) -> u8 {
self.slice[1] & 0x3
}
pub fn total_len(&self) -> u16 {
BigEndian::read_u16(&self.slice[2..4])
}
pub fn payload_len(&self) -> u16 {
self.total_len() - u16::from(self.ihl())*4
}
pub fn identification(&self) -> u16 {
BigEndian::read_u16(&self.slice[4..6])
}
pub fn dont_fragment(&self) -> bool {
0 != (self.slice[6] & 0x40)
}
pub fn more_fragments(&self) -> bool {
0 != (self.slice[6] & 0x20)
}
pub fn fragments_offset(&self) -> u16 {
let buf = [self.slice[6] & 0x1f, self.slice[7]];
BigEndian::read_u16(&buf[..])
}
pub fn ttl(&self) -> u8 {
self.slice[8]
}
pub fn protocol(&self) -> u8 {
self.slice[9]
}
pub fn header_checksum(&self) -> u16 {
BigEndian::read_u16(&self.slice[10..12])
}
pub fn source(&self) -> &'a [u8] {
&self.slice[12..16]
}
pub fn source_addr(&self) -> Ipv4Addr {
let mut result: [u8; 4] = Default::default();
result.copy_from_slice(self.source());
Ipv4Addr::from(result)
}
pub fn destination(&self) -> &'a [u8] {
&self.slice[16..20]
}
pub fn destination_addr(&self) -> Ipv4Addr {
let mut result: [u8; 4] = Default::default();
result.copy_from_slice(self.destination());
Ipv4Addr::from(result)
}
pub fn options(&self) -> &'a [u8] {
&self.slice[20..]
}
pub fn to_header(&self) -> Ipv4Header {
let options = self.options();
Ipv4Header {
differentiated_services_code_point: self.dcp(),
explicit_congestion_notification: self.ecn(),
payload_len: self.payload_len(),
identification: self.identification(),
dont_fragment: self.dont_fragment(),
more_fragments: self.more_fragments(),
fragments_offset: self.fragments_offset(),
time_to_live: self.ttl(),
protocol: self.protocol(),
header_checksum: self.header_checksum(),
source: {
let mut result: [u8; 4] = Default::default();
result.copy_from_slice(self.source());
result
},
destination: {
let mut result: [u8; 4] = Default::default();
result.copy_from_slice(self.destination());
result
},
options_len: options.len() as u8,
options_buffer: {
let mut result: [u8;40] = [0;40];
result[..options.len()].copy_from_slice(options);
result
}
}
}
}