use core::mem;
use core::net;
use num_traits::FromPrimitive as _;
use crate::getter_be;
use crate::setter_be;
#[derive(Debug, Copy, Clone)]
pub enum Icmp {
V4(Icmpv4Hdr),
V6(Icmpv6Hdr),
}
#[derive(Debug)]
pub enum IcmpError {
InvalidIcmpType(u8),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv4Hdr {
pub type_: u8,
pub code: u8,
pub check: [u8; 2],
pub data: [u8; 4],
}
#[derive(Clone, Copy, num_derive::FromPrimitive, num_derive::ToPrimitive)]
#[repr(u8)]
pub enum Icmpv4Type {
EchoReply = 0,
DestinationUnreachable = 3,
Redirect = 5,
Echo = 8,
ParameterProblem = 12,
Timestamp = 13,
TimestampReply = 14,
InformationRequest = 15,
InformationReply = 16,
AddressMaskRequest = 17,
AddressMaskReply = 18,
Traceroute = 30,
DomainNameRequest = 37,
DomainNameReply = 38,
Photuris = 40,
}
#[derive(Debug, Copy, Clone)]
pub enum Icmpv4HdrData<'a> {
EchoReply(&'a IcmpIdSequence),
DestinationUnreachable(&'a IcmpDstUnreachable),
Redirect(&'a Icmpv4Redirect),
Echo(&'a IcmpIdSequence),
ParameterProblem(&'a Icmpv4ParamProblem),
Timestamp(&'a IcmpIdSequence),
TimestampReply(&'a IcmpIdSequence),
InformationRequest(&'a IcmpIdSequence),
InformationReply(&'a IcmpIdSequence),
AddressMaskRequest(&'a IcmpIdSequence),
AddressMaskReply(&'a IcmpIdSequence),
Traceroute(&'a IcmpTraceroute),
DomainNameRequest(&'a IcmpIdSequence),
DomainNameReply(&'a IcmpIdSequence),
Photuris(&'a IcmpHdrPhoturis),
}
#[derive(Debug)]
pub enum Icmpv4HdrDataMut<'a> {
EchoReply(&'a mut IcmpIdSequence),
DestinationUnreachable(&'a mut IcmpDstUnreachable),
Redirect(&'a mut Icmpv4Redirect),
Echo(&'a mut IcmpIdSequence),
ParameterProblem(&'a mut Icmpv4ParamProblem),
Timestamp(&'a mut IcmpIdSequence),
TimestampReply(&'a mut IcmpIdSequence),
InformationRequest(&'a mut IcmpIdSequence),
InformationReply(&'a mut IcmpIdSequence),
AddressMaskRequest(&'a mut IcmpIdSequence),
AddressMaskReply(&'a mut IcmpIdSequence),
Traceroute(&'a mut IcmpTraceroute),
DomainNameRequest(&'a mut IcmpIdSequence),
DomainNameReply(&'a mut IcmpIdSequence),
Photuris(&'a mut IcmpHdrPhoturis),
}
impl Icmpv4Hdr {
pub const LEN: usize = mem::size_of::<Icmpv4Hdr>();
#[inline]
pub fn icmp_type(&self) -> Result<Icmpv4Type, IcmpError> {
Icmpv4Type::from_u8(self.type_).ok_or(IcmpError::InvalidIcmpType(self.type_))
}
pub fn checksum(&self) -> u16 {
unsafe { getter_be!(self, check, u16) }
}
pub fn set_checksum(&mut self, checksum: u16) {
unsafe { setter_be!(self, check, checksum) }
}
#[inline]
pub fn data(&self) -> Result<Icmpv4HdrData<'_>, IcmpError> {
match self.icmp_type()? {
Icmpv4Type::EchoReply => Ok(Icmpv4HdrData::EchoReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::DestinationUnreachable => {
Ok(Icmpv4HdrData::DestinationUnreachable(unsafe {
self.destination_unreachable_unchecked()
}))
}
Icmpv4Type::Redirect => Ok(Icmpv4HdrData::Redirect(unsafe {
self.redirect_unchecked()
})),
Icmpv4Type::Echo => Ok(Icmpv4HdrData::Echo(unsafe { self.id_sequence_unchecked() })),
Icmpv4Type::ParameterProblem => Ok(Icmpv4HdrData::ParameterProblem(unsafe {
self.parameter_problem_unchecked()
})),
Icmpv4Type::Timestamp => Ok(Icmpv4HdrData::Timestamp(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::TimestampReply => Ok(Icmpv4HdrData::TimestampReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::InformationRequest => Ok(Icmpv4HdrData::InformationRequest(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::InformationReply => Ok(Icmpv4HdrData::InformationReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::AddressMaskRequest => Ok(Icmpv4HdrData::AddressMaskRequest(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::AddressMaskReply => Ok(Icmpv4HdrData::AddressMaskReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::Traceroute => Ok(Icmpv4HdrData::Traceroute(unsafe {
self.traceroute_unchecked()
})),
Icmpv4Type::DomainNameRequest => Ok(Icmpv4HdrData::DomainNameRequest(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::DomainNameReply => Ok(Icmpv4HdrData::DomainNameReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv4Type::Photuris => Ok(Icmpv4HdrData::Photuris(unsafe {
self.photuris_unchecked()
})),
}
}
#[inline]
pub fn data_mut(&mut self) -> Result<Icmpv4HdrDataMut<'_>, IcmpError> {
match self.icmp_type()? {
Icmpv4Type::EchoReply => Ok(Icmpv4HdrDataMut::EchoReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::DestinationUnreachable => {
Ok(Icmpv4HdrDataMut::DestinationUnreachable(unsafe {
self.destination_unreachable_mut_unchecked()
}))
}
Icmpv4Type::Redirect => Ok(Icmpv4HdrDataMut::Redirect(unsafe {
self.redirect_mut_unchecked()
})),
Icmpv4Type::Echo => Ok(Icmpv4HdrDataMut::Echo(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::ParameterProblem => Ok(Icmpv4HdrDataMut::ParameterProblem(unsafe {
self.parameter_problem_mut_unchecked()
})),
Icmpv4Type::Timestamp => Ok(Icmpv4HdrDataMut::Timestamp(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::TimestampReply => Ok(Icmpv4HdrDataMut::TimestampReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::InformationRequest => Ok(Icmpv4HdrDataMut::InformationRequest(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::InformationReply => Ok(Icmpv4HdrDataMut::InformationReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::AddressMaskRequest => Ok(Icmpv4HdrDataMut::AddressMaskRequest(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::AddressMaskReply => Ok(Icmpv4HdrDataMut::AddressMaskReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::Traceroute => Ok(Icmpv4HdrDataMut::Traceroute(unsafe {
self.traceroute_mut_unchecked()
})),
Icmpv4Type::DomainNameRequest => Ok(Icmpv4HdrDataMut::DomainNameRequest(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::DomainNameReply => Ok(Icmpv4HdrDataMut::DomainNameReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv4Type::Photuris => Ok(Icmpv4HdrDataMut::Photuris(unsafe {
self.photuris_mut_unchecked()
})),
}
}
}
impl Icmpv4Hdr {
#[inline]
pub unsafe fn id_sequence_unchecked(&self) -> &IcmpIdSequence {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn id_sequence_mut_unchecked(&mut self) -> &mut IcmpIdSequence {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn redirect_unchecked(&self) -> &Icmpv4Redirect {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn redirect_mut_unchecked(&mut self) -> &mut Icmpv4Redirect {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn destination_unreachable_unchecked(&self) -> &IcmpDstUnreachable {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn destination_unreachable_mut_unchecked(&mut self) -> &mut IcmpDstUnreachable {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn parameter_problem_unchecked(&self) -> &Icmpv4ParamProblem {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn parameter_problem_mut_unchecked(&mut self) -> &mut Icmpv4ParamProblem {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn traceroute_unchecked(&self) -> &IcmpTraceroute {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn traceroute_mut_unchecked(&mut self) -> &mut IcmpTraceroute {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn photuris_unchecked(&self) -> &IcmpHdrPhoturis {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn photuris_mut_unchecked(&mut self) -> &mut IcmpHdrPhoturis {
mem::transmute(&mut self.data)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpIdSequence {
pub id: [u8; 2],
pub sequence: [u8; 2],
}
impl IcmpIdSequence {
#[inline]
pub fn id(&self) -> u16 {
unsafe { getter_be!(self, id, u16) }
}
#[inline]
pub fn set_id(&mut self, id: u16) {
unsafe { setter_be!(self, id, id) }
}
#[inline]
pub fn sequence(&self) -> u16 {
unsafe { getter_be!(self, sequence, u16) }
}
#[inline]
pub fn set_sequence(&mut self, sequence: u16) {
unsafe { setter_be!(self, sequence, sequence) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv4Redirect {
gateway: [u8; 4],
}
impl Icmpv4Redirect {
#[inline]
pub fn gateway_address(&self) -> net::Ipv4Addr {
net::Ipv4Addr::from(self.gateway)
}
#[inline]
pub fn set_gateway_address(&mut self, addr: net::Ipv4Addr) {
self.gateway = addr.octets();
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpDstUnreachable {
pub _unused: [u8; 2],
pub mtu: [u8; 2],
}
impl IcmpDstUnreachable {
#[inline]
pub fn mtu(&self) -> u16 {
unsafe { getter_be!(self, mtu, u16) }
}
#[inline]
pub fn set_mtu(&mut self, mtu: u16) {
unsafe { setter_be!(self, mtu, mtu) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv4ParamProblem {
pub pointer: u8,
pub _unused: [u8; 3], }
impl Icmpv4ParamProblem {
#[inline]
pub fn pointer(&self) -> u8 {
self.pointer
}
#[inline]
pub fn set_pointer(&mut self, pointer: u8) {
self.pointer = pointer;
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpHdrPhoturis {
pub reserved_spi: [u8; 2],
pub pointer: [u8; 2],
}
impl IcmpHdrPhoturis {
#[inline]
pub fn reserved_spi(&self) -> u16 {
unsafe { getter_be!(self, reserved_spi, u16) }
}
#[inline]
pub fn set_reserved_spi(&mut self, spi: u16) {
unsafe { setter_be!(self, reserved_spi, spi) }
}
#[inline]
pub fn pointer(&self) -> u16 {
unsafe { getter_be!(self, pointer, u16) }
}
#[inline]
pub fn set_pointer(&mut self, pointer: u16) {
unsafe { setter_be!(self, pointer, pointer) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpTraceroute {
pub id: [u8; 2],
pub _unused: [u8; 2],
}
impl IcmpTraceroute {
#[inline]
pub fn id(&self) -> u16 {
unsafe { getter_be!(self, id, u16) }
}
#[inline]
pub fn set_id(&mut self, id: u16) {
unsafe { setter_be!(self, id, id) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpTimestampMsgPart {
pub originate_timestamp: [u8; 4],
pub receive_timestamp: [u8; 4],
pub transmit_timestamp: [u8; 4],
}
impl IcmpTimestampMsgPart {
pub const LEN: usize = mem::size_of::<IcmpTimestampMsgPart>();
pub fn originate_timestamp(&self) -> u32 {
unsafe { getter_be!(self, originate_timestamp, u32) }
}
pub fn set_originate_timestamp(&mut self, timestamp: u32) {
unsafe { setter_be!(self, originate_timestamp, timestamp) }
}
pub fn receive_timestamp(&self) -> u32 {
unsafe { getter_be!(self, receive_timestamp, u32) }
}
pub fn set_receive_timestamp(&mut self, timestamp: u32) {
unsafe { setter_be!(self, receive_timestamp, timestamp) }
}
pub fn transmit_timestamp(&self) -> u32 {
unsafe { getter_be!(self, transmit_timestamp, u32) }
}
pub fn set_transmit_timestamp(&mut self, timestamp: u32) {
unsafe { setter_be!(self, transmit_timestamp, timestamp) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpTracerouteMsgPart {
pub hops_out: [u8; 2],
pub hops_in: [u8; 2],
pub bandwidth_out: [u8; 4],
pub mtu_out: [u8; 4],
}
impl IcmpTracerouteMsgPart {
pub const LEN: usize = mem::size_of::<IcmpTracerouteMsgPart>();
pub fn hops_out(&self) -> u16 {
unsafe { getter_be!(self, hops_out, u16) }
}
pub fn set_hops_out(&mut self, hops: u16) {
unsafe { setter_be!(self, hops_out, hops) }
}
pub fn hops_in(&self) -> u16 {
unsafe { getter_be!(self, hops_in, u16) }
}
pub fn set_hops_in(&mut self, hops: u16) {
unsafe { setter_be!(self, hops_in, hops) }
}
pub fn bandwidth_out(&self) -> u32 {
unsafe { getter_be!(self, bandwidth_out, u32) }
}
pub fn set_bandwidth_out(&mut self, bandwidth: u32) {
unsafe { setter_be!(self, bandwidth_out, bandwidth) }
}
pub fn mtu_out(&self) -> u32 {
unsafe { getter_be!(self, mtu_out, u32) }
}
pub fn set_mtu_out(&mut self, mtu: u32) {
unsafe { setter_be!(self, mtu_out, mtu) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv6Hdr {
pub type_: u8,
pub code: u8,
pub check: [u8; 2],
pub data: [u8; 4],
}
#[derive(Clone, Copy, num_derive::FromPrimitive, num_derive::ToPrimitive)]
#[repr(u8)]
pub enum Icmpv6Type {
PacketTooBig = 2,
ParameterProblem = 4,
EchoRequest = 128,
EchoReply = 129,
RedirectMessage = 137,
}
#[derive(Debug)]
pub enum Icmpv6HdrData<'a> {
PacketTooBig(&'a IcmpPacketTooBig),
ParameterProblem(&'a Icmpv6ParamProblem),
EchoRequest(&'a IcmpIdSequence),
EchoReply(&'a IcmpIdSequence),
RedirectMessage(&'a Icmpv6Redirect),
}
#[derive(Debug)]
pub enum Icmpv6HdrDataMut<'a> {
PacketTooBig(&'a mut IcmpPacketTooBig),
ParameterProblem(&'a mut Icmpv6ParamProblem),
EchoRequest(&'a mut IcmpIdSequence),
EchoReply(&'a mut IcmpIdSequence),
RedirectMessage(&'a mut Icmpv6Redirect),
}
impl Icmpv6Hdr {
pub const LEN: usize = mem::size_of::<Icmpv6Hdr>();
#[inline]
pub fn icmp_type(&self) -> Result<Icmpv6Type, IcmpError> {
Icmpv6Type::from_u8(self.type_).ok_or(IcmpError::InvalidIcmpType(self.type_))
}
pub fn checksum(&self) -> u16 {
unsafe { getter_be!(self, check, u16) }
}
pub fn set_checksum(&mut self, checksum: u16) {
unsafe { setter_be!(self, check, checksum) }
}
#[inline]
pub fn data(&self) -> Result<Icmpv6HdrData<'_>, IcmpError> {
match self.icmp_type()? {
Icmpv6Type::PacketTooBig => Ok(Icmpv6HdrData::PacketTooBig(unsafe {
self.packet_too_big_unchecked()
})),
Icmpv6Type::ParameterProblem => Ok(Icmpv6HdrData::ParameterProblem(unsafe {
self.parameter_problem_unchecked()
})),
Icmpv6Type::EchoRequest => Ok(Icmpv6HdrData::EchoRequest(unsafe {
self.id_sequence_unchecked()
})),
Icmpv6Type::EchoReply => Ok(Icmpv6HdrData::EchoReply(unsafe {
self.id_sequence_unchecked()
})),
Icmpv6Type::RedirectMessage => Ok(Icmpv6HdrData::RedirectMessage(unsafe {
self.redirect_unchecked()
})),
}
}
#[inline]
pub fn data_mut(&mut self) -> Result<Icmpv6HdrDataMut<'_>, IcmpError> {
match self.icmp_type()? {
Icmpv6Type::PacketTooBig => Ok(Icmpv6HdrDataMut::PacketTooBig(unsafe {
self.packet_too_big_mut_unchecked()
})),
Icmpv6Type::ParameterProblem => Ok(Icmpv6HdrDataMut::ParameterProblem(unsafe {
self.parameter_problem_mut_unchecked()
})),
Icmpv6Type::EchoRequest => Ok(Icmpv6HdrDataMut::EchoRequest(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv6Type::EchoReply => Ok(Icmpv6HdrDataMut::EchoReply(unsafe {
self.id_sequence_mut_unchecked()
})),
Icmpv6Type::RedirectMessage => Ok(Icmpv6HdrDataMut::RedirectMessage(unsafe {
self.redirect_mut_unchecked()
})),
}
}
}
impl Icmpv6Hdr {
#[inline]
pub unsafe fn id_sequence_unchecked(&self) -> &IcmpIdSequence {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn id_sequence_mut_unchecked(&mut self) -> &mut IcmpIdSequence {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn packet_too_big_unchecked(&self) -> &IcmpPacketTooBig {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn packet_too_big_mut_unchecked(&mut self) -> &mut IcmpPacketTooBig {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn parameter_problem_unchecked(&self) -> &Icmpv6ParamProblem {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn parameter_problem_mut_unchecked(&mut self) -> &mut Icmpv6ParamProblem {
mem::transmute(&mut self.data)
}
#[inline]
pub unsafe fn redirect_unchecked(&self) -> &Icmpv6Redirect {
mem::transmute(&self.data)
}
#[inline]
pub unsafe fn redirect_mut_unchecked(&mut self) -> &mut Icmpv6Redirect {
mem::transmute(&mut self.data)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct IcmpPacketTooBig {
mtu: [u8; 4],
}
impl IcmpPacketTooBig {
#[inline]
pub fn mtu(&self) -> u32 {
unsafe { getter_be!(self, mtu, u32) }
}
#[inline]
pub fn set_mtu(&mut self, mtu: u32) {
unsafe { setter_be!(self, mtu, mtu) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv6ParamProblem {
pub pointer: [u8; 4],
}
impl Icmpv6ParamProblem {
#[inline]
pub fn pointer(&self) -> u32 {
unsafe { getter_be!(self, pointer, u32) }
}
#[inline]
pub fn set_pointer(&mut self, pointer: u32) {
unsafe { setter_be!(self, pointer, pointer) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
pub struct Icmpv6Redirect {
pub reserved: [u8; 4],
}
#[cfg(test)]
mod tests {
use super::*;
use core::net::Ipv4Addr;
macro_rules! expect_data {
($hdr:expr, $enum:ident, $variant:ident) => {{
match $hdr.data().expect("invalid ICMP message") {
$enum::$variant(value) => value,
_ => panic!("expected {} message", stringify!($variant)),
}
}};
}
macro_rules! expect_data_mut {
($hdr:expr, $enum:ident, $variant:ident) => {{
match $hdr.data_mut().expect("invalid ICMP message") {
$enum::$variant(value) => value,
_ => panic!("expected {} message", stringify!($variant)),
}
}};
}
#[test]
fn test_icmp_hdr_size() {
assert_eq!(Icmpv4Hdr::LEN, 8);
assert_eq!(Icmpv4Hdr::LEN, mem::size_of::<Icmpv4Hdr>());
}
fn create_test_icmp_hdr() -> Icmpv4Hdr {
Icmpv4Hdr {
type_: 0,
code: 0,
check: [0, 0],
data: [0, 0, 0, 0],
}
}
#[test]
fn test_checksum() {
let mut hdr = create_test_icmp_hdr();
let test_checksum: u16 = 0x1234;
let bytes = test_checksum.to_be_bytes();
hdr.check = bytes;
assert_eq!(hdr.checksum(), test_checksum);
hdr.set_checksum(0xABCD);
assert_eq!(hdr.check, [0xAB, 0xCD]);
assert_eq!(hdr.checksum(), 0xABCD);
}
#[test]
fn test_echo_fields() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 0;
let test_id: u16 = 0x4321;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, EchoReply).set_id(test_id);
assert_eq!(expect_data!(&hdr, Icmpv4HdrData, EchoReply).id(), test_id);
assert_eq!(hdr.data[..2], test_id.to_be_bytes());
let test_seq: u16 = 0x8765;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, EchoReply).set_sequence(test_seq);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, EchoReply).sequence(),
test_seq
);
assert_eq!(hdr.data[2..], test_seq.to_be_bytes());
}
#[test]
fn test_gateway_address() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 5;
let test_addr = Ipv4Addr::new(192, 168, 1, 1);
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Redirect).set_gateway_address(test_addr);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, Redirect).gateway_address(),
test_addr
);
assert_eq!(hdr.data, [192, 168, 1, 1]);
}
#[test]
fn test_message_enum_echo() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 8;
match hdr.data_mut().expect("echo view") {
Icmpv4HdrDataMut::Echo(echo) => {
echo.set_id(0xABCD);
echo.set_sequence(0x1234);
}
_ => panic!("unexpected variant"),
}
match hdr.data().expect("echo view") {
Icmpv4HdrData::Echo(echo) => {
assert_eq!(echo.id(), 0xABCD);
assert_eq!(echo.sequence(), 0x1234);
}
_ => panic!("unexpected variant"),
}
}
#[test]
fn test_message_enum_invalid_type() {
let hdr = Icmpv4Hdr {
type_: 9,
code: 0,
check: [0, 0],
data: [0, 0, 0, 0],
};
assert!(hdr.data().is_err());
}
#[test]
fn test_next_hop_mtu() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 3;
let test_mtu: u16 = 1500;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, DestinationUnreachable).set_mtu(test_mtu);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, DestinationUnreachable).mtu(),
test_mtu
);
assert_eq!(hdr.data[2..], test_mtu.to_be_bytes());
}
#[test]
fn test_parameter_pointer() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 12;
let test_pointer: u8 = 42;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, ParameterProblem).set_pointer(test_pointer);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, ParameterProblem).pointer(),
test_pointer
);
assert_eq!(hdr.data[0], test_pointer);
}
#[test]
fn test_traceroute_id() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 30;
let test_id: u16 = 0x9876;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Traceroute).set_id(test_id);
assert_eq!(expect_data!(&hdr, Icmpv4HdrData, Traceroute).id(), test_id);
assert_eq!(hdr.data[..2], test_id.to_be_bytes());
}
#[test]
fn test_photuris_spi() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 40;
let test_spi: u16 = 0xFEDC;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Photuris).set_reserved_spi(test_spi);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, Photuris).reserved_spi(),
test_spi
);
assert_eq!(hdr.data[..2], test_spi.to_be_bytes());
}
#[test]
fn test_photuris_pointer() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 40;
let test_pointer: u16 = 0x1A2B;
expect_data_mut!(&mut hdr, Icmpv4HdrDataMut, Photuris).set_pointer(test_pointer);
assert_eq!(
expect_data!(&hdr, Icmpv4HdrData, Photuris).pointer(),
test_pointer
);
assert_eq!(hdr.data[2..], test_pointer.to_be_bytes());
}
#[test]
fn test_type_and_code_fields() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 8;
hdr.code = 0;
assert_eq!(hdr.type_, 8);
assert_eq!(hdr.code, 0);
hdr.type_ = 3;
hdr.code = 1;
assert_eq!(hdr.type_, 3);
assert_eq!(hdr.code, 1);
}
#[test]
fn test_icmp_hdr_bitwise_operations() {
let mut hdr = create_test_icmp_hdr();
hdr.set_checksum(0x1234);
let modified_checksum = hdr.checksum() ^ 0xFFFF; hdr.set_checksum(modified_checksum);
assert_eq!(hdr.checksum(), 0xEDCB); }
#[test]
fn test_icmp_common_type_constants() {
let mut hdr = create_test_icmp_hdr();
hdr.type_ = 8;
assert_eq!(hdr.type_, 8);
hdr.type_ = 0;
assert_eq!(hdr.type_, 0);
hdr.type_ = 3;
assert_eq!(hdr.type_, 3);
hdr.type_ = 5;
assert_eq!(hdr.type_, 5);
}
#[test]
fn test_icmp_timestamp_msg_part_size() {
assert_eq!(IcmpTimestampMsgPart::LEN, 12);
assert_eq!(
IcmpTimestampMsgPart::LEN,
mem::size_of::<IcmpTimestampMsgPart>()
);
}
#[test]
fn test_timestamp_originate() {
let mut timestamp_part = IcmpTimestampMsgPart {
originate_timestamp: [0, 0, 0, 0],
receive_timestamp: [0, 0, 0, 0],
transmit_timestamp: [0, 0, 0, 0],
};
let test_timestamp: u32 = 0x12345678;
timestamp_part.set_originate_timestamp(test_timestamp);
assert_eq!(timestamp_part.originate_timestamp(), test_timestamp);
assert_eq!(timestamp_part.originate_timestamp, [0x12, 0x34, 0x56, 0x78]);
timestamp_part.set_originate_timestamp(0);
assert_eq!(timestamp_part.originate_timestamp(), 0);
assert_eq!(timestamp_part.originate_timestamp, [0, 0, 0, 0]);
timestamp_part.set_originate_timestamp(u32::MAX);
assert_eq!(timestamp_part.originate_timestamp(), u32::MAX);
assert_eq!(timestamp_part.originate_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_timestamp_receive() {
let mut timestamp_part = IcmpTimestampMsgPart {
originate_timestamp: [0, 0, 0, 0],
receive_timestamp: [0, 0, 0, 0],
transmit_timestamp: [0, 0, 0, 0],
};
let test_timestamp: u32 = 0x87654321;
timestamp_part.set_receive_timestamp(test_timestamp);
assert_eq!(timestamp_part.receive_timestamp(), test_timestamp);
assert_eq!(timestamp_part.receive_timestamp, [0x87, 0x65, 0x43, 0x21]);
timestamp_part.set_receive_timestamp(0);
assert_eq!(timestamp_part.receive_timestamp(), 0);
assert_eq!(timestamp_part.receive_timestamp, [0, 0, 0, 0]);
timestamp_part.set_receive_timestamp(u32::MAX);
assert_eq!(timestamp_part.receive_timestamp(), u32::MAX);
assert_eq!(timestamp_part.receive_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_timestamp_transmit() {
let mut timestamp_part = IcmpTimestampMsgPart {
originate_timestamp: [0, 0, 0, 0],
receive_timestamp: [0, 0, 0, 0],
transmit_timestamp: [0, 0, 0, 0],
};
let test_timestamp: u32 = 0xABCDEF01;
timestamp_part.set_transmit_timestamp(test_timestamp);
assert_eq!(timestamp_part.transmit_timestamp(), test_timestamp);
assert_eq!(timestamp_part.transmit_timestamp, [0xAB, 0xCD, 0xEF, 0x01]);
timestamp_part.set_transmit_timestamp(0);
assert_eq!(timestamp_part.transmit_timestamp(), 0);
assert_eq!(timestamp_part.transmit_timestamp, [0, 0, 0, 0]);
timestamp_part.set_transmit_timestamp(u32::MAX);
assert_eq!(timestamp_part.transmit_timestamp(), u32::MAX);
assert_eq!(timestamp_part.transmit_timestamp, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_timestamp_msg_part_construction() {
let mut timestamp_part = IcmpTimestampMsgPart {
originate_timestamp: [0, 0, 0, 0],
receive_timestamp: [0, 0, 0, 0],
transmit_timestamp: [0, 0, 0, 0],
};
timestamp_part.set_originate_timestamp(0x11223344);
timestamp_part.set_receive_timestamp(0x55667788);
timestamp_part.set_transmit_timestamp(0x99AABBCC);
assert_eq!(timestamp_part.originate_timestamp(), 0x11223344);
assert_eq!(timestamp_part.receive_timestamp(), 0x55667788);
assert_eq!(timestamp_part.transmit_timestamp(), 0x99AABBCC);
assert_eq!(timestamp_part.originate_timestamp, [0x11, 0x22, 0x33, 0x44]);
assert_eq!(timestamp_part.receive_timestamp, [0x55, 0x66, 0x77, 0x88]);
assert_eq!(timestamp_part.transmit_timestamp, [0x99, 0xAA, 0xBB, 0xCC]);
}
#[test]
fn test_icmp_traceroute_msg_part_size() {
assert_eq!(IcmpTracerouteMsgPart::LEN, 12);
assert_eq!(
IcmpTracerouteMsgPart::LEN,
mem::size_of::<IcmpTracerouteMsgPart>()
);
}
#[test]
fn test_traceroute_hops_out() {
let mut traceroute_part = IcmpTracerouteMsgPart {
hops_out: [0, 0],
hops_in: [0, 0],
bandwidth_out: [0, 0, 0, 0],
mtu_out: [0, 0, 0, 0],
};
let test_hops: u16 = 0x1234;
traceroute_part.set_hops_out(test_hops);
assert_eq!(traceroute_part.hops_out(), test_hops);
assert_eq!(traceroute_part.hops_out, [0x12, 0x34]);
traceroute_part.set_hops_out(0);
assert_eq!(traceroute_part.hops_out(), 0);
assert_eq!(traceroute_part.hops_out, [0, 0]);
traceroute_part.set_hops_out(u16::MAX);
assert_eq!(traceroute_part.hops_out(), u16::MAX);
assert_eq!(traceroute_part.hops_out, [0xFF, 0xFF]);
}
#[test]
fn test_traceroute_hops_in() {
let mut traceroute_part = IcmpTracerouteMsgPart {
hops_out: [0, 0],
hops_in: [0, 0],
bandwidth_out: [0, 0, 0, 0],
mtu_out: [0, 0, 0, 0],
};
let test_hops: u16 = 0x5678;
traceroute_part.set_hops_in(test_hops);
assert_eq!(traceroute_part.hops_in(), test_hops);
assert_eq!(traceroute_part.hops_in, [0x56, 0x78]);
traceroute_part.set_hops_in(0);
assert_eq!(traceroute_part.hops_in(), 0);
assert_eq!(traceroute_part.hops_in, [0, 0]);
traceroute_part.set_hops_in(u16::MAX);
assert_eq!(traceroute_part.hops_in(), u16::MAX);
assert_eq!(traceroute_part.hops_in, [0xFF, 0xFF]);
}
#[test]
fn test_traceroute_bandwidth_out() {
let mut traceroute_part = IcmpTracerouteMsgPart {
hops_out: [0, 0],
hops_in: [0, 0],
bandwidth_out: [0, 0, 0, 0],
mtu_out: [0, 0, 0, 0],
};
let test_bandwidth: u32 = 0x12345678;
traceroute_part.set_bandwidth_out(test_bandwidth);
assert_eq!(traceroute_part.bandwidth_out(), test_bandwidth);
assert_eq!(traceroute_part.bandwidth_out, [0x12, 0x34, 0x56, 0x78]);
traceroute_part.set_bandwidth_out(0);
assert_eq!(traceroute_part.bandwidth_out(), 0);
assert_eq!(traceroute_part.bandwidth_out, [0, 0, 0, 0]);
traceroute_part.set_bandwidth_out(u32::MAX);
assert_eq!(traceroute_part.bandwidth_out(), u32::MAX);
assert_eq!(traceroute_part.bandwidth_out, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_traceroute_mtu_out() {
let mut traceroute_part = IcmpTracerouteMsgPart {
hops_out: [0, 0],
hops_in: [0, 0],
bandwidth_out: [0, 0, 0, 0],
mtu_out: [0, 0, 0, 0],
};
let test_mtu: u32 = 0x87654321;
traceroute_part.set_mtu_out(test_mtu);
assert_eq!(traceroute_part.mtu_out(), test_mtu);
assert_eq!(traceroute_part.mtu_out, [0x87, 0x65, 0x43, 0x21]);
traceroute_part.set_mtu_out(0);
assert_eq!(traceroute_part.mtu_out(), 0);
assert_eq!(traceroute_part.mtu_out, [0, 0, 0, 0]);
traceroute_part.set_mtu_out(u32::MAX);
assert_eq!(traceroute_part.mtu_out(), u32::MAX);
assert_eq!(traceroute_part.mtu_out, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_traceroute_msg_part_construction() {
let mut traceroute_part = IcmpTracerouteMsgPart {
hops_out: [0, 0],
hops_in: [0, 0],
bandwidth_out: [0, 0, 0, 0],
mtu_out: [0, 0, 0, 0],
};
traceroute_part.set_hops_out(30);
traceroute_part.set_hops_in(25);
traceroute_part.set_bandwidth_out(100000000); traceroute_part.set_mtu_out(1500);
assert_eq!(traceroute_part.hops_out(), 30);
assert_eq!(traceroute_part.hops_in(), 25);
assert_eq!(traceroute_part.bandwidth_out(), 100000000);
assert_eq!(traceroute_part.mtu_out(), 1500);
assert_eq!(traceroute_part.hops_out, [0, 30]);
assert_eq!(traceroute_part.hops_in, [0, 25]);
assert_eq!(traceroute_part.bandwidth_out, [0x05, 0xF5, 0xE1, 0x00]); assert_eq!(traceroute_part.mtu_out, [0, 0, 0x05, 0xDC]); }
#[test]
fn test_icmpv6_hdr_size() {
assert_eq!(Icmpv6Hdr::LEN, 8);
assert_eq!(Icmpv6Hdr::LEN, mem::size_of::<Icmpv6Hdr>());
}
fn create_test_icmpv6_hdr() -> Icmpv6Hdr {
Icmpv6Hdr {
type_: 0,
code: 0,
check: [0, 0],
data: [0, 0, 0, 0],
}
}
#[test]
fn test_icmpv6_checksum() {
let mut hdr = create_test_icmpv6_hdr();
let test_checksum: u16 = 0x1234;
let bytes = test_checksum.to_be_bytes();
hdr.check = bytes;
assert_eq!(hdr.checksum(), test_checksum);
hdr.set_checksum(0xABCD);
assert_eq!(hdr.check, [0xAB, 0xCD]);
assert_eq!(hdr.checksum(), 0xABCD);
}
#[test]
fn test_icmpv6_echo_fields() {
let mut hdr = create_test_icmpv6_hdr();
hdr.type_ = 128;
let test_id: u16 = 0x4321;
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, EchoRequest).set_id(test_id);
assert_eq!(expect_data!(&hdr, Icmpv6HdrData, EchoRequest).id(), test_id);
let test_id_bytes = test_id.to_be_bytes();
assert_eq!(&hdr.data[..2], &test_id_bytes);
let test_seq: u16 = 0x8765;
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, EchoRequest).set_sequence(test_seq);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, EchoRequest).sequence(),
test_seq
);
let test_seq_bytes = test_seq.to_be_bytes();
assert_eq!(&hdr.data[2..], &test_seq_bytes);
}
#[test]
fn test_icmpv6_mtu() {
let mut hdr = create_test_icmpv6_hdr();
hdr.type_ = 2;
let test_mtu: u32 = 0x12345678;
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(test_mtu);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(),
test_mtu
);
assert_eq!(hdr.data, test_mtu.to_be_bytes());
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(0);
assert_eq!(expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(), 0);
assert_eq!(hdr.data, [0, 0, 0, 0]);
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, PacketTooBig).set_mtu(u32::MAX);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, PacketTooBig).mtu(),
u32::MAX
);
assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_icmpv6_pointer() {
let mut hdr = create_test_icmpv6_hdr();
hdr.type_ = 4;
let test_pointer: u32 = 0x87654321;
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(test_pointer);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
test_pointer
);
assert_eq!(hdr.data, test_pointer.to_be_bytes());
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(0);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
0
);
assert_eq!(hdr.data, [0, 0, 0, 0]);
expect_data_mut!(&mut hdr, Icmpv6HdrDataMut, ParameterProblem).set_pointer(u32::MAX);
assert_eq!(
expect_data!(&hdr, Icmpv6HdrData, ParameterProblem).pointer(),
u32::MAX
);
assert_eq!(hdr.data, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn test_icmpv6_type_and_code_fields() {
let mut hdr = create_test_icmpv6_hdr();
hdr.type_ = 128;
hdr.code = 0;
assert_eq!(hdr.type_, 128);
assert_eq!(hdr.code, 0);
hdr.type_ = 129;
hdr.code = 0;
assert_eq!(hdr.type_, 129);
assert_eq!(hdr.code, 0);
hdr.type_ = 1;
hdr.code = 0;
assert_eq!(hdr.type_, 1);
assert_eq!(hdr.code, 0);
hdr.type_ = 2;
hdr.code = 0;
assert_eq!(hdr.type_, 2);
assert_eq!(hdr.code, 0);
hdr.type_ = 3;
hdr.code = 0;
assert_eq!(hdr.type_, 3);
assert_eq!(hdr.code, 0);
hdr.type_ = 4;
hdr.code = 0;
assert_eq!(hdr.type_, 4);
assert_eq!(hdr.code, 0);
}
}
#[cfg(all(test, feature = "wincode"))]
mod wincode_prop_tests {
use super::*;
use proptest::array::{uniform2, uniform4};
use proptest::prelude::*;
use proptest::test_runner::Config as ProptestConfig;
use wincode::{SchemaRead, SchemaWrite, config::DefaultConfig};
const MAX_PACKET_SIZE: usize = Icmpv6Hdr::LEN;
trait FixedPacket {
const SERIALIZED_LEN: usize;
}
impl FixedPacket for Icmpv4Hdr {
const SERIALIZED_LEN: usize = Icmpv4Hdr::LEN;
}
impl FixedPacket for Icmpv6Hdr {
const SERIALIZED_LEN: usize = Icmpv6Hdr::LEN;
}
fn round_trip<T>(value: &T) -> T
where
T: SchemaWrite<DefaultConfig, Src = T>,
for<'de> T: SchemaRead<'de, DefaultConfig, Dst = T>,
T: FixedPacket,
{
let mut bytes = [0u8; MAX_PACKET_SIZE];
let len = T::SERIALIZED_LEN;
assert!(len <= bytes.len());
wincode::serialize_into(bytes.as_mut_slice(), value).unwrap();
wincode::deserialize(&bytes).unwrap()
}
fn icmp_hdr_strategy() -> impl Strategy<Value = Icmpv4Hdr> {
(
any::<u8>(),
any::<u8>(),
uniform2(any::<u8>()),
uniform4(any::<u8>()),
)
.prop_map(|(type_, code, check, data)| Icmpv4Hdr {
type_,
code,
check,
data,
})
}
fn echo_bytes(id: [u8; 2], seq: [u8; 2]) -> [u8; 4] {
let mut bytes = [0u8; 4];
bytes[..2].copy_from_slice(&id);
bytes[2..].copy_from_slice(&seq);
bytes
}
fn icmpv6_hdr_strategy() -> impl Strategy<Value = Icmpv6Hdr> {
let echo = (
Just(128u8),
any::<u8>(),
uniform2(any::<u8>()),
uniform2(any::<u8>()),
uniform2(any::<u8>()),
)
.prop_map(|(type_, code, check, id, seq)| Icmpv6Hdr {
type_,
code,
check,
data: echo_bytes(id, seq),
});
let echo_reply = (
Just(129u8),
any::<u8>(),
uniform2(any::<u8>()),
uniform2(any::<u8>()),
uniform2(any::<u8>()),
)
.prop_map(|(type_, code, check, id, seq)| Icmpv6Hdr {
type_,
code,
check,
data: echo_bytes(id, seq),
});
let packet_too_big = (
Just(2u8),
any::<u8>(),
uniform2(any::<u8>()),
uniform4(any::<u8>()),
)
.prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
type_,
code,
check,
data: bytes,
});
let param_problem = (
Just(4u8),
any::<u8>(),
uniform2(any::<u8>()),
uniform4(any::<u8>()),
)
.prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
type_,
code,
check,
data: bytes,
});
let redirect = (
Just(137u8),
any::<u8>(),
uniform2(any::<u8>()),
uniform4(any::<u8>()),
)
.prop_map(|(type_, code, check, reserved)| Icmpv6Hdr {
type_,
code,
check,
data: reserved,
});
let fallback = (
any::<u8>().prop_filter("use reserved field", |ty| {
!matches!(ty, 128 | 129 | 2 | 4 | 137)
}),
any::<u8>(),
uniform2(any::<u8>()),
uniform4(any::<u8>()),
)
.prop_map(|(type_, code, check, bytes)| Icmpv6Hdr {
type_,
code,
check,
data: bytes,
});
prop_oneof![
echo,
echo_reply,
packet_too_big,
param_problem,
redirect,
fallback
]
}
proptest! {
#![proptest_config(ProptestConfig {
failure_persistence: None,
..ProptestConfig::default()
})]
#[test]
fn icmp_hdr_round_trips(hdr in icmp_hdr_strategy()) {
let decoded = round_trip(&hdr);
prop_assert_eq!(decoded.type_, hdr.type_);
prop_assert_eq!(decoded.code, hdr.code);
prop_assert_eq!(decoded.check, hdr.check);
prop_assert_eq!(decoded.data, hdr.data);
}
#[test]
fn icmpv6_hdr_round_trips(hdr in icmpv6_hdr_strategy()) {
let decoded = round_trip(&hdr);
prop_assert_eq!(decoded.type_, hdr.type_);
prop_assert_eq!(decoded.code, hdr.code);
prop_assert_eq!(decoded.check, hdr.check);
prop_assert_eq!(decoded.data, hdr.data);
}
}
}