internet 0.1.0

Network library for rust
Documentation
//! IPv6 Header and Extension Headers Mapping.
//!
//! Provides zero-copy read and write access to IPv6 header fields.
//!
//! As defined in [RFC 8200].
//!
//! [IETF RFC 8200]: https://datatracker.ietf.org/doc/html/rfc8200

use crate::ietf::ip::Protocol;
use crate::ietf::ipv6::Address;
use crate::{Buf, BufError, BufMut, BufResult};

/// A zero-copy mapping for an IPv6 header.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HeaderMapping<T> {
    buffer: T,
}

impl<T> HeaderMapping<T> {
    /// Creates a new header mapping.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }

    /// Consumes the mapping and returns the underlying buffer.
    pub fn into_inner(self) -> T {
        self.buffer
    }
}

impl<T: Buf> HeaderMapping<T> {
    /// Reads the IP version.
    pub fn read_version(&self) -> u8 {
        unsafe { (self.buffer.get_u32_be_unchecked(0) >> 28) as u8 }
    }

    /// Reads the traffic class.
    pub fn read_traffic_class(&self) -> u8 {
        unsafe { ((self.buffer.get_u32_be_unchecked(0) >> 20) & 0xFF) as u8 }
    }

    /// Reads the flow label.
    pub fn read_flow_label(&self) -> u32 {
        unsafe { self.buffer.get_u32_be_unchecked(0) & 0x000F_FFFF }
    }

    /// Reads the payload length.
    pub fn read_payload_length(&self) -> u16 {
        unsafe { self.buffer.get_u16_be_unchecked(4) }
    }

    /// Reads the next header.
    pub fn read_next_header(&self) -> Protocol {
        unsafe { Protocol::from(self.buffer.get_u8_unchecked(6)) }
    }

    /// Reads the hop limit.
    pub fn read_hop_limit(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(7) }
    }

    /// Reads the source address.
    pub fn read_source_address(&self) -> Address {
        unsafe {
            let mut octets = [0u8; 16];
            for i in 0..16 {
                octets[i] = self.buffer.get_u8_unchecked(8 + i);
            }
            Address::from(octets)
        }
    }

    /// Reads the destination address.
    pub fn read_destination_address(&self) -> Address {
        unsafe {
            let mut octets = [0u8; 16];
            for i in 0..16 {
                octets[i] = self.buffer.get_u8_unchecked(24 + i);
            }
            Address::from(octets)
        }
    }
}

impl<T: BufMut> HeaderMapping<T> {
    /// Writes the version, traffic class, and flow label.
    pub fn write_version_tc_fl(&mut self, version: u8, tc: u8, fl: u32) -> BufResult<()> {
        if self.buffer.length() < 4 {
            return Err(BufError::UnexpectedEof);
        }
        let val = ((version as u32 & 0x0F) << 28) | ((tc as u32 & 0xFF) << 20) | (fl & 0x000F_FFFF);
        unsafe { self.buffer.set_u32_be_unchecked(0, val) }
        Ok(())
    }

    /// Writes the payload length.
    pub fn write_payload_length(&mut self, length: u16) -> BufResult<()> {
        if self.buffer.length() < 6 {
            return Err(BufError::UnexpectedEof);
        }
        unsafe { self.buffer.set_u16_be_unchecked(4, length) }
        Ok(())
    }

    /// Writes the next header.
    pub fn write_next_header(&mut self, protocol: Protocol) -> BufResult<()> {
        if self.buffer.length() < 7 {
            return Err(BufError::UnexpectedEof);
        }
        unsafe { self.buffer.set_u8_unchecked(6, u8::from(protocol)) }
        Ok(())
    }

    /// Writes the hop limit.
    pub fn write_hop_limit(&mut self, limit: u8) -> BufResult<()> {
        if self.buffer.length() < 8 {
            return Err(BufError::UnexpectedEof);
        }
        unsafe { self.buffer.set_u8_unchecked(7, limit) }
        Ok(())
    }

    /// Writes the source address.
    pub fn write_source_address(&mut self, addr: Address) -> BufResult<()> {
        if self.buffer.length() < 24 {
            return Err(BufError::UnexpectedEof);
        }
        let octets = addr.octets();
        unsafe {
            for i in 0..16 {
                self.buffer.set_u8_unchecked(8 + i, octets[i]);
            }
        }
        Ok(())
    }

    /// Writes the destination address.
    pub fn write_destination_address(&mut self, addr: Address) -> BufResult<()> {
        if self.buffer.length() < 40 {
            return Err(BufError::UnexpectedEof);
        }
        let octets = addr.octets();
        unsafe {
            for i in 0..16 {
                self.buffer.set_u8_unchecked(24 + i, octets[i]);
            }
        }
        Ok(())
    }
}

/// A zero-copy mapping for a Hop-by-Hop Options header.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HopByHopOptionsMapping<T> {
    buffer: T,
}

impl<T> HopByHopOptionsMapping<T> {
    /// Creates a new mapping at the given buffer.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }
}

impl<T: Buf> HopByHopOptionsMapping<T> {
    /// Reads the next header.
    pub fn read_next_header(&self) -> Protocol {
        unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
    }

    /// Reads the header extension length.
    pub fn read_hdr_ext_len(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(1) }
    }
}

/// A zero-copy mapping for a Routing header.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RoutingHeaderMapping<T> {
    buffer: T,
}

impl<T> RoutingHeaderMapping<T> {
    /// Creates a new mapping at the given buffer.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }
}

impl<T: Buf> RoutingHeaderMapping<T> {
    /// Reads the next header.
    pub fn read_next_header(&self) -> Protocol {
        unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
    }

    /// Reads the header extension length.
    pub fn read_hdr_ext_len(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(1) }
    }

    /// Reads the routing type.
    pub fn read_routing_type(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(2) }
    }

    /// Reads the segments left.
    pub fn read_segments_left(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(3) }
    }
}

/// A zero-copy mapping for a Fragment header.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FragmentHeaderMapping<T> {
    buffer: T,
}

impl<T> FragmentHeaderMapping<T> {
    /// Creates a new mapping at the given buffer.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }
}

impl<T: Buf> FragmentHeaderMapping<T> {
    /// Reads the next header.
    pub fn read_next_header(&self) -> Protocol {
        unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
    }

    /// Reads the reserved field.
    pub fn read_reserved(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(1) }
    }

    /// Reads the fragment offset.
    pub fn read_fragment_offset(&self) -> u16 {
        unsafe { self.buffer.get_u16_be_unchecked(2) >> 3 }
    }

    /// Reads the More Fragments flag.
    pub fn read_m(&self) -> bool {
        unsafe { (self.buffer.get_u16_be_unchecked(2) & 0x01) != 0 }
    }

    /// Reads the identification.
    pub fn read_identification(&self) -> u32 {
        unsafe { self.buffer.get_u32_be_unchecked(4) }
    }
}

/// A zero-copy mapping for a Destination Options header.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DestinationOptionsMapping<T> {
    buffer: T,
}

impl<T> DestinationOptionsMapping<T> {
    /// Creates a new mapping at the given buffer.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }
}

impl<T: Buf> DestinationOptionsMapping<T> {
    /// Reads the next header.
    pub fn read_next_header(&self) -> Protocol {
        unsafe { Protocol::from(self.buffer.get_u8_unchecked(0)) }
    }

    /// Reads the header extension length.
    pub fn read_hdr_ext_len(&self) -> u8 {
        unsafe { self.buffer.get_u8_unchecked(1) }
    }
}

#[cfg(test)]
mod tests {
    use super::{Address, HeaderMapping, Protocol};

    #[test]
    fn read_ipv6_fields() {
        let buffer = [
            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 mapping = HeaderMapping::new(&buffer[..]);
        assert_eq!(mapping.read_version(), 6);
        assert_eq!(mapping.read_traffic_class(), 0);
        assert_eq!(mapping.read_flow_label(), 0);
        assert_eq!(mapping.read_payload_length(), 32);
        assert_eq!(mapping.read_next_header(), Protocol::TCP);
        assert_eq!(mapping.read_hop_limit(), 64);
        assert_eq!(mapping.read_source_address().octets()[0], 0x20);
        assert_eq!(mapping.read_destination_address().octets()[15], 0x02);
    }

    #[test]
    fn write_ipv6_fields() {
        let mut buffer = [0u8; 40];
        {
            let mut mapping = HeaderMapping::new(&mut buffer[..]);
            mapping.write_version_tc_fl(6, 0, 0).unwrap();
            mapping.write_payload_length(32).unwrap();
            mapping.write_next_header(Protocol::TCP).unwrap();
            mapping.write_hop_limit(64).unwrap();
            mapping
                .write_source_address(Address::from([
                    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x01,
                ]))
                .unwrap();
            mapping
                .write_destination_address(Address::from([
                    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x02,
                ]))
                .unwrap();
        }
        assert_eq!(&buffer[0..4], &[0x60, 0x00, 0x00, 0x00]);
        assert_eq!(buffer[6], 0x06);
    }
}