diff options
| author | luwenpeng <[email protected]> | 2023-09-22 14:10:21 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-09-22 14:10:21 +0800 |
| commit | afd40bfc655fa660513c5464e43655255655c53d (patch) | |
| tree | 5f64000842a8035f9dbc25315155b16a13429fd9 | |
| parent | 1582aaa3a8cf6b70329ae6d8a248120c34d52669 (diff) | |
[feature] Support PPP Decode
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/packet/error.rs | 2 | ||||
| -rw-r--r-- | src/packet/packet.rs | 253 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 | ||||
| -rw-r--r-- | src/protocol/ppp.rs | 115 |
5 files changed, 375 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs index 2efb827..5c02490 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,6 +66,9 @@ fn trigger_packet_event( Encapsulation::Pptp(_, _) => { // TODO } + Encapsulation::Ppp(_, _) => { + // TODO + } } } } diff --git a/src/packet/error.rs b/src/packet/error.rs index b9ca7cf..9566ce9 100644 --- a/src/packet/error.rs +++ b/src/packet/error.rs @@ -38,6 +38,7 @@ pub enum PacketError { UnsupportL2tpVersion, IncompletePptpHeader, + IncompletePppHeader, } impl core::fmt::Display for PacketError { @@ -72,6 +73,7 @@ impl core::fmt::Display for PacketError { PacketError::IncompleteL2tpHeader => write!(f, "Incomplete L2TP Header"), PacketError::UnsupportL2tpVersion => write!(f, "Unsupport L2TP Version"), PacketError::IncompletePptpHeader => write!(f, "Incomplete PPTP Header"), + PacketError::IncompletePppHeader => write!(f, "Incomplete PPP Header"), } } } diff --git a/src/packet/packet.rs b/src/packet/packet.rs index 13140be..e42dd2f 100644 --- a/src/packet/packet.rs +++ b/src/packet/packet.rs @@ -14,6 +14,8 @@ use crate::protocol::l2tp::L2tpHeader; use crate::protocol::l2tp::L2tpType; use crate::protocol::mpls::MplsHeader; use crate::protocol::mpls::PwEthHeader; +use crate::protocol::ppp::PppHeader; +use crate::protocol::ppp::PppProtocol; use crate::protocol::pptp::PptpHeader; use crate::protocol::tcp::TcpHeader; use crate::protocol::udp::UdpHeader; @@ -41,6 +43,7 @@ pub enum Encapsulation<'a> { Gtpv1(Gtpv1Header, &'a [u8]), L2tp(L2tpHeader, &'a [u8]), Pptp(PptpHeader, &'a [u8]), + Ppp(PppHeader, &'a [u8]), } #[derive(Debug)] @@ -673,12 +676,39 @@ fn handle_pptp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), Packe } } +fn handle_ppp<'a>(packet: &mut Packet<'a>, input: &'a [u8]) -> Result<(), PacketError> { + let result = PppHeader::decode(input); + if let Ok((payload, header)) = result { + dbg!(&header); + + let next_proto = header.protocol; + packet + .encapsulation + .push(Encapsulation::Ppp(header, payload)); + + match next_proto { + // PppProtocol::PAD => handle_pad(packet, payload), + PppProtocol::IPv4 => handle_ipv4(packet, payload), + PppProtocol::IPv6 => handle_ipv6(packet, payload), + // PppProtocol::IPCP => handle_ipcp(packet, payload), + // PppProtocol::CCP => handle_ccp(packet, payload), + // PppProtocol::LCP => handle_lcp(packet, payload), + // PppProtocol::PAP => handle_pap(packet, payload), + // PppProtocol::CHAP => handle_chap(packet, payload), + _ => Ok(()), + } + } else { + return Err(PacketError::IncompletePppHeader); + } +} + fn handle_l3<'a>( packet: &mut Packet<'a>, input: &'a [u8], next_proto: EtherType, ) -> Result<(), PacketError> { match next_proto { + EtherType::PPP => handle_ppp(packet, input), EtherType::MPLSuni => handle_mpls(packet, input), EtherType::QinQ => handle_vlan(packet, input), EtherType::VLAN => handle_vlan(packet, input), @@ -732,6 +762,8 @@ mod tests { use crate::protocol::ipv6::Ipv6Header; use crate::protocol::mpls::MplsHeader; use crate::protocol::mpls::PwEthHeader; + use crate::protocol::ppp::PppHeader; + use crate::protocol::ppp::PppProtocol; use crate::protocol::pptp::PptpControlMessageType; use crate::protocol::pptp::PptpHeader; use crate::protocol::pptp::PptpMessageType; @@ -3228,4 +3260,225 @@ mod tests { // assert_eq!(1, 0); } + + #[test] + fn test_packet_handle_eth_ipv4_gre_ppp_ipv4_icmp() { + /* + * Frame 26: 134 bytes on wire (1072 bits), 134 bytes captured (1072 bits) + * Encapsulation type: Ethernet (1) + * Arrival Time: Jul 16, 2014 21:52:00.197893000 CST + * [Time shift for this packet: 0.000000000 seconds] + * Epoch Time: 1405518720.197893000 seconds + * [Time delta from previous captured frame: 59.476334000 seconds] + * [Time delta from previous displayed frame: 59.975412000 seconds] + * [Time since reference or first frame: 70.385093000 seconds] + * Frame Number: 26 + * Frame Length: 134 bytes (1072 bits) + * Capture Length: 134 bytes (1072 bits) + * [Frame is marked: False] + * [Frame is ignored: False] + * [Protocols in frame: eth:ethertype:ip:gre:ppp:ip:icmp:data] + * [Coloring Rule Name: ICMP] + * [Coloring Rule String: icmp || icmpv6] + * Ethernet II, Src: MinervaK_00:02:00 (00:14:00:00:02:00), Dst: Cisco_55:c0:1c (00:09:e9:55:c0:1c) + * Destination: Cisco_55:c0:1c (00:09:e9:55:c0:1c) + * Address: Cisco_55:c0:1c (00:09:e9:55:c0:1c) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Source: MinervaK_00:02:00 (00:14:00:00:02:00) + * Address: MinervaK_00:02:00 (00:14:00:00:02:00) + * .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) + * .... ...0 .... .... .... .... = IG bit: Individual address (unicast) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 20.0.0.2, Dst: 20.0.0.1 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * 0000 00.. = Differentiated Services Codepoint: Default (0) + * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * Total Length: 120 + * Identification: 0x18d8 (6360) + * 000. .... = Flags: 0x0 + * 0... .... = Reserved bit: Not set + * .0.. .... = Don't fragment: Not set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: Generic Routing Encapsulation (47) + * Header Checksum: 0x397d [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0x397d] + * Source Address: 20.0.0.2 + * Destination Address: 20.0.0.1 + * Generic Routing Encapsulation (PPP) + * Flags and Version: 0x3001 + * 0... .... .... .... = Checksum Bit: No + * .0.. .... .... .... = Routing Bit: No + * ..1. .... .... .... = Key Bit: Yes + * ...1 .... .... .... = Sequence Number Bit: Yes + * .... 0... .... .... = Strict Source Route Bit: No + * .... .000 .... .... = Recursion control: 0 + * .... .... 0... .... = Acknowledgment: No + * .... .... .000 0... = Flags (Reserved): 0 + * .... .... .... .001 = Version: Enhanced GRE (1) + * Protocol Type: PPP (0x880b) + * Payload Length: 88 + * Call ID: 24 + * Sequence Number: 7 + * Point-to-Point Protocol + * Address: 0xff + * Control: 0x03 + * Protocol: Internet Protocol version 4 (0x0021) + * Internet Protocol Version 4, Src: 17.1.1.122, Dst: 40.0.0.2 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * 0000 00.. = Differentiated Services Codepoint: Default (0) + * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * Total Length: 84 + * Identification: 0x1101 (4353) + * 000. .... = Flags: 0x0 + * 0... .... = Reserved bit: Not set + * .0.. .... = Don't fragment: Not set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: ICMP (1) + * Header Checksum: 0x2f2c [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0x2f2c] + * Source Address: 17.1.1.122 + * Destination Address: 40.0.0.2 + * Internet Control Message Protocol + * Type: 8 (Echo (ping) request) + * Code: 0 + * Checksum: 0x4500 [correct] + * [Checksum Status: Good] + * Identifier (BE): 6187 (0x182b) + * Identifier (LE): 11032 (0x2b18) + * Sequence Number (BE): 1 (0x0001) + * Sequence Number (LE): 256 (0x0100) + * [Response frame: 27] + * Data (56 bytes) + * Data: 0000000053c6838000000000000304b7101112131415161718191a1b1c1d1e1f20212223… + * [Length: 56] + */ + + let bytes = [ + 0x00, 0x09, 0xe9, 0x55, 0xc0, 0x1c, 0x00, 0x14, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x78, 0x18, 0xd8, 0x00, 0x00, 0x40, 0x2f, 0x39, 0x7d, 0x14, 0x00, + 0x00, 0x02, 0x14, 0x00, 0x00, 0x01, 0x30, 0x01, 0x88, 0x0b, 0x00, 0x58, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x07, 0xff, 0x03, 0x00, 0x21, 0x45, 0x00, 0x00, 0x54, 0x11, 0x01, + 0x00, 0x00, 0x40, 0x01, 0x2f, 0x2c, 0x11, 0x01, 0x01, 0x7a, 0x28, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x45, 0x00, 0x18, 0x2b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x53, 0xc6, + 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0xb7, 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 mut packet = Packet::new(&bytes, bytes.len() as u32); + let result = packet.handle(); + assert_eq!(result.is_ok(), true); + + assert_eq!(packet.encapsulation.len(), 6); + assert_eq!( + packet.encapsulation[0], + Encapsulation::Eth( + EthernetFrame { + source_mac: MacAddress([0x00, 0x14, 0x00, 0x00, 0x02, 0x00]), + dest_mac: MacAddress([0x00, 0x09, 0xe9, 0x55, 0xc0, 0x1c]), + ether_type: EtherType::IPv4, + }, + &bytes[14..] + ) + ); + assert_eq!( + packet.encapsulation[1], + Encapsulation::Ipv4( + Ipv4Header { + version: 4, + ihl: 20, + tos: 0x00, + length: 120, + id: 0x18d8, + flags: 0x0, + frag_offset: 0, + ttl: 64, + protocol: IPProtocol::GRE, + checksum: 0x397d, + source_address: Ipv4Addr::new(20, 0, 0, 2), + dest_address: Ipv4Addr::new(20, 0, 0, 1), + }, + &bytes[34..] + ) + ); + assert_eq!( + packet.encapsulation[2], + Encapsulation::Grev1( + Grev1Header { + flag_checksum: false, + flag_routing: false, + flag_key: true, + flag_sequence: true, + flag_strictroute: false, + recursion_control: 0, + flag_acknowledgment: false, + flags: 0, + version: 1, + protocol_type: EtherType::PPP, + key_payload_length: 88, + key_call_id: 24, + sequence_number: Some(7), + acknowledgment_number: None, + }, + &bytes[46..] + ) + ); + assert_eq!( + packet.encapsulation[3], + Encapsulation::Ppp( + PppHeader { + address: 0xff, + control: 0x03, + protocol: PppProtocol::IPv4, + }, + &bytes[50..] + ) + ); + assert_eq!( + packet.encapsulation[4], + Encapsulation::Ipv4( + Ipv4Header { + version: 4, + ihl: 20, + tos: 0x00, + length: 84, + id: 0x1101, + flags: 0x0, + frag_offset: 0, + ttl: 64, + protocol: IPProtocol::ICMP, + checksum: 0x2f2c, + source_address: Ipv4Addr::new(17, 1, 1, 122), + dest_address: Ipv4Addr::new(40, 0, 0, 2), + }, + &bytes[70..] + ) + ); + assert_eq!( + packet.encapsulation[5], + Encapsulation::Icmp( + IcmpHeader { + icmp_type: IcmpType::EchoRequest, + icmp_code: 0, + icmp_checksum: 0x4500, + icmp_extended: vec![0x18, 0x2b, 0x00, 0x01,], + }, + &bytes[78..] + ) + ); + + // assert_eq!(1, 0); + } } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 1f02684..16b9963 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -15,4 +15,5 @@ pub mod gtpv1; pub mod l2tp; pub mod grev0; pub mod grev1; -pub mod pptp;
\ No newline at end of file +pub mod pptp; +pub mod ppp;
\ No newline at end of file diff --git a/src/protocol/ppp.rs b/src/protocol/ppp.rs new file mode 100644 index 0000000..e625cb6 --- /dev/null +++ b/src/protocol/ppp.rs @@ -0,0 +1,115 @@ +use crate::protocol::codec::Decode; +use nom::number; +use nom::IResult; + +/****************************************************************************** + * Struct + ******************************************************************************/ + +// https://www.iana.org/assignments/ppp-numbers/ppp-numbers.xhtml +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PppProtocol { + PAD, // Padding Protocol + IPv4, // Internet Protocol version 4 (IPv4) + IPv6, // Internet Protocol version 6 (IPv6) + IPCP, // Internet Protocol Control Protocol (IPCP) + CCP, // Compression Control Protocol (CCP) + LCP, // Link Control Protocol (LCP) + PAP, // Password Authentication Protocol (PAP) + CHAP, // Challenge Handshake Authentication Protocol (CHAP) + Other(u16), +} + +// https://www.rfc-editor.org/rfc/rfc1661.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PppHeader { + pub address: u8, + pub control: u8, + pub protocol: PppProtocol, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +impl From<u16> for PppProtocol { + fn from(raw: u16) -> Self { + match raw { + 0x0001 => PppProtocol::PAD, + 0x0021 => PppProtocol::IPv4, + 0x0057 => PppProtocol::IPv6, + 0x8021 => PppProtocol::IPCP, + 0x80FD => PppProtocol::CCP, + 0xC021 => PppProtocol::LCP, + 0xC023 => PppProtocol::PAP, + 0xC223 => PppProtocol::CHAP, + other => PppProtocol::Other(other), + } + } +} + +impl Decode for PppProtocol { + type Iterm = PppProtocol; + fn decode(input: &[u8]) -> IResult<&[u8], PppProtocol> { + let (input, protocol) = number::streaming::be_u16(input)?; + + Ok((input, protocol.into())) + } +} + +impl Decode for PppHeader { + type Iterm = PppHeader; + fn decode(input: &[u8]) -> IResult<&[u8], PppHeader> { + let (input, address) = number::streaming::be_u8(input)?; + let (input, control) = number::streaming::be_u8(input)?; + let (input, protocol) = PppProtocol::decode(input)?; + Ok(( + input, + PppHeader { + address, + control, + protocol, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::PppHeader; + use super::PppProtocol; + use crate::protocol::codec::Decode; + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn ppp_header_decode() { + /* + * Point-to-Point Protocol + * Address: 0xff + * Control: 0x03 + * Protocol: Link Control Protocol (0xc021) + */ + + let bytes = [0xff, 0x03, 0xc0, 0x21, 0xff /* Payload */]; + + let expectation = PppHeader { + address: 0xff, + control: 0x03, + protocol: PppProtocol::LCP, + }; + + assert_eq!(PppHeader::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = PppHeader::decode(&bytes); + if let Ok((payload, header)) = result { + println!("return: {:?}, payload: {}", header, payload.len()); + } else { + println!("return: Incomplete data"); + } + } +} |
