| use crate::*; |
| use arrayvec::ArrayVec; |
| |
| /// A header of an ICMPv4 packet. |
| /// |
| /// What is part of the header depends on the ICMPv4 type |
| /// and code. But usually the static sized elements are part |
| /// of the header. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct Icmpv4Header { |
| /// Type & type specific values & code. |
| pub icmp_type: Icmpv4Type, |
| /// Checksum in the ICMP header. |
| pub checksum: u16, |
| } |
| |
| impl Icmpv4Header { |
| /// Minimum number of bytes/octets an Icmpv4Header takes up |
| /// in serialized form. |
| pub const MIN_LEN: usize = 8; |
| |
| /// Deprecated, use [`Icmpv4Header::MIN_LEN`] instead. |
| #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MIN_LEN instead")] |
| pub const MIN_SERIALIZED_SIZE: usize = 8; |
| |
| /// Maximum number of bytes/octets an Icmpv4Header takes up |
| /// in serialized form. |
| /// |
| /// Currently this number is determined by the biggest |
| /// supported ICMPv4 header type, which is currently the |
| /// "Timestamp" and "Timestamp Reply Message". |
| pub const MAX_LEN: usize = 20; |
| |
| /// Deprecated, use [`Icmpv4Header::MAX_LEN`] instead. |
| #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MAX_LEN instead")] |
| pub const MAX_SERIALIZED_SIZE: usize = 20; |
| |
| /// Constructs an [`Icmpv4Header`] using the given type |
| /// and the checksum set to 0. |
| pub fn new(icmp_type: Icmpv4Type) -> Icmpv4Header { |
| // Note: will calculate checksum on send |
| Icmpv4Header { |
| icmp_type, |
| checksum: 0, |
| } |
| } |
| |
| /// Creates a [`Icmpv4Header`] with a checksum calculated based on the given payload. |
| pub fn with_checksum(icmp_type: Icmpv4Type, payload: &[u8]) -> Icmpv4Header { |
| let checksum = icmp_type.calc_checksum(payload); |
| Icmpv4Header { |
| icmp_type, |
| checksum, |
| } |
| } |
| |
| /// Reads an icmp4 header from a slice directly and returns a tuple containing the resulting header & unused part of the slice. |
| #[inline] |
| pub fn from_slice(slice: &[u8]) -> Result<(Icmpv4Header, &[u8]), err::LenError> { |
| let header = Icmpv4Slice::from_slice(slice)?.header(); |
| let rest = &slice[header.header_len()..]; |
| Ok((header, rest)) |
| } |
| |
| /// Reads an ICMPv4 header from the given reader. |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub fn read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv4Header, std::io::Error> { |
| let mut bytes = [0u8; Icmpv4Header::MAX_LEN]; |
| |
| // try reading the initial 8 bytes |
| reader.read_exact(&mut bytes[..8])?; |
| |
| match bytes[0] { |
| icmpv4::TYPE_TIMESTAMP_REPLY | icmpv4::TYPE_TIMESTAMP => { |
| if 0 == bytes[1] { |
| // Timetamp messages need additional data read & it and |
| // then set the slice correspondently |
| reader.read_exact(&mut bytes[8..icmpv4::TimestampMessage::LEN])?; |
| Ok(Icmpv4Slice { |
| slice: &bytes[..icmpv4::TimestampMessage::LEN], |
| } |
| .header()) |
| } else { |
| // fallback to unknown |
| Ok(Icmpv4Slice { slice: &bytes[..8] }.header()) |
| } |
| } |
| _ => Ok(Icmpv4Slice { slice: &bytes[..8] }.header()), |
| } |
| } |
| |
| /// Write the ICMPv4 header to the given writer. |
| #[cfg(feature = "std")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> { |
| writer.write_all(&self.to_bytes()) |
| } |
| |
| /// Length in bytes/octets of this header type. |
| #[inline] |
| pub fn header_len(&self) -> usize { |
| self.icmp_type.header_len() |
| } |
| |
| /// If the ICMP type has a fixed size returns the number of |
| /// bytes that should be present after the header of this type. |
| #[inline] |
| pub fn fixed_payload_size(&self) -> Option<usize> { |
| self.icmp_type.fixed_payload_size() |
| } |
| |
| /// Calculates & updates the checksum in the header. |
| /// |
| /// Note this method assumes that all unused bytes/octets |
| /// are filled with zeroes. |
| pub fn update_checksum(&mut self, payload: &[u8]) { |
| self.checksum = self.icmp_type.calc_checksum(payload); |
| } |
| |
| /// Converts the header to the on the wire bytes. |
| #[rustfmt::skip] |
| pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { |
| let checksum_be = self.checksum.to_be_bytes(); |
| let re_zero = |
| |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { |
| |
| #[rustfmt::skip] |
| let mut re = ArrayVec::from([ |
| type_u8, code_u8, checksum_be[0], checksum_be[1], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ]); |
| // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. |
| unsafe { |
| re.set_len(8); |
| } |
| re |
| }; |
| |
| let re_2u16 = |type_u8: u8, |
| code_u8: u8, |
| a_u16: u16, |
| b_u16: u16| |
| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { |
| let a = a_u16.to_be_bytes(); |
| let b = b_u16.to_be_bytes(); |
| |
| #[rustfmt::skip] |
| let mut re = ArrayVec::from([ |
| type_u8, code_u8, checksum_be[0], checksum_be[1], |
| a[0], a[1], b[0], b[1], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ]); |
| // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. |
| unsafe { |
| re.set_len(8); |
| } |
| re |
| }; |
| |
| let re_4u8 = |type_u8: u8, |
| code_u8: u8, |
| bytes5to8: [u8; 4]| |
| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { |
| |
| #[rustfmt::skip] |
| let mut re = ArrayVec::from([ |
| type_u8, code_u8, checksum_be[0], checksum_be[1], |
| bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ]); |
| // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. |
| unsafe { |
| re.set_len(8); |
| } |
| re |
| }; |
| |
| let re_timestamp_msg = |type_u8: u8, |
| msg: &icmpv4::TimestampMessage| |
| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> { |
| let id = msg.id.to_be_bytes(); |
| let seq = msg.seq.to_be_bytes(); |
| let o = msg.originate_timestamp.to_be_bytes(); |
| let r = msg.receive_timestamp.to_be_bytes(); |
| let t = msg.transmit_timestamp.to_be_bytes(); |
| |
| ArrayVec::from([ |
| type_u8, 0, checksum_be[0], checksum_be[1], |
| id[0], id[1], seq[0], seq[1], |
| o[0], o[1], o[2], o[3], |
| r[0], r[1], r[2], r[3], |
| t[0], t[1], t[2], t[3], |
| ]) |
| }; |
| |
| use Icmpv4Type::*; |
| use icmpv4::*; |
| match self.icmp_type { |
| Unknown { |
| type_u8, |
| code_u8, |
| bytes5to8, |
| } => re_4u8(type_u8, code_u8, bytes5to8), |
| EchoReply(echo) => re_2u16(TYPE_ECHO_REPLY, 0, echo.id, echo.seq), |
| DestinationUnreachable(ref dest) => { |
| use DestUnreachableHeader::*; |
| match dest { |
| Network => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET), |
| Host => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST), |
| Protocol => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL), |
| Port => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT), |
| FragmentationNeeded { next_hop_mtu } => { |
| let m_be = next_hop_mtu.to_be_bytes(); |
| re_4u8( |
| TYPE_DEST_UNREACH, |
| CODE_DST_UNREACH_NEED_FRAG, |
| [0, 0, m_be[0], m_be[1]], |
| ) |
| } |
| SourceRouteFailed => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED), |
| NetworkUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN), |
| HostUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN), |
| Isolated => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED), |
| NetworkProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB), |
| HostProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB), |
| TosNetwork => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET), |
| TosHost => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST), |
| FilterProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB), |
| HostPrecedenceViolation => re_zero( |
| TYPE_DEST_UNREACH, |
| CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, |
| ), |
| PrecedenceCutoff => { |
| re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF) |
| } |
| } |
| } |
| Redirect(ref msg) => { |
| re_4u8(TYPE_REDIRECT, msg.code as u8, msg.gateway_internet_address) |
| } |
| EchoRequest(echo) => re_2u16(TYPE_ECHO_REQUEST, 0, echo.id, echo.seq), |
| TimeExceeded(code) => re_zero(TYPE_TIME_EXCEEDED, code as u8), |
| ParameterProblem(ref header) => { |
| use ParameterProblemHeader::*; |
| match header { |
| PointerIndicatesError(pointer) => re_4u8( |
| TYPE_PARAMETER_PROBLEM, |
| CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, |
| [*pointer, 0, 0, 0], |
| ), |
| MissingRequiredOption => re_zero( |
| TYPE_PARAMETER_PROBLEM, |
| CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, |
| ), |
| BadLength => re_zero(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH), |
| } |
| } |
| TimestampRequest(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP, msg), |
| TimestampReply(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP_REPLY, msg), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use crate::{ |
| err::{Layer, LenError}, |
| icmpv4::*, |
| test_gens::*, |
| *, |
| }; |
| use alloc::{format, vec::Vec}; |
| use proptest::prelude::*; |
| |
| #[test] |
| #[allow(deprecated)] |
| fn constants() { |
| assert_eq!(8, Icmpv4Header::MIN_LEN); |
| assert_eq!(20, Icmpv4Header::MAX_LEN); |
| assert_eq!(8, Icmpv4Header::MIN_SERIALIZED_SIZE); |
| assert_eq!(20, Icmpv4Header::MAX_SERIALIZED_SIZE); |
| } |
| |
| proptest! { |
| #[test] |
| fn new(icmpv4_type in icmpv4_type_any()) { |
| assert_eq!( |
| Icmpv4Header { |
| icmp_type: icmpv4_type.clone(), |
| checksum: 0, |
| }, |
| Icmpv4Header::new(icmpv4_type) |
| ); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn with_checksum( |
| icmpv4_type in icmpv4_type_any(), |
| payload in proptest::collection::vec(any::<u8>(), 0..1024), |
| ) { |
| assert_eq!( |
| Icmpv4Header { |
| icmp_type: icmpv4_type.clone(), |
| checksum: icmpv4_type.calc_checksum(&payload), |
| }, |
| Icmpv4Header::with_checksum(icmpv4_type, &payload) |
| ); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn from_slice( |
| icmpv4_type in icmpv4_type_any(), |
| checksum in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..1024), |
| ) { |
| use Icmpv4Type::*; |
| |
| // ok case |
| let header = Icmpv4Header { |
| icmp_type: icmpv4_type.clone(), |
| checksum: checksum, |
| }; |
| let buffer = { |
| let mut buffer = Vec::with_capacity(header.header_len() + payload.len()); |
| buffer.extend_from_slice(&header.to_bytes()); |
| |
| match icmpv4_type { |
| // skip the payoad for the timestamp request (those don't have a payload) |
| TimestampRequest(_) | TimestampReply(_) => {}, |
| _ => { |
| buffer.extend_from_slice(&[0u8;36]); |
| } |
| } |
| buffer |
| }; |
| { |
| let (actual, rest) = Icmpv4Header::from_slice(&buffer).unwrap(); |
| assert_eq!(actual, header); |
| assert_eq!(rest, &buffer[header.header_len()..]); |
| } |
| |
| // error case |
| for bad_len in 0..header.header_len() { |
| assert_eq!( |
| Icmpv4Header::from_slice(&buffer[..bad_len]), |
| Err(LenError{ |
| required_len: if bad_len < Icmpv4Header::MIN_LEN { |
| Icmpv4Header::MIN_LEN |
| } else { |
| header.header_len() |
| }, |
| len: bad_len, |
| len_source: LenSource::Slice, |
| layer: if bad_len < Icmpv4Header::MIN_LEN { |
| Layer::Icmpv4 |
| } else { |
| use crate::Icmpv4Type::*; |
| match icmpv4_type { |
| TimestampRequest(_) => Layer::Icmpv4Timestamp, |
| TimestampReply(_) => Layer::Icmpv4TimestampReply, |
| _ => Layer::Icmpv4, |
| } |
| }, |
| layer_start_offset: 0, |
| }) |
| ); |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn read( |
| non_timestamp_type in any::<u8>().prop_filter( |
| "type must be a non timestamp type", |
| |v| (*v != icmpv4::TYPE_TIMESTAMP_REPLY && *v != icmpv4::TYPE_TIMESTAMP) |
| ), |
| non_zero_code in 1u8..=u8::MAX, |
| bytes in any::<[u8;icmpv4::TimestampMessage::LEN]>() |
| ) { |
| for (type_u8, code_u8) in [ |
| // non timestamp |
| (non_timestamp_type, bytes[1]), |
| // timestamp with zero code |
| (TYPE_TIMESTAMP_REPLY, 0u8), |
| (TYPE_TIMESTAMP, 0u8), |
| // timestamp with non-zero code |
| (TYPE_TIMESTAMP_REPLY, non_zero_code), |
| (TYPE_TIMESTAMP, non_zero_code), |
| ] { |
| let b = { |
| let mut b = bytes.clone(); |
| b[0] = type_u8; |
| b[1] = code_u8; |
| b |
| }; |
| let expected = Icmpv4Header::from_slice(&b).unwrap().0; |
| |
| // ok case |
| { |
| let mut cursor = std::io::Cursor::new(&b); |
| let actual = Icmpv4Header::read(&mut cursor).unwrap(); |
| assert_eq!(expected, actual); |
| assert_eq!(expected.header_len() as u64, cursor.position()); |
| } |
| |
| // size error case |
| for bad_len in 0..expected.header_len() { |
| let mut cursor = std::io::Cursor::new(&(b.as_ref()[..bad_len])); |
| assert!(Icmpv4Header::read(&mut cursor).is_err()); |
| } |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn write( |
| icmpv4_type in icmpv4_type_any(), |
| checksum in any::<u16>(), |
| ) { |
| let header = Icmpv4Header { |
| icmp_type: icmpv4_type.clone(), |
| checksum, |
| }; |
| |
| // normal write |
| { |
| let bytes = header.to_bytes(); |
| let mut buffer = Vec::with_capacity(header.header_len()); |
| header.write(&mut buffer).unwrap(); |
| assert_eq!(&bytes[..], &buffer[..]); |
| } |
| |
| // error case |
| { |
| for bad_len in 0..icmpv4_type.header_len() { |
| let mut bytes = [0u8;Icmpv6Header::MAX_LEN]; |
| let mut writer = std::io::Cursor::new(&mut bytes[..bad_len]); |
| header.write(&mut writer).unwrap_err(); |
| } |
| } |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn header_len( |
| checksum in any::<u16>(), |
| icmpv4_type in icmpv4_type_any() |
| ) { |
| let header = Icmpv4Header{ |
| icmp_type: icmpv4_type.clone(), |
| checksum, |
| }; |
| assert_eq!(header.header_len(), icmpv4_type.header_len()); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn fixed_payload_size( |
| checksum in any::<u16>(), |
| icmpv4_type in icmpv4_type_any() |
| ) { |
| let header = Icmpv4Header{ |
| icmp_type: icmpv4_type.clone(), |
| checksum, |
| }; |
| assert_eq!(header.fixed_payload_size(), icmpv4_type.fixed_payload_size()); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn update_checksum( |
| icmpv4_type in icmpv4_type_any(), |
| checksum in any::<u16>(), |
| payload in proptest::collection::vec(any::<u8>(), 0..1024), |
| ) { |
| let mut header = Icmpv4Header { |
| icmp_type: icmpv4_type.clone(), |
| checksum, |
| }; |
| header.update_checksum(&payload); |
| assert_eq!(header.checksum, icmpv4_type.calc_checksum(&payload)); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| #[rustfmt::skip] |
| fn to_bytes( |
| checksum in any::<u16>(), |
| next_hop_mtu in any::<u16>(), |
| redirect_code_u8 in 0u8..=3, |
| gateway_internet_address in any::<[u8;4]>(), |
| time_exceeded_code_u8 in 0u8..=1, |
| id in any::<u16>(), |
| seq in any::<u16>(), |
| originate_timestamp in any::<u32>(), |
| receive_timestamp in any::<u32>(), |
| transmit_timestamp in any::<u32>(), |
| pointer in any::<u8>(), |
| unknown_type_u8 in any::<u8>(), |
| unknown_code_u8 in any::<u8>(), |
| bytes5to8 in any::<[u8;4]>(), |
| ) { |
| use Icmpv4Type::*; |
| use arrayvec::ArrayVec; |
| |
| let ts = TimestampMessage{ |
| id, |
| seq, |
| originate_timestamp, |
| receive_timestamp, |
| transmit_timestamp, |
| }; |
| let ts_bytes = { |
| let id_be = id.to_be_bytes(); |
| let seq_be = seq.to_be_bytes(); |
| let ot = originate_timestamp.to_be_bytes(); |
| let rt = receive_timestamp.to_be_bytes(); |
| let tt = transmit_timestamp.to_be_bytes(); |
| [ |
| 0, 0, 0, 0, |
| id_be[0], id_be[1], seq_be[0], seq_be[1], |
| ot[0], ot[1], ot[2], ot[3], |
| rt[0], rt[1], rt[2], rt[3], |
| tt[0], tt[1], tt[2], tt[3], |
| ] |
| }; |
| let echo = IcmpEchoHeader{ |
| id, |
| seq, |
| }; |
| let redirect = RedirectHeader{ |
| code: RedirectCode::from_u8(redirect_code_u8).unwrap(), |
| gateway_internet_address, |
| }; |
| |
| // test values with no need for subtests |
| let random_values = [ |
| ( |
| Unknown { |
| type_u8: unknown_type_u8, |
| code_u8: unknown_code_u8, |
| bytes5to8: bytes5to8, |
| }, |
| 8, |
| [ |
| unknown_type_u8, unknown_code_u8, 0, 0, |
| bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ], |
| ), |
| ( |
| EchoReply(echo.clone()), |
| 8, |
| { |
| let id_be = id.to_be_bytes(); |
| let seq_be = seq.to_be_bytes(); |
| [ |
| TYPE_ECHO_REPLY, 0, 0, 0, |
| id_be[0], id_be[1], seq_be[0], seq_be[1], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ] |
| } |
| ), |
| |
| ( |
| Redirect(redirect), |
| 8, |
| { |
| let gip = gateway_internet_address; |
| [ |
| TYPE_REDIRECT, redirect_code_u8, 0, 0, |
| gip[0], gip[1], gip[2], gip[3], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ] |
| }, |
| ), |
| ( |
| EchoRequest(echo.clone()), |
| 8, |
| { |
| let id_be = id.to_be_bytes(); |
| let seq_be = seq.to_be_bytes(); |
| [ |
| TYPE_ECHO_REQUEST, 0, 0, 0, |
| id_be[0], id_be[1], seq_be[0], seq_be[1], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ] |
| } |
| ), |
| ( |
| TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()), |
| 8, |
| [ |
| TYPE_TIME_EXCEEDED, time_exceeded_code_u8, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ], |
| ), |
| ( |
| TimestampRequest(ts.clone()), |
| 20, |
| { |
| let mut b = ts_bytes; |
| b[0] = TYPE_TIMESTAMP; |
| b |
| } |
| ), |
| ( |
| TimestampReply(ts), |
| 20, |
| { |
| let mut b = ts_bytes; |
| b[0] = TYPE_TIMESTAMP_REPLY; |
| b |
| } |
| ), |
| ]; |
| |
| for t in random_values { |
| let actual = Icmpv4Header{ |
| icmp_type: t.0.clone(), |
| checksum, |
| }.to_bytes(); |
| |
| let mut expected = ArrayVec::from(t.2); |
| unsafe { |
| expected.set_len(t.1) |
| } |
| let checksum_be = checksum.to_be_bytes(); |
| expected[2] = checksum_be[0]; |
| expected[3] = checksum_be[1]; |
| assert_eq!(expected, actual); |
| } |
| |
| // destination unreachable |
| { |
| use DestUnreachableHeader::*; |
| let tests = [ |
| (CODE_DST_UNREACH_NET, [0;2], Network), |
| (CODE_DST_UNREACH_HOST, [0;2], Host), |
| (CODE_DST_UNREACH_PROTOCOL, [0;2], Protocol), |
| (CODE_DST_UNREACH_PORT, [0;2], Port), |
| (CODE_DST_UNREACH_NEED_FRAG, next_hop_mtu.to_be_bytes(), FragmentationNeeded{ next_hop_mtu }), |
| (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, [0;2], SourceRouteFailed), |
| (CODE_DST_UNREACH_NET_UNKNOWN, [0;2], NetworkUnknown), |
| (CODE_DST_UNREACH_HOST_UNKNOWN, [0;2], HostUnknown), |
| (CODE_DST_UNREACH_ISOLATED, [0;2], Isolated), |
| (CODE_DST_UNREACH_NET_PROHIB, [0;2], NetworkProhibited), |
| (CODE_DST_UNREACH_HOST_PROHIB, [0;2], HostProhibited), |
| (CODE_DST_UNREACH_TOS_NET, [0;2], TosNetwork), |
| (CODE_DST_UNREACH_TOS_HOST, [0;2], TosHost), |
| (CODE_DST_UNREACH_FILTER_PROHIB, [0;2], FilterProhibited), |
| (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, [0;2], HostPrecedenceViolation), |
| (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, [0;2], PrecedenceCutoff), |
| ]; |
| for t in tests { |
| let checksum_be = checksum.to_be_bytes(); |
| let mut expected = ArrayVec::from([ |
| TYPE_DEST_UNREACH, t.0, checksum_be[0], checksum_be[1], |
| 0, 0, t.1[0], t.1[1], |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ]); |
| unsafe { |
| expected.set_len(8); |
| } |
| let actual = Icmpv4Header{ |
| icmp_type: DestinationUnreachable(t.2.clone()), |
| checksum, |
| }.to_bytes(); |
| assert_eq!(expected, actual); |
| } |
| } |
| |
| // parameter problem |
| { |
| use ParameterProblemHeader::*; |
| let tests = [ |
| (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, pointer, PointerIndicatesError(pointer)), |
| (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 0, MissingRequiredOption), |
| (CODE_PARAMETER_PROBLEM_BAD_LENGTH, 0, BadLength), |
| ]; |
| for t in tests { |
| let checksum_be = checksum.to_be_bytes(); |
| let mut expected = ArrayVec::from([ |
| TYPE_PARAMETER_PROBLEM, t.0, checksum_be[0], checksum_be[1], |
| t.1, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| ]); |
| unsafe { |
| expected.set_len(8); |
| } |
| let actual = Icmpv4Header{ |
| icmp_type: ParameterProblem(t.2.clone()), |
| checksum, |
| }.to_bytes(); |
| assert_eq!(expected, actual); |
| } |
| } |
| } |
| } |
| |
| #[test] |
| fn clone_eq() { |
| use Icmpv4Type::*; |
| let header = Icmpv4Header { |
| icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), |
| checksum: 0, |
| }; |
| assert_eq!(header.clone(), header); |
| } |
| |
| #[test] |
| fn debug() { |
| use Icmpv4Type::*; |
| let header = Icmpv4Header { |
| icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), |
| checksum: 0, |
| }; |
| assert_eq!( |
| format!("{:?}", header), |
| format!( |
| "Icmpv4Header {{ icmp_type: {:?}, checksum: {:?} }}", |
| header.icmp_type, header.checksum |
| ) |
| ); |
| } |
| } |