use crate::ietf::ip::Protocol;
use crate::ietf::ipv4::{Address, Options};
use crate::{Buf, BufMut};
#[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_u8_unchecked(0) >> 4) & 0x0F }
}
pub fn read_ihl(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) & 0x0F }
}
pub fn read_dscp(&self) -> u8 {
unsafe { (self.buffer.get_u8_unchecked(1) >> 2) & 0x3F }
}
pub fn read_ecn(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) & 0x03 }
}
pub fn read_total_length(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(2) }
}
pub fn read_identification(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(4) }
}
pub fn read_df(&self) -> bool {
unsafe { (self.buffer.get_u16_be_unchecked(6) & 0x4000) != 0 }
}
pub fn read_mf(&self) -> bool {
unsafe { (self.buffer.get_u16_be_unchecked(6) & 0x2000) != 0 }
}
pub fn read_fragment_offset(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(6) & 0x1FFF }
}
pub fn read_ttl(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(8) }
}
pub fn read_protocol(&self) -> Protocol {
unsafe { Protocol::from(self.buffer.get_u8_unchecked(9)) }
}
pub fn read_checksum(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(10) }
}
pub fn read_source_address(&self) -> Address {
unsafe {
Address::from([
self.buffer.get_u8_unchecked(12),
self.buffer.get_u8_unchecked(13),
self.buffer.get_u8_unchecked(14),
self.buffer.get_u8_unchecked(15),
])
}
}
pub fn read_destination_address(&self) -> Address {
unsafe {
Address::from([
self.buffer.get_u8_unchecked(16),
self.buffer.get_u8_unchecked(17),
self.buffer.get_u8_unchecked(18),
self.buffer.get_u8_unchecked(19),
])
}
}
pub fn read_options(&self) -> Options {
let ihl = self.read_ihl() as usize;
let len = (ihl * 4).saturating_sub(20);
let mut data = [0u8; 40];
unsafe { self.buffer.read_into_unchecked(20, &mut data[..len]) };
Options::try_from(&data[..len]).unwrap()
}
}
impl<T: BufMut> HeaderMapping<T> {
pub fn write_version_and_ihl(&mut self, version: u8, ihl: u8) {
let val = ((version & 0x0F) << 4) | (ihl & 0x0F);
unsafe { self.buffer.set_u8_unchecked(0, val) }
}
pub fn write_dscp_and_ecn(&mut self, dscp: u8, ecn: u8) {
let val = ((dscp & 0x3F) << 2) | (ecn & 0x03);
unsafe { self.buffer.set_u8_unchecked(1, val) }
}
pub fn write_total_length(&mut self, length: u16) {
unsafe { self.buffer.set_u16_be_unchecked(2, length) }
}
pub fn write_identification(&mut self, id: u16) {
unsafe { self.buffer.set_u16_be_unchecked(4, id) }
}
pub fn write_flags_and_offset(&mut self, df: bool, mf: bool, offset: u16) {
let mut val = offset & 0x1FFF;
if df {
val |= 0x4000;
}
if mf {
val |= 0x2000;
}
unsafe { self.buffer.set_u16_be_unchecked(6, val) }
}
pub fn write_ttl(&mut self, ttl: u8) {
unsafe { self.buffer.set_u8_unchecked(8, ttl) }
}
pub fn write_protocol(&mut self, protocol: Protocol) {
unsafe { self.buffer.set_u8_unchecked(9, u8::from(protocol)) }
}
pub fn write_checksum(&mut self, checksum: u16) {
unsafe { self.buffer.set_u16_be_unchecked(10, checksum) }
}
pub fn write_source_address(&mut self, addr: Address) {
let octets = addr.octets();
unsafe {
self.buffer.set_u8_unchecked(12, octets[0]);
self.buffer.set_u8_unchecked(13, octets[1]);
self.buffer.set_u8_unchecked(14, octets[2]);
self.buffer.set_u8_unchecked(15, octets[3]);
}
}
pub fn write_destination_address(&mut self, addr: Address) {
let octets = addr.octets();
unsafe {
self.buffer.set_u8_unchecked(16, octets[0]);
self.buffer.set_u8_unchecked(17, octets[1]);
self.buffer.set_u8_unchecked(18, octets[2]);
self.buffer.set_u8_unchecked(19, octets[3]);
}
}
pub fn write_options(&mut self, options: &[u8]) {
let ihl = self.read_ihl() as usize;
let len = (ihl * 4).saturating_sub(20);
unsafe {
for i in 0..len {
self.buffer.set_u8_unchecked(20 + i, options[i]);
}
}
}
}
#[cfg(test)]
mod tests {
use super::{Address, HeaderMapping, Protocol};
#[test]
fn read_fields() {
let buffer = [
0x45, 0x00, 0x00, 0x34, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8,
0x00, 0x01, 0xc0, 0xa8, 0x00, 0x02,
];
let mapping = HeaderMapping::new(&buffer[..]);
assert_eq!(mapping.read_version(), 4);
assert_eq!(mapping.read_ihl(), 5);
assert_eq!(mapping.read_total_length(), 52);
assert!(mapping.read_df());
assert!(!mapping.read_mf());
assert_eq!(mapping.read_ttl(), 64);
assert_eq!(mapping.read_protocol(), Protocol::UDP);
assert_eq!(mapping.read_source_address(), Address::new(192, 168, 0, 1));
}
#[test]
fn write_fields() {
let mut buffer = [0u8; 20];
{
let mut mapping = HeaderMapping::new(&mut buffer[..]);
mapping.write_version_and_ihl(4, 5);
mapping.write_total_length(52);
mapping.write_flags_and_offset(true, false, 0);
mapping.write_ttl(64);
mapping.write_protocol(Protocol::UDP);
mapping.write_source_address(Address::new(192, 168, 0, 1));
mapping.write_destination_address(Address::new(192, 168, 0, 2));
}
assert_eq!(&buffer[0..2], &[0x45, 0x00]);
assert_eq!(&buffer[12..16], &[0xc0, 0xa8, 0x00, 0x01]);
}
}