use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Message {
DestinationUnreachable(DestinationUnreachable),
PacketTooBig(PacketTooBig),
TimeExceeded(TimeExceeded),
ParameterProblem(ParameterProblem),
EchoRequest(EchoRequest),
EchoReply(EchoReply),
}
impl Codec for Message {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
match self {
Message::DestinationUnreachable(message) => message.encode(writer, ()),
Message::PacketTooBig(message) => message.encode(writer, ()),
Message::TimeExceeded(message) => message.encode(writer, ()),
Message::ParameterProblem(message) => message.encode(writer, ()),
Message::EchoRequest(message) => message.encode(writer, ()),
Message::EchoReply(message) => message.encode(writer, ()),
}
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let r#type = Type::decode(&mut Cursor::new(&[(reader.peek_u8()?)]), ())?;
Ok(match r#type {
Type::DestinationUnreachable => {
Self::DestinationUnreachable(DestinationUnreachable::decode(reader, ())?)
}
Type::PacketTooBig => Self::PacketTooBig(PacketTooBig::decode(reader, ())?),
Type::TimeExceeded => Self::TimeExceeded(TimeExceeded::decode(reader, ())?),
Type::ParameterProblem => Self::ParameterProblem(ParameterProblem::decode(reader, ())?),
Type::EchoRequest => Self::EchoRequest(EchoRequest::decode(reader, ())?),
Type::EchoReply => Self::EchoReply(EchoReply::decode(reader, ())?),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Type {
DestinationUnreachable = 1,
PacketTooBig = 2,
TimeExceeded = 3,
ParameterProblem = 4,
EchoRequest = 128,
EchoReply = 129,
}
impl Codec for Type {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
x if x == (Self::DestinationUnreachable as u8) => Ok(Self::DestinationUnreachable),
x if x == (Self::PacketTooBig as u8) => Ok(Self::PacketTooBig),
x if x == (Self::TimeExceeded as u8) => Ok(Self::TimeExceeded),
x if x == (Self::ParameterProblem as u8) => Ok(Self::ParameterProblem),
x if x == (Self::EchoRequest as u8) => Ok(Self::EchoRequest),
x if x == (Self::EchoReply as u8) => Ok(Self::EchoReply),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum NoCode {
Zero = 0,
}
impl Codec for NoCode {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
x if x == Self::Zero as u8 => Ok(Self::Zero),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Checksum(pub u16);
impl Checksum {
pub fn calculate(data: &[u8]) -> Self {
let mut sum: u32 = 0;
let mut i = 0;
while i + 1 < data.len() {
sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
i += 2;
}
if i < data.len() {
sum += (data[i] as u32) << 8;
}
while sum >> 16 != 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
Self(!(sum as u16))
}
}
impl Codec for Checksum {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
self.0.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
Ok(Self(u16::decode(reader, ())?))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DestinationUnreachable {
pub code: DestinationUnreachableCode,
pub checksum: Checksum,
pub data: Vec<u8>,
}
impl DestinationUnreachable {
pub const TYPE: Type = Type::DestinationUnreachable;
pub const UNUSED: [u8; 4] = [0u8; 4];
}
impl Codec for DestinationUnreachable {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::DestinationUnreachable => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = DestinationUnreachableCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
match reader.read_array::<4>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum DestinationUnreachableCode {
NoRouteToDestination = 0,
CommunicationAdministrativelyProhibited = 1,
BeyondScopeOfSourceAddress = 2,
AddressUnreachable = 3,
PortUnreachable = 4,
SourceAddressFailedIngressEgressPolicy = 5,
RejectRouteToDestination = 6,
}
impl Codec for DestinationUnreachableCode {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
0 => Ok(Self::NoRouteToDestination),
1 => Ok(Self::CommunicationAdministrativelyProhibited),
2 => Ok(Self::BeyondScopeOfSourceAddress),
3 => Ok(Self::AddressUnreachable),
4 => Ok(Self::PortUnreachable),
5 => Ok(Self::SourceAddressFailedIngressEgressPolicy),
6 => Ok(Self::RejectRouteToDestination),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PacketTooBig {
pub code: PacketTooBigCode,
pub checksum: Checksum,
pub mtu: u32,
pub data: Vec<u8>,
}
impl PacketTooBig {
pub const TYPE: Type = Type::PacketTooBig;
}
impl Codec for PacketTooBig {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.mtu.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::PacketTooBig => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = PacketTooBigCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let mtu = u32::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
mtu,
data,
})
}
}
pub type PacketTooBigCode = NoCode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TimeExceeded {
pub code: TimeExceededCode,
pub checksum: Checksum,
pub data: Vec<u8>,
}
impl TimeExceeded {
pub const TYPE: Type = Type::TimeExceeded;
pub const UNUSED: [u8; 4] = [0u8; 4];
}
impl Codec for TimeExceeded {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
Self::UNUSED.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::TimeExceeded => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = TimeExceededCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
match reader.read_array::<4>()? {
Self::UNUSED => (),
_ => return Err(BufError::UnexpectedValue),
}
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum TimeExceededCode {
HopLimitExceededInTransit = 0,
FragmentReassemblyTimeExceeded = 1,
}
impl Codec for TimeExceededCode {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
0 => Ok(Self::HopLimitExceededInTransit),
1 => Ok(Self::FragmentReassemblyTimeExceeded),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParameterProblem {
pub code: ParameterProblemCode,
pub checksum: Checksum,
pub pointer: u32,
pub data: Vec<u8>,
}
impl ParameterProblem {
pub const TYPE: Type = Type::ParameterProblem;
}
impl Codec for ParameterProblem {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.pointer.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Type::ParameterProblem => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = ParameterProblemCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let pointer = u32::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
pointer,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum ParameterProblemCode {
ErroneousHeaderField = 0,
UnrecognizedNextHeaderType = 1,
UnrecognizedIpv6Option = 2,
}
impl Codec for ParameterProblemCode {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
0 => Ok(Self::ErroneousHeaderField),
1 => Ok(Self::UnrecognizedNextHeaderType),
2 => Ok(Self::UnrecognizedIpv6Option),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EchoRequest {
pub code: EchoRequestCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub data: Vec<u8>,
}
impl EchoRequest {
pub const TYPE: Type = Type::EchoRequest;
}
impl Codec for EchoRequest {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Self::TYPE => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = EchoRequestCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
data,
})
}
}
pub type EchoRequestCode = NoCode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EchoReply {
pub code: EchoReplyCode,
pub checksum: Checksum,
pub identifier: u16,
pub sequence_number: u16,
pub data: Vec<u8>,
}
impl EchoReply {
pub const TYPE: Type = Type::EchoReply;
}
impl Codec for EchoReply {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
self.code.encode(writer, ())?;
self.checksum.encode(writer, ())?;
self.identifier.encode(writer, ())?;
self.sequence_number.encode(writer, ())?;
self.data.encode(writer, ())?;
Ok(())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match Type::decode(reader, ())? {
Self::TYPE => (),
_ => return Err(BufError::UnexpectedValue),
}
let code = EchoReplyCode::decode(reader, ())?;
let checksum = Checksum::decode(reader, ())?;
let identifier = u16::decode(reader, ())?;
let sequence_number = u16::decode(reader, ())?;
let data = Vec::decode(reader, ())?;
Ok(Self {
code,
checksum,
identifier,
sequence_number,
data,
})
}
}
pub type EchoReplyCode = NoCode;
#[cfg(test)]
mod tests {
use core::fmt::Debug;
use crate::{
Codec, Cursor,
ietf::icmpv6::{
Checksum, DestinationUnreachable, DestinationUnreachableCode, EchoReply, EchoReplyCode,
EchoRequest, EchoRequestCode, NoCode, PacketTooBig, PacketTooBigCode, ParameterProblem,
ParameterProblemCode, TimeExceeded, TimeExceededCode, Type,
},
};
fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
etalon_struct: T,
etalon_bytes: &[u8],
context: C,
) {
let mut encoded_bytes = vec![];
{
let writer = &mut Cursor::new(&mut encoded_bytes);
etalon_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
let decoded_struct = {
let reader = &mut Cursor::new(&mut encoded_bytes);
T::decode(reader, context).unwrap()
};
assert_eq!(etalon_struct, decoded_struct);
encoded_bytes.fill(0x00);
{
let writer = &mut Cursor::new(&mut encoded_bytes);
decoded_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
}
#[test]
fn checksum() {
let packet = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02];
let checksum = Checksum::calculate(packet);
let mut with_checksum = vec![0x80, 0x00];
with_checksum.extend_from_slice(&checksum.0.to_be_bytes());
with_checksum.extend_from_slice(&[0x00, 0x01, 0x00, 0x02]);
let verify = Checksum::calculate(&with_checksum);
assert!(verify.0 == 0x0000 || verify.0 == 0xFFFF);
}
#[test]
fn type_roundtrip() {
let cases = [
(Type::DestinationUnreachable, &[0x01][..]),
(Type::PacketTooBig, &[0x02][..]),
(Type::TimeExceeded, &[0x03][..]),
(Type::ParameterProblem, &[0x04][..]),
(Type::EchoRequest, &[0x80][..]),
(Type::EchoReply, &[0x81][..]),
];
for (t, bytes) in &cases {
codec_roundtrip(*t, bytes, ());
}
}
#[test]
fn no_code() {
codec_roundtrip(NoCode::Zero, &[0x00], ());
}
#[test]
fn destination_unreachable() {
let etalon_bytes = &[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, ];
let etalon_struct = DestinationUnreachable {
code: DestinationUnreachableCode::NoRouteToDestination,
checksum: Checksum(0x0000),
data: vec![0xde, 0xad, 0xbe, 0xef],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn packet_too_big() {
let etalon_bytes = &[
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xca, 0xfe, 0xba, 0xbe, ];
let etalon_struct = PacketTooBig {
code: PacketTooBigCode::Zero,
checksum: Checksum(0x0000),
mtu: 1280,
data: vec![0xca, 0xfe, 0xba, 0xbe],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn time_exceeded() {
let etalon_bytes = &[
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, ];
let etalon_struct = TimeExceeded {
code: TimeExceededCode::HopLimitExceededInTransit,
checksum: Checksum(0x0000),
data: vec![0x11, 0x22, 0x33, 0x44],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn parameter_problem() {
let etalon_bytes = &[
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 0x66, 0x77, 0x88, ];
let etalon_struct = ParameterProblem {
code: ParameterProblemCode::ErroneousHeaderField,
checksum: Checksum(0x0000),
pointer: 5,
data: vec![0x55, 0x66, 0x77, 0x88],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo_request() {
let etalon_bytes = &[
0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x41, 0x42, 0x43, ];
let etalon_struct = EchoRequest {
code: EchoRequestCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
data: vec![0x41, 0x42, 0x43],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn echo_reply() {
let etalon_bytes = &[
0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x41, 0x42, 0x43, ];
let etalon_struct = EchoReply {
code: EchoReplyCode::Zero,
checksum: Checksum(0x0000),
identifier: 1,
sequence_number: 2,
data: vec![0x41, 0x42, 0x43],
};
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
}