use crate::protocol::codec::Decode; use nom::number; use nom::IResult; /****************************************************************************** * Struct ******************************************************************************/ #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum IcmpType { EchoReply, DestinationUnreachable, SourceQuench, Redirect, EchoRequest, RouterAdvertisement, RouterSolicitation, TimeExceeded, ParameterProblem, Timestamp, TimestampReply, InformationRequest, InformationReply, AddressMaskRequest, AddressMaskReply, Traceroute, Other(u8), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct IcmpHeader { pub icmp_type: IcmpType, pub icmp_code: u8, pub icmp_checksum: u16, pub icmp_extended: Vec, } /****************************************************************************** * API ******************************************************************************/ impl From for IcmpType { fn from(raw: u8) -> Self { match raw { 0 => IcmpType::EchoReply, 3 => IcmpType::DestinationUnreachable, 4 => IcmpType::SourceQuench, 5 => IcmpType::Redirect, 8 => IcmpType::EchoRequest, 9 => IcmpType::RouterAdvertisement, 10 => IcmpType::RouterSolicitation, 11 => IcmpType::TimeExceeded, 12 => IcmpType::ParameterProblem, 13 => IcmpType::Timestamp, 14 => IcmpType::TimestampReply, 15 => IcmpType::InformationRequest, 16 => IcmpType::InformationReply, 17 => IcmpType::AddressMaskRequest, 18 => IcmpType::AddressMaskReply, 30 => IcmpType::Traceroute, other => IcmpType::Other(other), } } } impl Decode for IcmpHeader { type Iterm = IcmpHeader; fn decode(input: &[u8]) -> IResult<&[u8], IcmpHeader> { let (input, icmp_type) = number::streaming::be_u8(input)?; let (input, icmp_code) = number::streaming::be_u8(input)?; let (input, icmp_checksum) = number::streaming::be_u16(input)?; let (input, icmp_extended) = nom::bytes::streaming::take(4u8)(input)?; Ok(( input, IcmpHeader { icmp_type: icmp_type.into(), icmp_code, icmp_checksum, icmp_extended: icmp_extended.to_vec(), }, )) } } /****************************************************************************** * TEST ******************************************************************************/ #[cfg(test)] mod tests { use super::IcmpHeader; use crate::protocol::{codec::Decode, icmp::IcmpType}; const LAST_SLICE: &'static [u8] = &[ 0x96, 0xb5, 0xe9, 0x5e, 0x00, 0x00, 0x00, 0x00, 0xac, 0xe6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; #[test] fn icmp_header_decode() { /* * Internet Control Message Protocol * Type: 8 (Echo (ping) request) * Code: 0 * Checksum: 0xab05 [correct] * [Checksum Status: Good] * Identifier (BE): 24363 (0x5f2b) * Identifier (LE): 11103 (0x2b5f) * Sequence Number (BE): 1 (0x0001) * Sequence Number (LE): 256 (0x0100) * [Response frame: 2] * Timestamp from icmp data: Jun 17, 2020 14:17:58.000000000 CST * [Timestamp from icmp data (relative): 0.055548000 seconds] * Data (48 bytes) * Data: ace6020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b… * [Length: 48] */ let bytes = [ 0x08, 0x00, 0xab, 0x05, 0x5f, 0x2b, 0x00, 0x01, /* Payload */ 0x96, 0xb5, 0xe9, 0x5e, 0x00, 0x00, 0x00, 0x00, 0xac, 0xe6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; let expectation = IcmpHeader { icmp_type: IcmpType::EchoRequest, icmp_code: 0, icmp_checksum: 0xab05, icmp_extended: vec![0x5f, 0x2b, 0x00, 0x01], }; assert_eq!(IcmpHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); // example let result = IcmpHeader::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { println!("return: Incomplete data"); } // assert_eq!(1, 0); } }