summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-09-22 14:10:21 +0800
committerluwenpeng <[email protected]>2023-09-22 14:10:21 +0800
commitafd40bfc655fa660513c5464e43655255655c53d (patch)
tree5f64000842a8035f9dbc25315155b16a13429fd9
parent1582aaa3a8cf6b70329ae6d8a248120c34d52669 (diff)
[feature] Support PPP Decode
-rw-r--r--src/main.rs3
-rw-r--r--src/packet/error.rs2
-rw-r--r--src/packet/packet.rs253
-rw-r--r--src/protocol/mod.rs3
-rw-r--r--src/protocol/ppp.rs115
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");
+ }
+ }
+}