use std::fmt; use byteorder::{BigEndian, ReadBytesExt}; use crate::cal_checksum; use crate::error::*; /// icmp 协议 /* https://www.rfc-editor.org/rfc/rfc792 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Code | Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 不同Type和Code有不同含义 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 数据体 不同Type和Code有不同含义 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ use crate::icmp::{Code, Kind}; use crate::ip::ipv4::packet::IpV4Packet; pub struct IcmpPacket { buffer: B, } impl> IcmpPacket { pub fn unchecked(buffer: B) -> Self { Self { buffer } } pub fn new(buffer: B) -> Result { if buffer.as_ref().len() < 8 { Err(Error::SmallBuffer)? } let packet = Self::unchecked(buffer); Ok(packet) } } impl + AsMut<[u8]>> IcmpPacket { pub fn set_kind(&mut self, kind: Kind) { self.buffer.as_mut()[0] = kind.into(); } pub fn update_checksum(&mut self) { self.buffer.as_mut()[2..4].copy_from_slice(&[0, 0]); let checksum = cal_checksum(self.buffer.as_ref()); self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); } } impl> IcmpPacket { pub fn kind(&self) -> Kind { Kind::from(self.buffer.as_ref()[0]) } pub fn code(&self) -> Code { Code::from(self.kind(), self.buffer.as_ref()[1]) } pub fn checksum(&self) -> u16 { (&self.buffer.as_ref()[2..]) .read_u16::() .unwrap() } pub fn is_valid(&self) -> bool { self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 } pub fn header_other(&self) -> HeaderOther { match self.kind() { Kind::EchoReply | Kind::EchoRequest | Kind::TimestampRequest | Kind::TimestampReply | Kind::InformationRequest | Kind::InformationReply => { let ide = (&self.buffer.as_ref()[4..]) .read_u16::() .unwrap(); let seq = (&self.buffer.as_ref()[6..]) .read_u16::() .unwrap(); HeaderOther::Identifier(ide, seq) } Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => { let bytes = self.buffer.as_ref(); HeaderOther::Unused(bytes[4], bytes[5], bytes[6], bytes[7]) } Kind::Redirect => { let bytes = self.buffer.as_ref(); HeaderOther::Address(bytes[4], bytes[5], bytes[6], bytes[7]) } Kind::ParameterProblem => HeaderOther::Pointer(self.buffer.as_ref()[4]), _ => { let bytes = self.buffer.as_ref(); HeaderOther::UnKnown(bytes[4], bytes[5], bytes[6], bytes[7]) } } } pub fn payload(&self) -> &[u8] { &self.buffer.as_ref()[8..] } pub fn description(&self) -> Description<&[u8]> { use std::io::Cursor; match self.kind() { Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::ParameterProblem | Kind::SourceQuench | Kind::Redirect => match IpV4Packet::new(self.payload()) { Ok(d) => Description::Ip(d), Err(_) => Description::Other(self.payload()), }, Kind::TimestampRequest | Kind::TimestampReply => { let mut buffer = Cursor::new(self.payload()); Description::Timestamp( buffer.read_u32::().unwrap(), buffer.read_u32::().unwrap(), buffer.read_u32::().unwrap(), ) } _ => Description::Other(self.payload()), } } } impl> fmt::Debug for IcmpPacket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct(if self.is_valid() { "icmp::Packet" } else { "icmp::Packet!" }) .field("kind", &self.kind()) .field("code", &self.code()) .field("checksum", &self.checksum()) .field("payload", &self.payload()) .finish() } } #[derive(Debug)] pub enum HeaderOther { /// 全零 Unused(u8, u8, u8, u8), /// If code = 0, identifies the octet where an error was detected. Pointer(u8), /// Address of the gateway to which traffic for the network specified /// in the internet destination network field of the original /// datagram's data should be sent. Address(u8, u8, u8, u8), /// Identifier | Sequence Number Identifier(u16, u16), UnKnown(u8, u8, u8, u8), } pub enum Description { Ip(IpV4Packet), ///时间戳 Originate Timestamp,Receive Timestamp,Transmit Timestamp Timestamp(u32, u32, u32), Other(B), } impl + std::fmt::Debug> fmt::Debug for Description { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Description::Ip(packet) => f.debug_struct(&format!("{:?}", packet)).finish(), Description::Timestamp(originate, receive, transmit) => f .debug_struct("") .field("originate", originate) .field("receive", receive) .field("transmit", transmit) .finish(), Description::Other(bytes) => f.debug_struct(&format!("{:?}", bytes)).finish(), } } }