175 lines
5.9 KiB
Rust
175 lines
5.9 KiB
Rust
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<B> {
|
|
buffer: B,
|
|
}
|
|
|
|
impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
|
pub fn unchecked(buffer: B) -> Self {
|
|
Self { buffer }
|
|
}
|
|
pub fn new(buffer: B) -> Result<Self> {
|
|
if buffer.as_ref().len() < 8 {
|
|
Err(Error::SmallBuffer)?
|
|
}
|
|
let packet = Self::unchecked(buffer);
|
|
Ok(packet)
|
|
}
|
|
}
|
|
|
|
impl<B: AsRef<[u8]> + AsMut<[u8]>> IcmpPacket<B> {
|
|
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<B: AsRef<[u8]>> IcmpPacket<B> {
|
|
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::<BigEndian>()
|
|
.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::<BigEndian>()
|
|
.unwrap();
|
|
let seq = (&self.buffer.as_ref()[6..])
|
|
.read_u16::<BigEndian>()
|
|
.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::<BigEndian>().unwrap(),
|
|
buffer.read_u32::<BigEndian>().unwrap(),
|
|
buffer.read_u32::<BigEndian>().unwrap(),
|
|
)
|
|
}
|
|
_ => Description::Other(self.payload()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<B: AsRef<[u8]>> fmt::Debug for IcmpPacket<B> {
|
|
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<B> {
|
|
Ip(IpV4Packet<B>),
|
|
///时间戳 Originate Timestamp,Receive Timestamp,Transmit Timestamp
|
|
Timestamp(u32, u32, u32),
|
|
Other(B),
|
|
}
|
|
|
|
impl<B: AsRef<[u8]> + std::fmt::Debug> fmt::Debug for Description<B> {
|
|
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(),
|
|
}
|
|
}
|
|
}
|