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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // assert_eq!(1, 0); } }