use crate::ietf::icmpv6::{Checksum, Type};
use crate::{Buf, BufError, BufMut, BufResult};
const BASE_HEADER_SIZE: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EchoRequestMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> EchoRequestMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_identifier(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(4) }
}
pub fn read_sequence_number(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(6) }
}
}
impl<T: BufMut> EchoRequestMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_identifier(&mut self, identifier: u16) -> BufResult<()> {
if self.buffer.length() < 6 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(4, identifier) }
Ok(())
}
pub fn write_sequence_number(&mut self, sequence_number: u16) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(6, sequence_number) }
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EchoReplyMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> EchoReplyMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_identifier(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(4) }
}
pub fn read_sequence_number(&self) -> u16 {
unsafe { self.buffer.get_u16_be_unchecked(6) }
}
}
impl<T: BufMut> EchoReplyMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_identifier(&mut self, identifier: u16) -> BufResult<()> {
if self.buffer.length() < 6 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(4, identifier) }
Ok(())
}
pub fn write_sequence_number(&mut self, sequence_number: u16) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(6, sequence_number) }
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DestinationUnreachableMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> DestinationUnreachableMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_unused(&self) -> [u8; 4] {
unsafe { self.buffer.get_array_unchecked::<4>(4) }
}
}
impl<T: BufMut> DestinationUnreachableMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_unused(&mut self) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_array_unchecked::<4>(4, &[0; 4]) }
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PacketTooBigMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> PacketTooBigMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_mtu(&self) -> u32 {
unsafe { self.buffer.get_u32_be_unchecked(4) }
}
}
impl<T: BufMut> PacketTooBigMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_mtu(&mut self, mtu: u32) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u32_be_unchecked(4, mtu) }
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TimeExceededMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> TimeExceededMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_unused(&self) -> [u8; 4] {
unsafe { self.buffer.get_array_unchecked::<4>(4) }
}
}
impl<T: BufMut> TimeExceededMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_unused(&mut self) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_array_unchecked::<4>(4, &[0; 4]) }
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ParameterProblemMapping<T: Buf> {
buffer: T,
}
impl<T: Buf> ParameterProblemMapping<T> {
pub fn new(buffer: T) -> Self {
Self { buffer }
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn as_inner(&self) -> &T {
&self.buffer
}
pub fn read_type(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(0) }
}
pub fn read_code(&self) -> u8 {
unsafe { self.buffer.get_u8_unchecked(1) }
}
pub fn read_checksum(&self) -> Checksum {
unsafe { Checksum(self.buffer.get_u16_be_unchecked(2)) }
}
pub fn read_pointer(&self) -> u32 {
unsafe { self.buffer.get_u32_be_unchecked(4) }
}
}
impl<T: BufMut> ParameterProblemMapping<T> {
pub fn write_type(&mut self, r#type: Type) -> BufResult<()> {
if self.buffer.length() < 1 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(0, r#type as u8) }
Ok(())
}
pub fn write_code(&mut self, code: u8) -> BufResult<()> {
if self.buffer.length() < 2 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u8_unchecked(1, code) }
Ok(())
}
pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
if self.buffer.length() < 4 {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u16_be_unchecked(2, checksum.0) }
Ok(())
}
pub fn write_pointer(&mut self, pointer: u32) -> BufResult<()> {
if self.buffer.length() < BASE_HEADER_SIZE {
return Err(BufError::BufferTooSmall);
}
unsafe { self.buffer.set_u32_be_unchecked(4, pointer) }
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn echo_request_roundtrip() {
let mut buf = [0u8; 8];
let mut m = EchoRequestMapping::new(&mut buf[..]);
m.write_type(Type::EchoRequest).unwrap();
m.write_code(0).unwrap();
m.write_checksum(Checksum(0x1234)).unwrap();
m.write_identifier(0x5678).unwrap();
m.write_sequence_number(0x9ABC).unwrap();
let read = EchoRequestMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x80);
assert_eq!(read.read_code(), 0);
assert_eq!(read.read_checksum(), Checksum(0x1234));
assert_eq!(read.read_identifier(), 0x5678);
assert_eq!(read.read_sequence_number(), 0x9ABC);
}
#[test]
fn echo_reply_roundtrip() {
let mut buf = [0u8; 8];
let mut m = EchoReplyMapping::new(&mut buf[..]);
m.write_type(Type::EchoReply).unwrap();
m.write_code(0).unwrap();
m.write_checksum(Checksum(0xABCD)).unwrap();
m.write_identifier(1).unwrap();
m.write_sequence_number(2).unwrap();
let read = EchoReplyMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x81);
assert_eq!(read.read_checksum(), Checksum(0xABCD));
}
#[test]
fn destination_unreachable_roundtrip() {
let mut buf = [0u8; 8];
let mut m = DestinationUnreachableMapping::new(&mut buf[..]);
m.write_type(Type::DestinationUnreachable).unwrap();
m.write_code(1).unwrap();
m.write_checksum(Checksum(0xBEEF)).unwrap();
m.write_unused().unwrap();
let read = DestinationUnreachableMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x01);
assert_eq!(read.read_code(), 1);
assert_eq!(read.read_checksum(), Checksum(0xBEEF));
assert_eq!(read.read_unused(), [0; 4]);
}
#[test]
fn packet_too_big_roundtrip() {
let mut buf = [0u8; 8];
let mut m = PacketTooBigMapping::new(&mut buf[..]);
m.write_type(Type::PacketTooBig).unwrap();
m.write_code(0).unwrap();
m.write_checksum(Checksum(0xCAFE)).unwrap();
m.write_mtu(1280).unwrap();
let read = PacketTooBigMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x02);
assert_eq!(read.read_mtu(), 1280);
}
#[test]
fn time_exceeded_roundtrip() {
let mut buf = [0u8; 8];
let mut m = TimeExceededMapping::new(&mut buf[..]);
m.write_type(Type::TimeExceeded).unwrap();
m.write_code(0).unwrap();
m.write_checksum(Checksum(0xDEAD)).unwrap();
m.write_unused().unwrap();
let read = TimeExceededMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x03);
assert_eq!(read.read_unused(), [0; 4]);
}
#[test]
fn parameter_problem_roundtrip() {
let mut buf = [0u8; 8];
let mut m = ParameterProblemMapping::new(&mut buf[..]);
m.write_type(Type::ParameterProblem).unwrap();
m.write_code(2).unwrap();
m.write_checksum(Checksum(0xBABE)).unwrap();
m.write_pointer(42).unwrap();
let read = ParameterProblemMapping::new(&buf[..]);
assert_eq!(read.read_type(), 0x04);
assert_eq!(read.read_code(), 2);
assert_eq!(read.read_checksum(), Checksum(0xBABE));
assert_eq!(read.read_pointer(), 42);
}
#[test]
fn buffer_too_small() {
let mut buf = [0u8; 2];
let mut m = EchoRequestMapping::new(&mut buf[..]);
assert!(m.write_checksum(Checksum(0)).is_err());
}
}