diff options
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/packet/error.rs | 4 | ||||
| -rw-r--r-- | src/packet/packet.rs | 46 | ||||
| -rw-r--r-- | src/protocol/icmp.rs | 147 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
5 files changed, 197 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs index c1286bd..d4fc3ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,9 @@ fn trigger_packet_event( PacketEvent::L2_EVENT => { BuiltInEvent::trigger_l2_event(event_mgr.clone(), session.clone()); } + PacketEvent::L2_VLAN => { + // TODO + } PacketEvent::L3_EVENT => { BuiltInEvent::trigger_l3_event(event_mgr.clone(), session.clone()); } @@ -40,6 +43,9 @@ fn trigger_packet_event( PacketEvent::UDP_EVENT => { BuiltInEvent::trigger_udp_event(event_mgr.clone(), session.clone()); } + PacketEvent::ICMP_EVENT => { + // TODO + } } } } diff --git a/src/packet/error.rs b/src/packet/error.rs index 174930c..55523be 100644 --- a/src/packet/error.rs +++ b/src/packet/error.rs @@ -4,6 +4,7 @@ pub enum PacketError { // L2 IncompleteEthernetFrame, + IncompleteVlanHeader, UnsupportEthernetType, // L3 @@ -18,6 +19,7 @@ pub enum PacketError { // L4 IncompleteUdpHeader, IncompleteTcpHeader, + IncompleteIcmpHeader, } impl core::fmt::Display for PacketError { @@ -26,6 +28,7 @@ impl core::fmt::Display for PacketError { PacketError::InvalidPacketLength => write!(f, "Invalid Packet Length"), // L2 PacketError::IncompleteEthernetFrame => write!(f, "Incomplete Ethernet Frame"), + PacketError::IncompleteVlanHeader => write!(f, "Incomplete VLAN Header"), PacketError::UnsupportEthernetType => write!(f, "Unsupport Ethernet Type"), // L3 PacketError::IncompleteIpv4Header => write!(f, "Incomplete IPv4 Header"), @@ -36,6 +39,7 @@ impl core::fmt::Display for PacketError { // L4 PacketError::IncompleteUdpHeader => write!(f, "Incomplete UDP Header"), PacketError::IncompleteTcpHeader => write!(f, "Incomplete TCP Header"), + PacketError::IncompleteIcmpHeader => write!(f, "Incomplete ICMP Header"), } } } diff --git a/src/packet/packet.rs b/src/packet/packet.rs index 23bbc6f..7e1c8ef 100644 --- a/src/packet/packet.rs +++ b/src/packet/packet.rs @@ -2,6 +2,7 @@ use crate::packet::error::PacketError; use crate::protocol::codec::Decode; use crate::protocol::ethernet::EtherType; use crate::protocol::ethernet::EthernetFrame; +use crate::protocol::icmp::IcmpHeader; use crate::protocol::ip::IPProtocol; use crate::protocol::ipv4::IPv4Header; use crate::protocol::ipv6::IPv6Header; @@ -13,13 +14,14 @@ use crate::protocol::vlan::VlanHeader; #[derive(Clone, Debug, PartialEq)] pub enum Encapsulation<'a> { L2_ETH(EthernetFrame, &'a [u8]), + L2_VLAN(VlanHeader, &'a [u8]), - L3_VLAN(VlanHeader, &'a [u8]), L3_IPV4(IPv4Header, &'a [u8]), L3_IPV6(IPv6Header, &'a [u8]), L4_TCP(TcpHeader, &'a [u8]), L4_UDP(UdpHeader, &'a [u8]), + L4_ICMP(IcmpHeader, &'a [u8]), UNSUPPORTED(&'a [u8]), } @@ -27,13 +29,20 @@ pub enum Encapsulation<'a> { #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq)] pub enum PacketEvent { + // L2 Layer Event L2_EVENT, + L2_VLAN, + + // L3 Layer Event L3_EVENT, IPV4_EVENT, IPV6_EVENT, + + // L4 Layer Event L4_EVENT, TCP_EVENT, UDP_EVENT, + ICMP_EVENT, } #[derive(Debug)] @@ -356,9 +365,11 @@ fn handle_l2<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketE let result = EthernetFrame::decode(input); if let Ok((payload, header)) = result { dbg!(&header); + packet .encapsulation .push(Encapsulation::L2_ETH(header, payload)); + packet.event.push(PacketEvent::L2_EVENT); return handle_l3(packet, payload, header.ether_type); } else { @@ -379,11 +390,13 @@ fn handle_l3<'a>( packet .encapsulation - .push(Encapsulation::L3_VLAN(header, payload)); - packet.event.push(PacketEvent::L3_EVENT); + .push(Encapsulation::L2_VLAN(header, payload)); + + packet.event.push(PacketEvent::L2_EVENT); + packet.event.push(PacketEvent::L2_VLAN); return handle_l3(packet, payload, header.ether_type); } else { - return Err(PacketError::IncompleteEthernetFrame); + return Err(PacketError::IncompleteVlanHeader); } } EtherType::IPv4 => { @@ -442,10 +455,27 @@ fn handle_l4<'a>( next_proto: IPProtocol, ) -> Result<(), PacketError> { match next_proto { + IPProtocol::ICMP => { + let result = IcmpHeader::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + + packet + .encapsulation + .push(Encapsulation::L4_ICMP(header, payload)); + + packet.event.push(PacketEvent::L4_EVENT); + packet.event.push(PacketEvent::ICMP_EVENT); + return Ok(()); + } else { + return Err(PacketError::IncompleteIcmpHeader); + } + } IPProtocol::UDP => { let result = UdpHeader::decode(input); if let Ok((payload, header)) = result { dbg!(&header); + packet .encapsulation .push(Encapsulation::L4_UDP(header, payload)); @@ -757,7 +787,7 @@ mod tests { ); assert_eq!( packet.encapsulation[1], - Encapsulation::L3_VLAN( + Encapsulation::L2_VLAN( VlanHeader { priority_code_point: 0, drop_eligible_indicator: false, @@ -769,7 +799,7 @@ mod tests { ); assert_eq!( packet.encapsulation[2], - Encapsulation::L3_VLAN( + Encapsulation::L2_VLAN( VlanHeader { priority_code_point: 0, drop_eligible_indicator: false, @@ -956,7 +986,7 @@ mod tests { ); assert_eq!( packet.encapsulation[1], - Encapsulation::L3_VLAN( + Encapsulation::L2_VLAN( VlanHeader { priority_code_point: 0, drop_eligible_indicator: false, @@ -968,7 +998,7 @@ mod tests { ); assert_eq!( packet.encapsulation[2], - Encapsulation::L3_VLAN( + Encapsulation::L2_VLAN( VlanHeader { priority_code_point: 0, drop_eligible_indicator: false, diff --git a/src/protocol/icmp.rs b/src/protocol/icmp.rs new file mode 100644 index 0000000..c1352d5 --- /dev/null +++ b/src/protocol/icmp.rs @@ -0,0 +1,147 @@ +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_payload: Vec<u8>, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +impl From<u8> 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_payload) = nom::bytes::streaming::take(4u8)(input)?; + + Ok(( + input, + IcmpHeader { + icmp_type: icmp_type.into(), + icmp_code, + icmp_checksum, + icmp_payload: icmp_payload.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_payload: vec![0x5f, 0x2b, 0x00, 0x01], + }; + + assert_eq!(IcmpHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let vlan = IcmpHeader::decode(&bytes); + if let Ok((payload, header)) = vlan { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + + // assert_eq!(1, 0); + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 044481a..ba06b66 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -7,4 +7,5 @@ pub mod udp; pub mod tcp; pub mod dns; pub mod http; -pub mod vlan;
\ No newline at end of file +pub mod vlan; +pub mod icmp;
\ No newline at end of file |
